From 3a844fcf5a0e70a19c38dc500306b9ebe4e1413b Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Tue, 16 Aug 2011 14:37:57 -0700 Subject: [PATCH] Background data notification, API clean up. When restricting background data, show ongoing notification to give easy access to re-enable. Deprecate getBackgroundDataSetting() API to always return true, since NetworkInfo.isConnected() is new source of truth. Handle upgrade path by reading from existing secure value, and kick one last broadcast when changing value. Remove background data code from ConnectivityService. Remove warning alerts, since they push ifaces into restricted list; should only happen when iface has limit. Bug: 5163559, 5129421 Change-Id: I0064d9d643656a4d32aaae51d4a58bce49fe295f --- api/current.txt | 4 +- .../java/android/net/ConnectivityManager.java | 22 +-- .../android/net/IConnectivityManager.aidl | 4 - core/java/android/provider/Settings.java | 1 + core/res/res/values/strings.xml | 7 +- .../android/server/ConnectivityService.java | 47 ------- .../net/NetworkPolicyManagerService.java | 133 ++++++++++++++---- 7 files changed, 123 insertions(+), 95 deletions(-) diff --git a/api/current.txt b/api/current.txt index c70cea85bfd07..3a20260f230d3 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11400,7 +11400,7 @@ package android.net { method public android.net.NetworkInfo getActiveNetworkInfo(); method public android.net.NetworkQuotaInfo getActiveNetworkQuotaInfo(); method public android.net.NetworkInfo[] getAllNetworkInfo(); - method public boolean getBackgroundDataSetting(); + method public deprecated boolean getBackgroundDataSetting(); method public android.net.NetworkInfo getNetworkInfo(int); method public int getNetworkPreference(); method public static boolean isNetworkTypeValid(int); @@ -17049,7 +17049,7 @@ package android.provider { field public static final java.lang.String ALLOWED_GEOLOCATION_ORIGINS = "allowed_geolocation_origins"; field public static final java.lang.String ALLOW_MOCK_LOCATION = "mock_location"; field public static final java.lang.String ANDROID_ID = "android_id"; - field public static final java.lang.String BACKGROUND_DATA = "background_data"; + field public static final deprecated java.lang.String BACKGROUND_DATA = "background_data"; field public static final java.lang.String BLUETOOTH_ON = "bluetooth_on"; field public static final android.net.Uri CONTENT_URI; field public static final java.lang.String DATA_ROAMING = "data_roaming"; diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index eb9cd213dd8ea..68e80ff7d2623 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -21,6 +21,7 @@ import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.os.Binder; +import android.os.Build.VERSION_CODES; import android.os.RemoteException; import java.net.InetAddress; @@ -494,16 +495,19 @@ public class ConnectivityManager { *

* All applications that have background services that use the network * should listen to {@link #ACTION_BACKGROUND_DATA_SETTING_CHANGED}. + *

+ * As of {@link VERSION_CODES#ICE_CREAM_SANDWICH}, availability of + * background data depends on several combined factors, and this method will + * always return {@code true}. Instead, when background data is unavailable, + * {@link #getActiveNetworkInfo()} will now appear disconnected. * * @return Whether background data usage is allowed. */ + @Deprecated public boolean getBackgroundDataSetting() { - try { - return mService.getBackgroundDataSetting(); - } catch (RemoteException e) { - // Err on the side of safety - return false; - } + // assume that background data is allowed; final authority is + // NetworkInfo which may be blocked. + return true; } /** @@ -516,11 +520,9 @@ public class ConnectivityManager { * @see #getBackgroundDataSetting() * @hide */ + @Deprecated public void setBackgroundDataSetting(boolean allowBackgroundData) { - try { - mService.setBackgroundDataSetting(allowBackgroundData); - } catch (RemoteException e) { - } + // ignored } /** diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index f3912005a667a..1b95b60a536d0 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -63,10 +63,6 @@ interface IConnectivityManager boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress); - boolean getBackgroundDataSetting(); - - void setBackgroundDataSetting(boolean allowBackgroundData); - boolean getMobileDataEnabled(); void setMobileDataEnabled(boolean enabled); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index f8702b9d91178..a380efcc73389 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3081,6 +3081,7 @@ public final class Settings { * Whether background data usage is allowed by the user. See * ConnectivityManager for more info. */ + @Deprecated public static final String BACKGROUND_DATA = "background_data"; /** diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 7d6d25c840e4e..4f62f126958be 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -3073,7 +3073,7 @@ Mobile data disabled - tap to enable + Touch to enable 2G-3G data limit exceeded @@ -3084,6 +3084,11 @@ %s over specified limit + + Background data restricted + + Touch to remove restriction + Security certificate diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index acfc7a4d95226..2bb952ee8b2d8 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -163,8 +163,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { private boolean mTestMode; private static ConnectivityService sServiceInstance; - private AtomicBoolean mBackgroundDataEnabled = new AtomicBoolean(true); - private INetworkManagementService mNetd; private INetworkPolicyManager mPolicyManager; @@ -212,13 +210,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { private static final int EVENT_INET_CONDITION_HOLD_END = MAX_NETWORK_STATE_TRACKER_EVENT + 5; - /** - * used internally to set the background data preference - * arg1 = TRUE for enabled, FALSE for disabled - */ - private static final int EVENT_SET_BACKGROUND_DATA = - MAX_NETWORK_STATE_TRACKER_EVENT + 6; - /** * used internally to set enable/disable cellular data * arg1 = ENBALED or DISABLED @@ -317,9 +308,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { handlerThread.start(); mHandler = new MyHandler(handlerThread.getLooper()); - mBackgroundDataEnabled.set(Settings.Secure.getInt(context.getContentResolver(), - Settings.Secure.BACKGROUND_DATA, 1) == 1); - // setup our unique device name if (TextUtils.isEmpty(SystemProperties.get("net.hostname"))) { String id = Settings.Secure.getString(context.getContentResolver(), @@ -1225,35 +1213,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { return true; } - /** - * @see ConnectivityManager#getBackgroundDataSetting() - */ - public boolean getBackgroundDataSetting() { - return mBackgroundDataEnabled.get(); - } - - /** - * @see ConnectivityManager#setBackgroundDataSetting(boolean) - */ - public void setBackgroundDataSetting(boolean allowBackgroundDataUsage) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CHANGE_BACKGROUND_DATA_SETTING, - "ConnectivityService"); - - mBackgroundDataEnabled.set(allowBackgroundDataUsage); - - mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_BACKGROUND_DATA, - (allowBackgroundDataUsage ? ENABLED : DISABLED), 0)); - } - - private void handleSetBackgroundData(boolean enabled) { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.BACKGROUND_DATA, enabled ? 1 : 0); - Intent broadcast = new Intent( - ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED); - mContext.sendBroadcast(broadcast); - } - /** * @see ConnectivityManager#getMobileDataEnabled() */ @@ -2268,12 +2227,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { handleSetNetworkPreference(preference); break; } - case EVENT_SET_BACKGROUND_DATA: - { - boolean enabled = (msg.arg1 == ENABLED); - handleSetBackgroundData(enabled); - break; - } case EVENT_SET_MOBILE_DATA: { boolean enabled = (msg.arg1 == ENABLED); diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java index 9c3d166c5481f..14d966511d6f0 100644 --- a/services/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java @@ -89,6 +89,7 @@ import android.os.IPowerManager; import android.os.Message; import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.provider.Settings; import android.telephony.TelephonyManager; import android.text.format.Formatter; import android.text.format.Time; @@ -168,6 +169,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final String ATTR_UID = "uid"; private static final String ATTR_POLICY = "policy"; + private static final String TAG_ALLOW_BACKGROUND = TAG + ":allowBackground"; + + // @VisibleForTesting + public static final String ACTION_ALLOW_BACKGROUND = + "com.android.server.action.ACTION_ALLOW_BACKGROUND"; + private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS; private static final int MSG_RULES_CHANGED = 0x1; @@ -185,8 +192,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private final Object mRulesLock = new Object(); - private boolean mScreenOn; - private boolean mRestrictBackground; + private volatile boolean mScreenOn; + private volatile boolean mRestrictBackground; /** Defined network policies. */ private HashMap mNetworkPolicy = Maps.newHashMap(); @@ -265,6 +272,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (mRestrictBackground) { updateRulesForRestrictBackgroundLocked(); + updateNotificationsLocked(); } } @@ -309,6 +317,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mContext.registerReceiver( mStatsReceiver, statsFilter, READ_NETWORK_USAGE_HISTORY, mHandler); + // listen for restrict background changes from notifications + final IntentFilter allowFilter = new IntentFilter(ACTION_ALLOW_BACKGROUND); + mContext.registerReceiver(mAllowReceiver, allowFilter, MANAGE_NETWORK_POLICY, mHandler); + } private IProcessObserver mProcessObserver = new IProcessObserver.Stub() { @@ -401,6 +413,20 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } }; + /** + * Receiver that watches for {@link Notification} control of + * {@link #mRestrictBackground}. + */ + private BroadcastReceiver mAllowReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // on background handler thread, and verified MANAGE_NETWORK_POLICY + // permission above. + + setRestrictBackground(false); + } + }; + /** * Observer that watches for {@link INetworkManagementService} alerts. */ @@ -494,6 +520,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { notifyUnderLimitLocked(policy.template); } } + + // ongoing notification when restricting background data + if (mRestrictBackground) { + enqueueRestrictedNotification(TAG_ALLOW_BACKGROUND); + } else { + cancelNotification(TAG_ALLOW_BACKGROUND); + } } /** @@ -614,16 +647,52 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } /** - * Cancel any notification for combined {@link NetworkPolicy} and specific - * type, like {@link #TYPE_LIMIT}. + * Show ongoing notification to reflect that {@link #mRestrictBackground} + * has been enabled. */ - private void cancelNotification(NetworkPolicy policy, int type) { - final String tag = buildNotificationTag(policy, type); + private void enqueueRestrictedNotification(String tag) { + final Resources res = mContext.getResources(); + final Notification.Builder builder = new Notification.Builder(mContext); + + final CharSequence title = res.getText(R.string.data_usage_restricted_title); + final CharSequence body = res.getString(R.string.data_usage_restricted_body); + + builder.setOnlyAlertOnce(true); + builder.setOngoing(true); + builder.setSmallIcon(R.drawable.ic_menu_info_details); + builder.setTicker(title); + builder.setContentTitle(title); + builder.setContentText(body); + + final Intent intent = buildAllowBackgroundDataIntent(); + builder.setContentIntent( + PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)); // TODO: move to NotificationManager once we can mock it try { final String packageName = mContext.getPackageName(); - mNotifManager.cancelNotificationWithTag(packageName, tag, 0x0); + final int[] idReceived = new int[1]; + mNotifManager.enqueueNotificationWithTag(packageName, tag, + 0x0, builder.getNotification(), idReceived); + } 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 { + final String packageName = mContext.getPackageName(); + mNotifManager.cancelNotificationWithTag( + packageName, tag, 0x0); } catch (RemoteException e) { Slog.w(TAG, "problem during enqueueNotification: " + e); } @@ -731,15 +800,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final boolean hasLimit = policy.limitBytes != LIMIT_DISABLED; final boolean hasWarning = policy.warningBytes != WARNING_DISABLED; - if (hasLimit || hasWarning) { - final long quotaBytes; - if (hasLimit) { - // remaining "quota" is based on usage in current cycle - quotaBytes = Math.max(0, policy.limitBytes - total); - } else { - // to track warning alert later, use a high quota - quotaBytes = Long.MAX_VALUE; - } + if (hasLimit) { + // remaining "quota" is based on usage in current cycle + final long quotaBytes = Math.max(0, policy.limitBytes - total); if (ifaces.length > 1) { // TODO: switch to shared quota once NMS supports @@ -754,16 +817,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } } - - if (hasWarning) { - final long alertBytes = Math.max(0, policy.warningBytes - total); - for (String iface : ifaces) { - removeInterfaceAlert(iface); - if (alertBytes > 0) { - setInterfaceAlert(iface, alertBytes); - } - } - } } // remove quota on any trailing interfaces @@ -839,11 +892,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mRestrictBackground = readBooleanAttribute( in, ATTR_RESTRICT_BACKGROUND); } else { - try { - mRestrictBackground = !mConnManager.getBackgroundDataSetting(); - } catch (RemoteException e) { - mRestrictBackground = false; - } + mRestrictBackground = false; } } else if (TAG_NETWORK_POLICY.equals(tag)) { @@ -879,6 +928,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } catch (FileNotFoundException e) { // missing policy is okay, probably first boot + upgradeLegacyBackgroundData(); } catch (IOException e) { Slog.e(TAG, "problem reading network stats", e); } catch (XmlPullParserException e) { @@ -888,6 +938,22 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + /** + * Upgrade legacy background data flags, notifying listeners of one last + * change to always-true. + */ + private void upgradeLegacyBackgroundData() { + mRestrictBackground = Settings.Secure.getInt( + mContext.getContentResolver(), Settings.Secure.BACKGROUND_DATA, 1) != 1; + + // kick off one last broadcast if restricted + if (mRestrictBackground) { + final Intent broadcast = new Intent( + ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED); + mContext.sendBroadcast(broadcast); + } + } + private void writePolicyLocked() { if (LOGV) Slog.v(TAG, "writePolicyLocked()"); @@ -1057,6 +1123,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { synchronized (mRulesLock) { mRestrictBackground = restrictBackground; updateRulesForRestrictBackgroundLocked(); + updateNotificationsLocked(); writePolicyLocked(); } } @@ -1420,6 +1487,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return telephony.getSubscriberId(); } + private static Intent buildAllowBackgroundDataIntent() { + return new Intent(ACTION_ALLOW_BACKGROUND); + } + private static Intent buildNetworkOverLimitIntent(NetworkTemplate template) { final Intent intent = new Intent(); intent.setComponent(new ComponentName(