diff options
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 + ")"); } /** |