summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2011-08-19 02:24:24 -0700
committerJeff Sharkey <jsharkey@android.com>2011-08-23 18:45:44 -0700
commit8e28b7d78232f6cf08739ca0d129cc7f9e650801 (patch)
treec72bf1bcc07e1dd02764a4f4738a56735e5f4252
parent7a12d6ba14a35276fd3afb314d6c95055da4c6f0 (diff)
downloadframeworks_base-8e28b7d78232f6cf08739ca0d129cc7f9e650801.zip
frameworks_base-8e28b7d78232f6cf08739ca0d129cc7f9e650801.tar.gz
frameworks_base-8e28b7d78232f6cf08739ca0d129cc7f9e650801.tar.bz2
Proactively disable data when over policy limit.
Add policy controls to NetworkStateTracker which are combined with other user preference and internal flags to decide if data connection should be established. Better locking around enabled flags. When data network would be over limit, proactively disable data on that network. Enable when policy is snoozed or when cycle resets. Track and dismiss notifications from now-stale policies. Bug: 4587023, 5178147 Change-Id: Ibfcc9f73cda7c369209af701b46eddd3d1943f2d
-rw-r--r--core/java/android/bluetooth/BluetoothTetheringDataTracker.java15
-rw-r--r--core/java/android/net/DummyDataStateTracker.java12
-rw-r--r--core/java/android/net/EthernetDataTracker.java22
-rw-r--r--core/java/android/net/IConnectivityManager.aidl4
-rw-r--r--core/java/android/net/MobileDataStateTracker.java49
-rw-r--r--core/java/android/net/NetworkStateTracker.java12
-rwxr-xr-xcore/res/res/values/strings.xml4
-rw-r--r--services/java/com/android/server/ConnectivityService.java33
-rw-r--r--services/java/com/android/server/net/NetworkPolicyManagerService.java297
-rw-r--r--services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java115
-rw-r--r--telephony/java/com/android/internal/telephony/DataConnectionTracker.java110
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java9
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java50
-rw-r--r--wifi/java/android/net/wifi/WifiStateTracker.java22
14 files changed, 482 insertions, 272 deletions
diff --git a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
index a7b0037..83d1bda 100644
--- a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
+++ b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
@@ -19,7 +19,6 @@ package android.bluetooth;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.DhcpInfoInternal;
-import android.net.LinkAddress;
import android.net.LinkCapabilities;
import android.net.LinkProperties;
import android.net.NetworkInfo;
@@ -30,7 +29,6 @@ import android.os.Handler;
import android.os.Message;
import android.util.Log;
-import java.net.InetAddress;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -184,11 +182,14 @@ public class BluetoothTetheringDataTracker implements NetworkStateTracker {
return -1;
}
- /**
- * @param enabled
- */
- public void setDataEnable(boolean enabled) {
- android.util.Log.d(TAG, "setDataEnabled: IGNORING enabled=" + enabled);
+ @Override
+ public void setUserDataEnable(boolean enabled) {
+ Log.w(TAG, "ignoring setUserDataEnable(" + enabled + ")");
+ }
+
+ @Override
+ public void setPolicyDataEnable(boolean enabled) {
+ Log.w(TAG, "ignoring setPolicyDataEnable(" + enabled + ")");
}
/**
diff --git a/core/java/android/net/DummyDataStateTracker.java b/core/java/android/net/DummyDataStateTracker.java
index e39725a..9f0f9cd 100644
--- a/core/java/android/net/DummyDataStateTracker.java
+++ b/core/java/android/net/DummyDataStateTracker.java
@@ -19,9 +19,6 @@ package android.net;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
-import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkInfo;
-import android.net.LinkProperties;
import android.util.Slog;
/**
@@ -168,7 +165,14 @@ public class DummyDataStateTracker implements NetworkStateTracker {
return true;
}
- public void setDataEnable(boolean enabled) {
+ @Override
+ public void setUserDataEnable(boolean enabled) {
+ // ignored
+ }
+
+ @Override
+ public void setPolicyDataEnable(boolean enabled) {
+ // ignored
}
@Override
diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java
index b035c51..21ecc22 100644
--- a/core/java/android/net/EthernetDataTracker.java
+++ b/core/java/android/net/EthernetDataTracker.java
@@ -17,15 +17,7 @@
package android.net;
import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.DhcpInfoInternal;
-import android.net.LinkAddress;
-import android.net.LinkCapabilities;
-import android.net.LinkProperties;
-import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkStateTracker;
-import android.net.NetworkUtils;
import android.os.Handler;
import android.os.IBinder;
import android.os.INetworkManagementService;
@@ -34,7 +26,6 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
-import java.net.InetAddress;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -302,11 +293,14 @@ public class EthernetDataTracker implements NetworkStateTracker {
return -1;
}
- /**
- * @param enabled
- */
- public void setDataEnable(boolean enabled) {
- Log.d(TAG, "setDataEnabled: IGNORING enabled=" + enabled);
+ @Override
+ public void setUserDataEnable(boolean enabled) {
+ Log.w(TAG, "ignoring setUserDataEnable(" + enabled + ")");
+ }
+
+ @Override
+ public void setPolicyDataEnable(boolean enabled) {
+ Log.w(TAG, "ignoring setPolicyDataEnable(" + enabled + ")");
}
/**
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 1b95b60..c9553c0 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -64,9 +64,11 @@ interface IConnectivityManager
boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress);
boolean getMobileDataEnabled();
-
void setMobileDataEnabled(boolean enabled);
+ /** Policy control over specific {@link NetworkStateTracker}. */
+ void setPolicyDataEnable(int networkType, boolean enabled);
+
int tether(String iface);
int untether(String iface);
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 5501f38..5929cfb 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -16,19 +16,26 @@
package android.net;
+import static com.android.internal.telephony.DataConnectionTracker.CMD_SET_POLICY_DATA_ENABLE;
+import static com.android.internal.telephony.DataConnectionTracker.CMD_SET_USER_DATA_ENABLE;
+import static com.android.internal.telephony.DataConnectionTracker.DISABLED;
+import static com.android.internal.telephony.DataConnectionTracker.ENABLED;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.net.NetworkInfo.DetailedState;
import android.os.Bundle;
-import android.os.HandlerThread;
+import android.os.Handler;
import android.os.Looper;
+import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
import android.os.ServiceManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Slog;
import com.android.internal.telephony.DataConnectionTracker;
import com.android.internal.telephony.ITelephony;
@@ -36,13 +43,6 @@ import com.android.internal.telephony.Phone;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.util.AsyncChannel;
-import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkInfo;
-import android.net.LinkProperties;
-import android.telephony.TelephonyManager;
-import android.util.Slog;
-import android.text.TextUtils;
-
/**
* Track the state of mobile data connectivity. This is done by
* receiving broadcast intents from the Phone process whenever
@@ -452,17 +452,22 @@ public class MobileDataStateTracker implements NetworkStateTracker {
return false;
}
- /**
- * @param enabled
- */
- public void setDataEnable(boolean enabled) {
- try {
- if (DBG) log("setDataEnable: E enabled=" + enabled);
- mDataConnectionTrackerAc.sendMessage(DataConnectionTracker.CMD_SET_DATA_ENABLE,
- enabled ? DataConnectionTracker.ENABLED : DataConnectionTracker.DISABLED);
- if (VDBG) log("setDataEnable: X enabled=" + enabled);
- } catch (Exception e) {
- loge("setDataEnable: X mAc was null" + e);
+ @Override
+ public void setUserDataEnable(boolean enabled) {
+ if (DBG) log("setUserDataEnable: E enabled=" + enabled);
+ final AsyncChannel channel = mDataConnectionTrackerAc;
+ if (channel != null) {
+ channel.sendMessage(CMD_SET_USER_DATA_ENABLE, enabled ? ENABLED : DISABLED);
+ }
+ if (VDBG) log("setUserDataEnable: X enabled=" + enabled);
+ }
+
+ @Override
+ public void setPolicyDataEnable(boolean enabled) {
+ if (DBG) log("setPolicyDataEnable(enabled=" + enabled + ")");
+ final AsyncChannel channel = mDataConnectionTrackerAc;
+ if (channel != null) {
+ channel.sendMessage(CMD_SET_POLICY_DATA_ENABLE, enabled ? ENABLED : DISABLED);
}
}
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index f53063d..1735592 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -136,9 +136,17 @@ public interface NetworkStateTracker {
public boolean isAvailable();
/**
- * @param enabled
+ * User control of data connection through this network, typically persisted
+ * internally.
*/
- public void setDataEnable(boolean enabled);
+ public void setUserDataEnable(boolean enabled);
+
+ /**
+ * Policy control of data connection through this network, typically not
+ * persisted internally. Usually used when {@link NetworkPolicy#limitBytes}
+ * is passed.
+ */
+ public void setPolicyDataEnable(boolean enabled);
/**
* -------------------------------------------------------------
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 6aff54e..ade2865 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3098,6 +3098,8 @@
<string name="data_usage_4g_limit_title">4G data disabled</string>
<!-- Notification title when mobile data usage has exceeded limit threshold, and has been disabled. [CHAR LIMIT=32] -->
<string name="data_usage_mobile_limit_title">Mobile data disabled</string>
+ <!-- Notification title when Wi-Fi data usage has exceeded limit threshold, and has been disabled. [CHAR LIMIT=32] -->
+ <string name="data_usage_wifi_limit_title">Wi-Fi data disabled</string>
<!-- Notification body when data usage has exceeded limit threshold, and has been disabled. [CHAR LIMIT=32] -->
<string name="data_usage_limit_body">Touch to enable</string>
@@ -3107,6 +3109,8 @@
<string name="data_usage_4g_limit_snoozed_title">4G data limit exceeded</string>
<!-- Notification title when mobile data usage has exceeded limit threshold. [CHAR LIMIT=32] -->
<string name="data_usage_mobile_limit_snoozed_title">Mobile data limit exceeded</string>
+ <!-- Notification title when Wi-Fi data usage has exceeded limit threshold. [CHAR LIMIT=32] -->
+ <string name="data_usage_wifi_limit_snoozed_title">Wi-Fi data limit exceeded</string>
<!-- Notification body when data usage has exceeded limit threshold. [CHAR LIMIT=32] -->
<string name="data_usage_limit_snoozed_body"><xliff:g id="size" example="3.8GB">%s</xliff:g> over specified limit</string>
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 1341dd4..bfca851 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -72,7 +72,6 @@ import com.android.internal.net.VpnConfig;
import com.android.internal.telephony.Phone;
import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
-
import com.google.android.collect.Lists;
import com.google.android.collect.Sets;
@@ -89,7 +88,6 @@ import java.util.Collection;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
/**
* @hide
@@ -251,6 +249,12 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private static final int EVENT_SEND_STICKY_BROADCAST_INTENT =
MAX_NETWORK_STATE_TRACKER_EVENT + 12;
+ /**
+ * Used internally to
+ * {@link NetworkStateTracker#setPolicyDataEnable(boolean)}.
+ */
+ private static final int EVENT_SET_POLICY_DATA_ENABLE = MAX_NETWORK_STATE_TRACKER_EVENT + 13;
+
private Handler mHandler;
// list of DeathRecipients used to make sure features are turned off when
@@ -1282,7 +1286,25 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (VDBG) {
log(mNetTrackers[ConnectivityManager.TYPE_MOBILE].toString() + enabled);
}
- mNetTrackers[ConnectivityManager.TYPE_MOBILE].setDataEnable(enabled);
+ mNetTrackers[ConnectivityManager.TYPE_MOBILE].setUserDataEnable(enabled);
+ }
+ }
+
+ @Override
+ public void setPolicyDataEnable(int networkType, boolean enabled) {
+ // only someone like NPMS should only be calling us
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+ mHandler.sendMessage(mHandler.obtainMessage(
+ EVENT_SET_POLICY_DATA_ENABLE, networkType, (enabled ? ENABLED : DISABLED)));
+ }
+
+ private void handleSetPolicyDataEnable(int networkType, boolean enabled) {
+ if (isNetworkTypeValid(networkType)) {
+ final NetworkStateTracker tracker = mNetTrackers[networkType];
+ if (tracker != null) {
+ tracker.setPolicyDataEnable(enabled);
+ }
}
}
@@ -2263,6 +2285,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
sendStickyBroadcast(intent);
break;
}
+ case EVENT_SET_POLICY_DATA_ENABLE: {
+ final int networkType = msg.arg1;
+ final boolean enabled = msg.arg2 == ENABLED;
+ handleSetPolicyDataEnable(networkType, enabled);
+ }
}
}
}
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 14d9665..84880f9 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -27,7 +27,10 @@ import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.ACTION_UID_REMOVED;
import static android.content.Intent.EXTRA_UID;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.ConnectivityManager.TYPE_WIMAX;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.SNOOZE_NEVER;
import static android.net.NetworkPolicy.WARNING_DISABLED;
@@ -40,8 +43,11 @@ import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
import static android.net.NetworkPolicyManager.dumpPolicy;
import static android.net.NetworkPolicyManager.dumpRules;
import static android.net.NetworkPolicyManager.isUidValidForPolicy;
+import static android.net.NetworkTemplate.MATCH_ETHERNET;
import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER;
import static android.net.NetworkTemplate.MATCH_MOBILE_4G;
+import static android.net.NetworkTemplate.MATCH_MOBILE_ALL;
+import static android.net.NetworkTemplate.MATCH_WIFI;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static com.android.internal.util.Preconditions.checkNotNull;
@@ -104,6 +110,7 @@ import android.util.Xml;
import com.android.internal.R;
import com.android.internal.os.AtomicFile;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.Objects;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
import com.google.android.collect.Sets;
@@ -129,8 +136,8 @@ import java.util.List;
import libcore.io.IoUtils;
/**
- * Service that maintains low-level network policy rules and collects usage
- * statistics to drive those rules.
+ * Service that maintains low-level network policy rules, using
+ * {@link NetworkStatsService} statistics to drive those rules.
* <p>
* Derives active rules by combining a given policy with other system status,
* and delivers to listeners, such as {@link ConnectivityManager}, for
@@ -195,6 +202,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private volatile boolean mScreenOn;
private volatile boolean mRestrictBackground;
+ private final boolean mSuppressDefaultPolicy;
+
/** Defined network policies. */
private HashMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = Maps.newHashMap();
/** Currently active network rules for ifaces. */
@@ -210,6 +219,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
/** Set of over-limit templates that have been notified. */
private HashSet<NetworkTemplate> mOverLimitNotified = Sets.newHashSet();
+ /** Set of currently active {@link Notification} tags. */
+ private HashSet<String> mActiveNotifs = Sets.newHashSet();
+ /** Current values from {@link #setPolicyDataEnable(int, boolean)}. */
+ private SparseBooleanArray mActiveNetworkEnabled = new SparseBooleanArray();
+
/** Foreground at both UID and PID granularity. */
private SparseBooleanArray mUidForeground = new SparseBooleanArray();
private SparseArray<SparseBooleanArray> mUidPidForeground = new SparseArray<
@@ -232,7 +246,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
IPowerManager powerManager, INetworkStatsService networkStats,
INetworkManagementService networkManagement) {
this(context, activityManager, powerManager, networkStats, networkManagement,
- NtpTrustedTime.getInstance(context), getSystemDir());
+ NtpTrustedTime.getInstance(context), getSystemDir(), false);
}
private static File getSystemDir() {
@@ -241,8 +255,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
IPowerManager powerManager, INetworkStatsService networkStats,
- INetworkManagementService networkManagement,
- TrustedTime time, File systemDir) {
+ INetworkManagementService networkManagement, TrustedTime time, File systemDir,
+ boolean suppressDefaultPolicy) {
mContext = checkNotNull(context, "missing context");
mActivityManager = checkNotNull(activityManager, "missing activityManager");
mPowerManager = checkNotNull(powerManager, "missing powerManager");
@@ -254,6 +268,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper(), mHandlerCallback);
+ mSuppressDefaultPolicy = suppressDefaultPolicy;
+
mPolicyFile = new AtomicFile(new File(systemDir, "netpolicy.xml"));
}
@@ -408,6 +424,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// READ_NETWORK_USAGE_HISTORY permission above.
synchronized (mRulesLock) {
+ updateNetworkEnabledLocked();
updateNotificationsLocked();
}
}
@@ -446,6 +463,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
Slog.w(TAG, "problem updating network stats");
}
+ updateNetworkEnabledLocked();
updateNotificationsLocked();
}
}
@@ -459,74 +477,70 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private void updateNotificationsLocked() {
if (LOGV) Slog.v(TAG, "updateNotificationsLocked()");
- // try refreshing time source when stale
- if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE) {
- mTime.forceRefresh();
- }
-
- final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
- : System.currentTimeMillis();
+ // keep track of previously active notifications
+ final HashSet<String> beforeNotifs = Sets.newHashSet();
+ beforeNotifs.addAll(mActiveNotifs);
+ mActiveNotifs.clear();
// TODO: when switching to kernel notifications, compute next future
// cycle boundary to recompute notifications.
// examine stats for each active policy
- for (NetworkPolicy policy : mNetworkRules.keySet()) {
+ final long currentTime = currentTimeMillis(true);
+ for (NetworkPolicy policy : mNetworkPolicy.values()) {
+ // ignore policies that aren't relevant to user
+ if (!isTemplateRelevant(policy.template)) continue;
+
final long start = computeLastCycleBoundary(currentTime, policy);
final long end = currentTime;
- final long totalBytes;
- try {
- final NetworkStats stats = mNetworkStats.getSummaryForNetwork(
- policy.template, start, end);
- final NetworkStats.Entry entry = stats.getValues(0, null);
- totalBytes = entry.rxBytes + entry.txBytes;
- } catch (RemoteException e) {
- Slog.w(TAG, "problem reading summary for template " + policy.template);
- continue;
- }
+ final long totalBytes = getTotalBytes(policy.template, start, end);
+ if (totalBytes == UNKNOWN_BYTES) continue;
if (policy.limitBytes != LIMIT_DISABLED && totalBytes >= policy.limitBytes) {
- cancelNotification(policy, TYPE_WARNING);
-
if (policy.lastSnooze >= start) {
- cancelNotification(policy, TYPE_LIMIT);
enqueueNotification(policy, TYPE_LIMIT_SNOOZED, totalBytes);
} else {
- cancelNotification(policy, TYPE_LIMIT_SNOOZED);
enqueueNotification(policy, TYPE_LIMIT, totalBytes);
notifyOverLimitLocked(policy.template);
}
} else {
- cancelNotification(policy, TYPE_LIMIT);
- cancelNotification(policy, TYPE_LIMIT_SNOOZED);
notifyUnderLimitLocked(policy.template);
if (policy.warningBytes != WARNING_DISABLED && totalBytes >= policy.warningBytes) {
enqueueNotification(policy, TYPE_WARNING, totalBytes);
- } else {
- cancelNotification(policy, TYPE_WARNING);
}
}
}
- // clear notifications for non-active policies
- for (NetworkPolicy policy : mNetworkPolicy.values()) {
- if (!mNetworkRules.containsKey(policy)) {
- cancelNotification(policy, TYPE_WARNING);
- cancelNotification(policy, TYPE_LIMIT);
- cancelNotification(policy, TYPE_LIMIT_SNOOZED);
- notifyUnderLimitLocked(policy.template);
- }
- }
-
// ongoing notification when restricting background data
if (mRestrictBackground) {
enqueueRestrictedNotification(TAG_ALLOW_BACKGROUND);
- } else {
- cancelNotification(TAG_ALLOW_BACKGROUND);
}
+
+ // cancel stale notifications that we didn't renew above
+ for (String tag : beforeNotifs) {
+ if (!mActiveNotifs.contains(tag)) {
+ cancelNotification(tag);
+ }
+ }
+ }
+
+ /**
+ * Test if given {@link NetworkTemplate} is relevant to user based on
+ * current device state, such as when {@link #getActiveSubscriberId()}
+ * matches. This is regardless of data connection status.
+ */
+ private boolean isTemplateRelevant(NetworkTemplate template) {
+ switch (template.getMatchRule()) {
+ case MATCH_MOBILE_3G_LOWER:
+ case MATCH_MOBILE_4G:
+ case MATCH_MOBILE_ALL:
+ // mobile templates are relevant when subscriberid is active
+ return Objects.equal(getActiveSubscriberId(), template.getSubscriberId());
+ }
+ return true;
}
/**
@@ -590,9 +604,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
case MATCH_MOBILE_4G:
title = res.getText(R.string.data_usage_4g_limit_title);
break;
- default:
+ case MATCH_MOBILE_ALL:
title = res.getText(R.string.data_usage_mobile_limit_title);
break;
+ case MATCH_WIFI:
+ title = res.getText(R.string.data_usage_wifi_limit_title);
+ break;
+ default:
+ title = null;
+ break;
}
builder.setSmallIcon(com.android.internal.R.drawable.ic_menu_block);
@@ -618,9 +638,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
case MATCH_MOBILE_4G:
title = res.getText(R.string.data_usage_4g_limit_snoozed_title);
break;
- default:
+ case MATCH_MOBILE_ALL:
title = res.getText(R.string.data_usage_mobile_limit_snoozed_title);
break;
+ case MATCH_WIFI:
+ title = res.getText(R.string.data_usage_wifi_limit_snoozed_title);
+ break;
+ default:
+ title = null;
+ break;
}
builder.setSmallIcon(R.drawable.ic_menu_info_details);
@@ -641,6 +667,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final int[] idReceived = new int[1];
mNotifManager.enqueueNotificationWithTag(
packageName, tag, 0x0, builder.getNotification(), idReceived);
+ mActiveNotifs.add(tag);
} catch (RemoteException e) {
Slog.w(TAG, "problem during enqueueNotification: " + e);
}
@@ -674,19 +701,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final int[] idReceived = new int[1];
mNotifManager.enqueueNotificationWithTag(packageName, tag,
0x0, builder.getNotification(), idReceived);
+ mActiveNotifs.add(tag);
} catch (RemoteException e) {
Slog.w(TAG, "problem during enqueueNotification: " + e);
}
}
- /**
- * Cancel any notification for combined {@link NetworkPolicy} and specific
- * type, like {@link #TYPE_LIMIT}.
- */
- private void cancelNotification(NetworkPolicy policy, int type) {
- cancelNotification(buildNotificationTag(policy, type));
- }
-
private void cancelNotification(String tag) {
// TODO: move to NotificationManager once we can mock it
try {
@@ -709,6 +729,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// permission above.
synchronized (mRulesLock) {
ensureActiveMobilePolicyLocked();
+ updateNetworkEnabledLocked();
updateNetworkRulesLocked();
updateNotificationsLocked();
}
@@ -716,6 +737,65 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
};
/**
+ * Proactively control network data connections when they exceed
+ * {@link NetworkPolicy#limitBytes}.
+ */
+ private void updateNetworkEnabledLocked() {
+ if (LOGV) Slog.v(TAG, "updateNetworkEnabledLocked()");
+
+ // TODO: reset any policy-disabled networks when any policy is removed
+ // completely, which is currently rare case.
+
+ final long currentTime = currentTimeMillis(true);
+ for (NetworkPolicy policy : mNetworkPolicy.values()) {
+ // shortcut when policy has no limit
+ if (policy.limitBytes == LIMIT_DISABLED) {
+ setNetworkTemplateEnabled(policy.template, true);
+ continue;
+ }
+
+ final long start = computeLastCycleBoundary(currentTime, policy);
+ final long end = currentTime;
+
+ final long totalBytes = getTotalBytes(policy.template, start, end);
+ if (totalBytes == UNKNOWN_BYTES) continue;
+
+ // disable data connection when over limit and not snoozed
+ final boolean overLimit = policy.limitBytes != LIMIT_DISABLED
+ && totalBytes > policy.limitBytes && policy.lastSnooze < start;
+ setNetworkTemplateEnabled(policy.template, !overLimit);
+ }
+ }
+
+ /**
+ * Control {@link IConnectivityManager#setPolicyDataEnable(int, boolean)}
+ * for the given {@link NetworkTemplate}.
+ */
+ private void setNetworkTemplateEnabled(NetworkTemplate template, boolean enabled) {
+ if (LOGD) Slog.d(TAG, "setting template=" + template + " enabled=" + enabled);
+ switch (template.getMatchRule()) {
+ case MATCH_MOBILE_3G_LOWER:
+ case MATCH_MOBILE_4G:
+ case MATCH_MOBILE_ALL:
+ // TODO: offer more granular control over radio states once
+ // 4965893 is available.
+ if (Objects.equal(getActiveSubscriberId(), template.getSubscriberId())) {
+ setPolicyDataEnable(TYPE_MOBILE, enabled);
+ setPolicyDataEnable(TYPE_WIMAX, enabled);
+ }
+ break;
+ case MATCH_WIFI:
+ setPolicyDataEnable(TYPE_WIFI, enabled);
+ break;
+ case MATCH_ETHERNET:
+ setPolicyDataEnable(TYPE_ETHERNET, enabled);
+ break;
+ default:
+ throw new IllegalArgumentException("unexpected template");
+ }
+ }
+
+ /**
* Examine all connected {@link NetworkState}, looking for
* {@link NetworkPolicy} that need to be enforced. When matches found, set
* remaining quota based on usage cycle and historical stats.
@@ -763,34 +843,19 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
}
- // try refreshing time source when stale
- if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE) {
- mTime.forceRefresh();
- }
-
- final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
- : System.currentTimeMillis();
-
final HashSet<String> newMeteredIfaces = Sets.newHashSet();
// apply each policy that we found ifaces for; compute remaining data
// based on current cycle and historical stats, and push to kernel.
+ final long currentTime = currentTimeMillis(true);
for (NetworkPolicy policy : mNetworkRules.keySet()) {
final String[] ifaces = mNetworkRules.get(policy);
final long start = computeLastCycleBoundary(currentTime, policy);
final long end = currentTime;
- final NetworkStats stats;
- final long total;
- try {
- stats = mNetworkStats.getSummaryForNetwork(policy.template, start, end);
- final NetworkStats.Entry entry = stats.getValues(0, null);
- total = entry.rxBytes + entry.txBytes;
- } catch (RemoteException e) {
- Slog.w(TAG, "problem reading summary for template " + policy.template);
- continue;
- }
+ final long totalBytes = getTotalBytes(policy.template, start, end);
+ if (totalBytes == UNKNOWN_BYTES) continue;
if (LOGD) {
Slog.d(TAG, "applying policy " + policy.toString() + " to ifaces "
@@ -798,11 +863,18 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
final boolean hasLimit = policy.limitBytes != LIMIT_DISABLED;
- final boolean hasWarning = policy.warningBytes != WARNING_DISABLED;
-
if (hasLimit) {
- // remaining "quota" is based on usage in current cycle
- final long quotaBytes = Math.max(0, policy.limitBytes - total);
+ final long quotaBytes;
+ if (policy.lastSnooze >= start) {
+ // snoozing past quota, but we still need to restrict apps,
+ // so push really high quota.
+ quotaBytes = Long.MAX_VALUE;
+ } else {
+ // remaining "quota" bytes are based on total usage in
+ // current cycle. kernel doesn't like 0-byte rules, so we
+ // set 1-byte quota and disable the radio later.
+ quotaBytes = Math.max(1, policy.limitBytes - totalBytes);
+ }
if (ifaces.length > 1) {
// TODO: switch to shared quota once NMS supports
@@ -811,10 +883,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
for (String iface : ifaces) {
removeInterfaceQuota(iface);
- if (quotaBytes > 0) {
- setInterfaceQuota(iface, quotaBytes);
- newMeteredIfaces.add(iface);
- }
+ setInterfaceQuota(iface, quotaBytes);
+ newMeteredIfaces.add(iface);
}
}
}
@@ -837,6 +907,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
*/
private void ensureActiveMobilePolicyLocked() {
if (LOGV) Slog.v(TAG, "ensureActiveMobilePolicyLocked()");
+ if (mSuppressDefaultPolicy) return;
+
final String subscriberId = getActiveSubscriberId();
final NetworkIdentity probeIdent = new NetworkIdentity(
TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, false);
@@ -1073,6 +1145,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
mNetworkPolicy.put(policy.template, policy);
}
+ updateNetworkEnabledLocked();
updateNetworkRulesLocked();
updateNotificationsLocked();
writePolicyLocked();
@@ -1093,14 +1166,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
public void snoozePolicy(NetworkTemplate template) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
- // try refreshing time source when stale
- if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE) {
- mTime.forceRefresh();
- }
-
- final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
- : System.currentTimeMillis();
-
+ final long currentTime = currentTimeMillis(true);
synchronized (mRulesLock) {
// find and snooze local policy that matches
final NetworkPolicy policy = mNetworkPolicy.get(template);
@@ -1110,6 +1176,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
policy.lastSnooze = currentTime;
+ updateNetworkEnabledLocked();
updateNetworkRulesLocked();
updateNotificationsLocked();
writePolicyLocked();
@@ -1173,22 +1240,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
return null;
}
- final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
- : System.currentTimeMillis();
+ final long currentTime = currentTimeMillis(false);
final long start = computeLastCycleBoundary(currentTime, policy);
final long end = currentTime;
// find total bytes used under policy
- long totalBytes = 0;
- try {
- final NetworkStats stats = mNetworkStats.getSummaryForNetwork(
- policy.template, start, end);
- final NetworkStats.Entry entry = stats.getValues(0, null);
- totalBytes = entry.rxBytes + entry.txBytes;
- } catch (RemoteException e) {
- Slog.w(TAG, "problem reading summary for template " + policy.template);
- }
+ final long totalBytes = getTotalBytes(policy.template, start, end);
+ if (totalBytes == UNKNOWN_BYTES) return null;
// report soft and hard limits under policy
final long softLimitBytes = policy.warningBytes != WARNING_DISABLED ? policy.warningBytes
@@ -1481,12 +1540,54 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
}
+ /**
+ * Control {@link IConnectivityManager#setPolicyDataEnable(int, boolean)},
+ * dispatching only when actually changed.
+ */
+ private void setPolicyDataEnable(int networkType, boolean enabled) {
+ synchronized (mActiveNetworkEnabled) {
+ final boolean prevEnabled = mActiveNetworkEnabled.get(networkType, true);
+ if (prevEnabled == enabled) return;
+
+ try {
+ mConnManager.setPolicyDataEnable(networkType, enabled);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "problem setting network enabled", e);
+ }
+
+ mActiveNetworkEnabled.put(networkType, enabled);
+ }
+ }
+
private String getActiveSubscriberId() {
final TelephonyManager telephony = (TelephonyManager) mContext.getSystemService(
Context.TELEPHONY_SERVICE);
return telephony.getSubscriberId();
}
+ private static final long UNKNOWN_BYTES = -1;
+
+ private long getTotalBytes(NetworkTemplate template, long start, long end) {
+ try {
+ final NetworkStats stats = mNetworkStats.getSummaryForNetwork(
+ template, start, end);
+ final NetworkStats.Entry entry = stats.getValues(0, null);
+ return entry.rxBytes + entry.txBytes;
+ } catch (RemoteException e) {
+ Slog.w(TAG, "problem reading summary for template " + template);
+ return UNKNOWN_BYTES;
+ }
+ }
+
+ private long currentTimeMillis(boolean allowRefresh) {
+ // try refreshing time source when stale
+ if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE && allowRefresh) {
+ mTime.forceRefresh();
+ }
+
+ return mTime.hasCache() ? mTime.currentTimeMillis() : System.currentTimeMillis();
+ }
+
private static Intent buildAllowBackgroundDataIntent() {
return new Intent(ACTION_ALLOW_BACKGROUND);
}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 845aa3f..f67d251 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -79,6 +79,7 @@ import com.google.common.util.concurrent.AbstractFuture;
import org.easymock.Capture;
import org.easymock.EasyMock;
import org.easymock.IAnswer;
+import org.easymock.IExpectationSetters;
import java.io.File;
import java.util.LinkedHashSet;
@@ -87,6 +88,8 @@ import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import libcore.io.IoUtils;
+
/**
* Tests for {@link NetworkPolicyManagerService}.
*/
@@ -117,6 +120,9 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
private Binder mStubBinder = new Binder();
+ private long mStartTime;
+ private long mElapsedRealtime;
+
private static final int UID_A = android.os.Process.FIRST_APPLICATION_UID + 800;
private static final int UID_B = android.os.Process.FIRST_APPLICATION_UID + 801;
@@ -128,6 +134,8 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
public void setUp() throws Exception {
super.setUp();
+ setCurrentTimeMillis(TEST_START);
+
// intercept various broadcasts, and pretend that uids have packages
mServiceContext = new BroadcastInterceptingContext(getContext()) {
@Override
@@ -160,8 +168,8 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
};
mPolicyDir = getContext().getFilesDir();
- for (File file : mPolicyDir.listFiles()) {
- file.delete();
+ if (mPolicyDir.exists()) {
+ IoUtils.deleteContents(mPolicyDir);
}
mActivityManager = createMock(IActivityManager.class);
@@ -173,9 +181,8 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
mConnManager = createMock(IConnectivityManager.class);
mNotifManager = createMock(INotificationManager.class);
- mService = new NetworkPolicyManagerService(
- mServiceContext, mActivityManager, mPowerManager, mStatsService,
- mNetworkManager, mTime, mPolicyDir);
+ mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager, mPowerManager,
+ mStatsService, mNetworkManager, mTime, mPolicyDir, true);
mService.bindConnectivityManager(mConnManager);
mService.bindNotificationManager(mNotifManager);
@@ -198,7 +205,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
// expect to answer screen status during systemReady()
expect(mPowerManager.isScreenOn()).andReturn(true).atLeastOnce();
- expectTime(System.currentTimeMillis());
+ expectCurrentTime();
replay();
mService.systemReady();
@@ -485,7 +492,6 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
}
public void testNetworkPolicyAppliedCycleLastMonth() throws Exception {
- long elapsedRealtime = 0;
NetworkState[] state = null;
NetworkStats stats = null;
Future<Void> future;
@@ -494,11 +500,13 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
final long TIME_MAR_10 = 1173484800000L;
final int CYCLE_DAY = 15;
+ setCurrentTimeMillis(TIME_MAR_10);
+
// first, pretend that wifi network comes online. no policy active,
// which means we shouldn't push limit to interface.
state = new NetworkState[] { buildWifi() };
expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
- expectTime(TIME_MAR_10 + elapsedRealtime);
+ expectCurrentTime();
expectClearNotifications();
future = expectMeteredIfacesChanged();
@@ -510,10 +518,10 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
// now change cycle to be on 15th, and test in early march, to verify we
// pick cycle day in previous month.
expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
- expectTime(TIME_MAR_10 + elapsedRealtime);
+ expectCurrentTime();
// pretend that 512 bytes total have happened
- stats = new NetworkStats(elapsedRealtime, 1)
+ stats = new NetworkStats(getElapsedRealtime(), 1)
.addIfaceValues(TEST_IFACE, 256L, 2L, 256L, 2L);
expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, TIME_MAR_10))
.andReturn(stats).atLeastOnce();
@@ -521,8 +529,6 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
// TODO: consider making strongly ordered mock
expectRemoveInterfaceQuota(TEST_IFACE);
expectSetInterfaceQuota(TEST_IFACE, 1536L);
- expectRemoveInterfaceAlert(TEST_IFACE);
- expectSetInterfaceAlert(TEST_IFACE, 512L);
expectClearNotifications();
future = expectMeteredIfacesChanged(TEST_IFACE);
@@ -558,8 +564,6 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
}
public void testOverWarningLimitNotification() throws Exception {
- long elapsedRealtime = 0;
- long currentTime = 0;
NetworkState[] state = null;
NetworkStats stats = null;
Future<Void> future;
@@ -569,14 +573,18 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
final long TIME_MAR_10 = 1173484800000L;
final int CYCLE_DAY = 15;
+ setCurrentTimeMillis(TIME_MAR_10);
+
// assign wifi policy
- elapsedRealtime = 0;
- currentTime = TIME_MAR_10 + elapsedRealtime;
state = new NetworkState[] {};
+ stats = new NetworkStats(getElapsedRealtime(), 1)
+ .addIfaceValues(TEST_IFACE, 0L, 0L, 0L, 0L);
{
- expectTime(currentTime);
+ expectCurrentTime();
expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
+ expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
+ .andReturn(stats).atLeastOnce();
expectClearNotifications();
future = expectMeteredIfacesChanged();
@@ -589,22 +597,19 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
}
// bring up wifi network
- elapsedRealtime += MINUTE_IN_MILLIS;
- currentTime = TIME_MAR_10 + elapsedRealtime;
- stats = new NetworkStats(elapsedRealtime, 1)
- .addIfaceValues(TEST_IFACE, 0L, 0L, 0L, 0L);
+ incrementCurrentTime(MINUTE_IN_MILLIS);
state = new NetworkState[] { buildWifi() };
+ stats = new NetworkStats(getElapsedRealtime(), 1)
+ .addIfaceValues(TEST_IFACE, 0L, 0L, 0L, 0L);
{
- expectTime(currentTime);
+ expectCurrentTime();
expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
- expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTime))
+ expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
.andReturn(stats).atLeastOnce();
expectRemoveInterfaceQuota(TEST_IFACE);
expectSetInterfaceQuota(TEST_IFACE, 2048L);
- expectRemoveInterfaceAlert(TEST_IFACE);
- expectSetInterfaceAlert(TEST_IFACE, 1024L);
expectClearNotifications();
future = expectMeteredIfacesChanged(TEST_IFACE);
@@ -616,14 +621,13 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
}
// go over warning, which should kick notification
- elapsedRealtime += MINUTE_IN_MILLIS;
- currentTime = TIME_MAR_10 + elapsedRealtime;
- stats = new NetworkStats(elapsedRealtime, 1)
+ incrementCurrentTime(MINUTE_IN_MILLIS);
+ stats = new NetworkStats(getElapsedRealtime(), 1)
.addIfaceValues(TEST_IFACE, 1536L, 15L, 0L, 0L);
{
- expectTime(currentTime);
- expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTime))
+ expectCurrentTime();
+ expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
.andReturn(stats).atLeastOnce();
expectForceUpdate();
@@ -637,15 +641,15 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
}
// go over limit, which should kick notification and dialog
- elapsedRealtime += MINUTE_IN_MILLIS;
- currentTime = TIME_MAR_10 + elapsedRealtime;
- stats = new NetworkStats(elapsedRealtime, 1)
+ incrementCurrentTime(MINUTE_IN_MILLIS);
+ stats = new NetworkStats(getElapsedRealtime(), 1)
.addIfaceValues(TEST_IFACE, 5120L, 512L, 0L, 0L);
{
- expectTime(currentTime);
- expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTime))
+ expectCurrentTime();
+ expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
.andReturn(stats).atLeastOnce();
+ expectPolicyDataEnable(TYPE_WIFI, false).atLeastOnce();
expectForceUpdate();
expectClearNotifications();
@@ -658,21 +662,23 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
}
// now snooze policy, which should remove quota
- elapsedRealtime += MINUTE_IN_MILLIS;
- currentTime = TIME_MAR_10 + elapsedRealtime;
+ incrementCurrentTime(MINUTE_IN_MILLIS);
{
- expectTime(currentTime);
+ expectCurrentTime();
expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
- expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTime))
+ expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
.andReturn(stats).atLeastOnce();
+ expectPolicyDataEnable(TYPE_WIFI, true).atLeastOnce();
+ // snoozed interface still has high quota so background data is
+ // still restricted.
expectRemoveInterfaceQuota(TEST_IFACE);
- expectRemoveInterfaceAlert(TEST_IFACE);
+ expectSetInterfaceQuota(TEST_IFACE, Long.MAX_VALUE);
expectClearNotifications();
tag = expectEnqueueNotification();
- future = expectMeteredIfacesChanged();
+ future = expectMeteredIfacesChanged(TEST_IFACE);
replay();
mService.snoozePolicy(sTemplateWifi);
@@ -700,10 +706,10 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
return new NetworkState(info, prop, null);
}
- private void expectTime(long currentTime) throws Exception {
+ private void expectCurrentTime() throws Exception {
expect(mTime.forceRefresh()).andReturn(false).anyTimes();
expect(mTime.hasCache()).andReturn(true).anyTimes();
- expect(mTime.currentTimeMillis()).andReturn(currentTime).anyTimes();
+ expect(mTime.currentTimeMillis()).andReturn(currentTimeMillis()).anyTimes();
expect(mTime.getCacheAge()).andReturn(0L).anyTimes();
expect(mTime.getCacheCertainty()).andReturn(0L).anyTimes();
}
@@ -770,6 +776,12 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
return future;
}
+ private <T> IExpectationSetters<T> expectPolicyDataEnable(int type, boolean enabled)
+ throws Exception {
+ mConnManager.setPolicyDataEnable(type, enabled);
+ return expectLastCall();
+ }
+
private static class FutureAnswer extends AbstractFuture<Void> implements IAnswer<Void> {
@Override
public Void get() throws InterruptedException, ExecutionException {
@@ -818,6 +830,23 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
Integer.toString(expected), actualTag.substring(actualTag.lastIndexOf(':') + 1));
}
+ private long getElapsedRealtime() {
+ return mElapsedRealtime;
+ }
+
+ private void setCurrentTimeMillis(long currentTimeMillis) {
+ mStartTime = currentTimeMillis;
+ mElapsedRealtime = 0L;
+ }
+
+ private long currentTimeMillis() {
+ return mStartTime + mElapsedRealtime;
+ }
+
+ private void incrementCurrentTime(long duration) {
+ mElapsedRealtime += duration;
+ }
+
private void replay() {
EasyMock.replay(mActivityManager, mPowerManager, mStatsService, mPolicyListener,
mNetworkManager, mTime, mConnManager, mNotifManager);
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index 3bd78e0..42ea4f2 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -36,8 +36,8 @@ import android.os.Messenger;
import android.os.SystemProperties;
import android.preference.PreferenceManager;
import android.provider.Settings;
-import android.telephony.ServiceState;
import android.provider.Settings.SettingNotFoundException;
+import android.telephony.ServiceState;
import android.text.TextUtils;
import android.util.Log;
@@ -126,9 +126,10 @@ public abstract class DataConnectionTracker extends Handler {
protected static final int EVENT_RESTART_RADIO = BASE + 26;
protected static final int EVENT_SET_INTERNAL_DATA_ENABLE = BASE + 27;
protected static final int EVENT_RESET_DONE = BASE + 28;
- public static final int CMD_SET_DATA_ENABLE = BASE + 29;
+ public static final int CMD_SET_USER_DATA_ENABLE = BASE + 29;
public static final int EVENT_CLEAN_UP_ALL_CONNECTIONS = BASE + 30;
public static final int CMD_SET_DEPENDENCY_MET = BASE + 31;
+ public static final int CMD_SET_POLICY_DATA_ENABLE = BASE + 32;
/***** Constants *****/
@@ -153,6 +154,8 @@ public abstract class DataConnectionTracker extends Handler {
protected static final int APN_DELAY_MILLIS =
SystemProperties.getInt("persist.radio.apn_delay", 5000);
+ protected Object mDataEnabledLock = new Object();
+
// responds to the setInternalDataEnabled call - used internally to turn off data
// for example during emergency calls
protected boolean mInternalDataEnabled = true;
@@ -160,11 +163,12 @@ public abstract class DataConnectionTracker extends Handler {
// responds to public (user) API to enable/disable data use
// independent of mInternalDataEnabled and requests for APN access
// persisted
- protected boolean mDataEnabled = true;
+ protected boolean mUserDataEnabled = true;
+ protected boolean mPolicyDataEnabled = true;
- protected boolean[] dataEnabled = new boolean[APN_NUM_TYPES];
+ private boolean[] dataEnabled = new boolean[APN_NUM_TYPES];
- protected int enabledCount = 0;
+ private int enabledCount = 0;
/* Currently requested APN type (TODO: This should probably be a parameter not a member) */
protected String mRequestedApnType = Phone.APN_TYPE_DEFAULT;
@@ -408,8 +412,8 @@ public abstract class DataConnectionTracker extends Handler {
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
filter.addAction(INTENT_SET_FAIL_DATA_SETUP_COUNTER);
- mDataEnabled = Settings.Secure.getInt(mPhone.getContext().getContentResolver(),
- Settings.Secure.MOBILE_DATA, 1) == 1;
+ mUserDataEnabled = Settings.Secure.getInt(
+ mPhone.getContext().getContentResolver(), Settings.Secure.MOBILE_DATA, 1) == 1;
// TODO: Why is this registering the phone as the receiver of the intent
// and not its own handler?
@@ -630,13 +634,12 @@ public abstract class DataConnectionTracker extends Handler {
onResetDone((AsyncResult) msg.obj);
break;
}
- case CMD_SET_DATA_ENABLE: {
- boolean enabled = (msg.arg1 == ENABLED) ? true : false;
- if (DBG) log("CMD_SET_DATA_ENABLE enabled=" + enabled);
- onSetDataEnabled(enabled);
+ case CMD_SET_USER_DATA_ENABLE: {
+ final boolean enabled = (msg.arg1 == ENABLED) ? true : false;
+ if (DBG) log("CMD_SET_USER_DATA_ENABLE enabled=" + enabled);
+ onSetUserDataEnabled(enabled);
break;
}
-
case CMD_SET_DEPENDENCY_MET: {
boolean met = (msg.arg1 == ENABLED) ? true : false;
if (DBG) log("CMD_SET_DEPENDENCY_MET met=" + met);
@@ -649,7 +652,11 @@ public abstract class DataConnectionTracker extends Handler {
}
break;
}
-
+ case CMD_SET_POLICY_DATA_ENABLE: {
+ final boolean enabled = (msg.arg1 == ENABLED) ? true : false;
+ onSetPolicyDataEnabled(enabled);
+ break;
+ }
default:
Log.e("DATA", "Unidentified event msg=" + msg);
break;
@@ -662,8 +669,12 @@ public abstract class DataConnectionTracker extends Handler {
* @return {@code false} if data connectivity has been explicitly disabled,
* {@code true} otherwise.
*/
- public synchronized boolean getAnyDataEnabled() {
- boolean result = (mInternalDataEnabled && mDataEnabled && (enabledCount != 0));
+ public boolean getAnyDataEnabled() {
+ final boolean result;
+ synchronized (mDataEnabledLock) {
+ result = (mInternalDataEnabled && mUserDataEnabled && mPolicyDataEnabled
+ && (enabledCount != 0));
+ }
if (!result && DBG) log("getAnyDataEnabled " + result);
return result;
}
@@ -985,18 +996,18 @@ public abstract class DataConnectionTracker extends Handler {
return true;
}
- protected void onSetInternalDataEnabled(boolean enable) {
- boolean prevEnabled = getAnyDataEnabled();
- if (mInternalDataEnabled != enable) {
- synchronized (this) {
- mInternalDataEnabled = enable;
- }
- if (prevEnabled != getAnyDataEnabled()) {
- if (!prevEnabled) {
- resetAllRetryCounts();
- onTrySetupData(Phone.REASON_DATA_ENABLED);
- } else {
- cleanUpAllConnections(null);
+ protected void onSetInternalDataEnabled(boolean enabled) {
+ synchronized (mDataEnabledLock) {
+ final boolean prevEnabled = getAnyDataEnabled();
+ if (mInternalDataEnabled != enabled) {
+ mInternalDataEnabled = enabled;
+ if (prevEnabled != getAnyDataEnabled()) {
+ if (!prevEnabled) {
+ resetAllRetryCounts();
+ onTrySetupData(Phone.REASON_DATA_ENABLED);
+ } else {
+ cleanUpAllConnections(null);
+ }
}
}
}
@@ -1010,20 +1021,20 @@ public abstract class DataConnectionTracker extends Handler {
public abstract boolean isAnyActiveDataConnections();
- protected void onSetDataEnabled(boolean enable) {
- boolean prevEnabled = getAnyDataEnabled();
- if (mDataEnabled != enable) {
- synchronized (this) {
- mDataEnabled = enable;
- }
- Settings.Secure.putInt(mPhone.getContext().getContentResolver(),
- Settings.Secure.MOBILE_DATA, enable ? 1 : 0);
- if (prevEnabled != getAnyDataEnabled()) {
- if (!prevEnabled) {
- resetAllRetryCounts();
- onTrySetupData(Phone.REASON_DATA_ENABLED);
- } else {
- onCleanUpAllConnections(Phone.REASON_DATA_DISABLED);
+ protected void onSetUserDataEnabled(boolean enabled) {
+ synchronized (mDataEnabledLock) {
+ final boolean prevEnabled = getAnyDataEnabled();
+ if (mUserDataEnabled != enabled) {
+ mUserDataEnabled = enabled;
+ Settings.Secure.putInt(mPhone.getContext().getContentResolver(),
+ Settings.Secure.MOBILE_DATA, enabled ? 1 : 0);
+ if (prevEnabled != getAnyDataEnabled()) {
+ if (!prevEnabled) {
+ resetAllRetryCounts();
+ onTrySetupData(Phone.REASON_DATA_ENABLED);
+ } else {
+ onCleanUpAllConnections(Phone.REASON_DATA_DISABLED);
+ }
}
}
}
@@ -1032,6 +1043,23 @@ public abstract class DataConnectionTracker extends Handler {
protected void onSetDependencyMet(String apnType, boolean met) {
}
+ protected void onSetPolicyDataEnabled(boolean enabled) {
+ synchronized (mDataEnabledLock) {
+ final boolean prevEnabled = getAnyDataEnabled();
+ if (mPolicyDataEnabled != enabled) {
+ mPolicyDataEnabled = enabled;
+ if (prevEnabled != getAnyDataEnabled()) {
+ if (!prevEnabled) {
+ resetAllRetryCounts();
+ onTrySetupData(Phone.REASON_DATA_ENABLED);
+ } else {
+ onCleanUpAllConnections(Phone.REASON_DATA_DISABLED);
+ }
+ }
+ }
+ }
+ }
+
protected String getReryConfig(boolean forDefault) {
int rt = mPhone.getServiceState().getRadioTechnology();
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index 800615c..1a077d0 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -175,6 +175,11 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
@Override
protected boolean isDataAllowed() {
+ final boolean internalDataEnabled;
+ synchronized (mDataEnabledLock) {
+ internalDataEnabled = mInternalDataEnabled;
+ }
+
int psState = mCdmaPhone.mSST.getCurrentDataConnectionState();
boolean roaming = (mPhone.getServiceState().getRoaming() && !getDataOnRoamingEnabled());
boolean desiredPowerState = mCdmaPhone.mSST.getDesiredPowerState();
@@ -187,7 +192,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
(mCdmaPhone.mSST.isConcurrentVoiceAndDataAllowed() ||
mPhone.getState() == Phone.State.IDLE) &&
!roaming &&
- mInternalDataEnabled &&
+ internalDataEnabled &&
desiredPowerState &&
!mPendingRestartRadio &&
!mCdmaPhone.needsOtaServiceProvisioning();
@@ -205,7 +210,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
reason += " - concurrentVoiceAndData not allowed and state= " + mPhone.getState();
}
if (roaming) reason += " - Roaming";
- if (!mInternalDataEnabled) reason += " - mInternalDataEnabled= false";
+ if (!internalDataEnabled) reason += " - mInternalDataEnabled= false";
if (!desiredPowerState) reason += " - desiredPowerState= false";
if (mPendingRestartRadio) reason += " - mPendingRestartRadio= true";
if (mCdmaPhone.needsOtaServiceProvisioning()) reason += " - needs Provisioning";
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 3236901..c8671c1 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -27,14 +27,14 @@ import android.database.ContentObserver;
import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.LinkAddress;
+import android.net.LinkCapabilities;
+import android.net.LinkProperties;
import android.net.LinkProperties.CompareResult;
+import android.net.NetworkConfig;
import android.net.NetworkUtils;
import android.net.ProxyProperties;
import android.net.TrafficStats;
import android.net.Uri;
-import android.net.LinkCapabilities;
-import android.net.LinkProperties;
-import android.net.NetworkConfig;
import android.os.AsyncResult;
import android.os.Message;
import android.os.SystemClock;
@@ -49,34 +49,27 @@ import android.telephony.gsm.GsmCellLocation;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
-import android.preference.PreferenceManager;
-import com.android.internal.R;
import com.android.internal.telephony.ApnContext;
import com.android.internal.telephony.ApnSetting;
import com.android.internal.telephony.DataCallState;
import com.android.internal.telephony.DataConnection;
+import com.android.internal.telephony.DataConnection.FailCause;
import com.android.internal.telephony.DataConnection.UpdateLinkPropertyResult;
import com.android.internal.telephony.DataConnectionAc;
import com.android.internal.telephony.DataConnectionTracker;
+import com.android.internal.telephony.EventLogTags;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneBase;
-import com.android.internal.telephony.RetryManager;
-import com.android.internal.telephony.EventLogTags;
-import com.android.internal.telephony.DataConnection.FailCause;
import com.android.internal.telephony.RILConstants;
+import com.android.internal.telephony.RetryManager;
import com.android.internal.util.AsyncChannel;
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.List;
-import java.util.Map;
import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
/**
* {@hide}
@@ -523,16 +516,18 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
* {@code true} otherwise.
*/
@Override
- public synchronized boolean getAnyDataEnabled() {
- if (!(mInternalDataEnabled && mDataEnabled)) return false;
- for (ApnContext apnContext : mApnContexts.values()) {
- // Make sure we dont have a context that going down
- // and is explicitly disabled.
- if (isDataAllowed(apnContext)) {
- return true;
+ public boolean getAnyDataEnabled() {
+ synchronized (mDataEnabledLock) {
+ if (!(mInternalDataEnabled && mUserDataEnabled && mPolicyDataEnabled)) return false;
+ for (ApnContext apnContext : mApnContexts.values()) {
+ // Make sure we dont have a context that going down
+ // and is explicitly disabled.
+ if (isDataAllowed(apnContext)) {
+ return true;
+ }
}
+ return false;
}
- return false;
}
private boolean isDataAllowed(ApnContext apnContext) {
@@ -570,6 +565,11 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
@Override
protected boolean isDataAllowed() {
+ final boolean internalDataEnabled;
+ synchronized (mDataEnabledLock) {
+ internalDataEnabled = mInternalDataEnabled;
+ }
+
int gprsState = mPhone.getServiceStateTracker().getCurrentDataConnectionState();
boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
@@ -577,7 +577,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
(gprsState == ServiceState.STATE_IN_SERVICE || mAutoAttachOnCreation) &&
mPhone.mIccRecords.getRecordsLoaded() &&
mPhone.getState() == Phone.State.IDLE &&
- mInternalDataEnabled &&
+ internalDataEnabled &&
(!mPhone.getServiceState().getRoaming() || getDataOnRoamingEnabled()) &&
!mIsPsRestricted &&
desiredPowerState;
@@ -590,7 +590,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
if (mPhone.getState() != Phone.State.IDLE) {
reason += " - PhoneState= " + mPhone.getState();
}
- if (!mInternalDataEnabled) reason += " - mInternalDataEnabled= false";
+ if (!internalDataEnabled) reason += " - mInternalDataEnabled= false";
if (mPhone.getServiceState().getRoaming() && !getDataOnRoamingEnabled()) {
reason += " - Roaming and data roaming not enabled";
}
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index c20c716..956c3f2 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -16,23 +16,20 @@
package android.net.wifi;
-
-
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.net.ConnectivityManager;
import android.net.LinkCapabilities;
-import android.net.NetworkInfo;
import android.net.LinkProperties;
+import android.net.NetworkInfo;
import android.net.NetworkStateTracker;
import android.net.wifi.p2p.WifiP2pManager;
import android.os.Handler;
import android.os.Message;
+import android.util.Slog;
import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* Track the state of wifi for connectivity service.
@@ -44,6 +41,8 @@ public class WifiStateTracker implements NetworkStateTracker {
private static final String NETWORKTYPE = "WIFI";
private static final String TAG = "WifiStateTracker";
+ private static final boolean LOGV = true;
+
private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false);
@@ -135,11 +134,14 @@ public class WifiStateTracker implements NetworkStateTracker {
return mNetworkInfo.isAvailable();
}
- /**
- * @param enabled
- */
- public void setDataEnable(boolean enabled) {
- android.util.Log.d(TAG, "setDataEnabled: IGNORING enabled=" + enabled);
+ @Override
+ public void setUserDataEnable(boolean enabled) {
+ Slog.w(TAG, "ignoring setUserDataEnable(" + enabled + ")");
+ }
+
+ @Override
+ public void setPolicyDataEnable(boolean enabled) {
+ Slog.w(TAG, "ignoring setPolicyDataEnable(" + enabled + ")");
}
/**