From 70c57c2810eb5004ac6487921bfdf0880f818e3c Mon Sep 17 00:00:00 2001 From: Felipe Leme Date: Tue, 29 Mar 2016 10:45:13 -0700 Subject: Uses bw_happy_box and bw_penalty_box for Data Saver. bw_penalty_box is a blacklist-based firewall chain used to determine which UIDs do not have access to metered interfaces. It can be used alone or with bw_happy_box, which is a whitelist-based chain: when bw_happy_box is enabled, it's included in the bw_penalty_box chain. Currently, NMPS and NMS uses just bw_penalty_box for both blacklist and whitelist, so when Data Saver is turned on, it has to build a extensive list of UIDs to be blacklisted, which can take seconds (since it makes dozens of iptables, and which forks an iptables process) This CL changes this behavior so it uses both chain (plus a new bw_data_saver chain, which is added to the end of bw_happy_box), in which case the Data Saver switch is much faster (around 120-160ms), since it requires just 1 or 2 iptables calls (one to switch the bw_data_saver rule, and another to whitelist the foreground app if it's not whitelisted yet). BUG: 27127112 BUG: 26685616 Change-Id: If10222aef9f49a924b07b978d4bdccdd92f9acdb --- core/java/android/net/NetworkPolicyManager.java | 4 + .../server/net/NetworkPolicyManagerService.java | 245 ++++++++++++++++----- 2 files changed, 199 insertions(+), 50 deletions(-) diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 8738424e1a50..e464a4a31497 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -57,6 +57,10 @@ public class NetworkPolicyManager { public static final int RULE_REJECT_METERED = 1; /** Reject traffic on all networks. */ public static final int RULE_REJECT_ALL = 2; + /** Allow traffic on metered networks. */ + public static final int RULE_ALLOW_METERED = 3; + /** Temporarily allow traffic on metered networks because app is on foreground. */ + public static final int RULE_TEMPORARY_ALLOW_METERED = 4; public static final int FIREWALL_RULE_DEFAULT = 0; public static final int FIREWALL_RULE_ALLOW = 1; diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index c75e287949bc..6ae01a192e34 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -49,7 +49,9 @@ import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY; import static android.net.NetworkPolicyManager.POLICY_NONE; import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND; import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL; +import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED; import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; +import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED; import static android.net.NetworkPolicyManager.RULE_UNKNOWN; import static android.net.NetworkPolicyManager.computeLastCycleBoundary; import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER; @@ -1729,7 +1731,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (wlUids.length > 0) { for (int uid : wlUids) { - removeRestrictBackgroundWhitelistedUidLocked(uid, false); + removeRestrictBackgroundWhitelistedUidLocked(uid, false, false); } writePolicy = true; } @@ -1896,10 +1898,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { try { maybeRefreshTrustedTime(); synchronized (mRulesLock) { - mRestrictBackground = restrictBackground; - updateRulesForRestrictBackgroundLocked(); - updateNotificationsLocked(); - writePolicyLocked(); + if (restrictBackground == mRestrictBackground) { + // Ideally, UI should never allow this scenario... + Slog.w(TAG, "setRestrictBackground: already " + restrictBackground); + return; + } + setRestrictBackgroundLocked(restrictBackground); } } finally { @@ -1910,17 +1914,42 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { .sendToTarget(); } + private void setRestrictBackgroundLocked(boolean restrictBackground) { + final boolean oldRestrictBackground = mRestrictBackground; + mRestrictBackground = restrictBackground; + // Must whitelist foreground apps before turning data saver mode on. + // TODO: there is no need to iterate through all apps here, just those in the foreground, + // so it could call AM to get the UIDs of such apps, and iterate through them instead. + updateRulesForRestrictBackgroundLocked(); + try { + if (!mNetworkManager.setDataSaverModeEnabled(mRestrictBackground)) { + Slog.e(TAG, "Could not change Data Saver Mode on NMS to " + mRestrictBackground); + mRestrictBackground = oldRestrictBackground; + // TODO: if it knew the foreground apps (see TODO above), it could call + // updateRulesForRestrictBackgroundLocked() again to restore state. + return; + } + } catch (RemoteException e) { + // ignored; service lives in system_server + } + updateNotificationsLocked(); + writePolicyLocked(); + } + @Override public void addRestrictBackgroundWhitelistedUid(int uid) { mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); - if (!isUidValidForRules(uid)) return; - final boolean changed; + final boolean oldStatus; synchronized (mRulesLock) { - final boolean oldStatus = mRestrictBackgroundWhitelistUids.get(uid); + oldStatus = mRestrictBackgroundWhitelistUids.get(uid); if (oldStatus) { if (LOGD) Slog.d(TAG, "uid " + uid + " is already whitelisted"); return; } + if (!isUidValidForWhitelistRules(uid)) { + if (LOGD) Slog.d(TAG, "no need to whitelist uid " + uid); + return; + } Slog.i(TAG, "adding uid " + uid + " to restrict background whitelist"); mRestrictBackgroundWhitelistUids.append(uid, true); if (mDefaultRestrictBackgroundWhitelistUids.get(uid) @@ -1929,13 +1958,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { + " from revoked restrict background whitelist"); mRestrictBackgroundWhitelistRevokedUids.delete(uid); } - changed = mRestrictBackground && !oldStatus; - if (changed && hasInternetPermissions(uid)) { - setUidNetworkRules(uid, false); - } + updateRuleForRestrictBackgroundLocked(uid); writePolicyLocked(); } - if (changed) { + if (mRestrictBackground && !oldStatus) { mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, 0) .sendToTarget(); } @@ -1944,10 +1970,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @Override public void removeRestrictBackgroundWhitelistedUid(int uid) { mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); - if (!isUidValidForRules(uid)) return; final boolean changed; synchronized (mRulesLock) { - changed = removeRestrictBackgroundWhitelistedUidLocked(uid, true); + changed = removeRestrictBackgroundWhitelistedUidLocked(uid, false, true); } if (changed) { mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED, uid, 0) @@ -1955,14 +1980,22 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - private boolean removeRestrictBackgroundWhitelistedUidLocked(int uid, boolean updateNow) { + /** + * Removes a uid from the restricted background whitelist, returning whether its current + * {@link ConnectivityManager.RestrictBackgroundStatus} changed. + */ + private boolean removeRestrictBackgroundWhitelistedUidLocked(int uid, boolean uidDeleted, + boolean updateNow) { final boolean oldStatus = mRestrictBackgroundWhitelistUids.get(uid); if (!oldStatus) { if (LOGD) Slog.d(TAG, "uid " + uid + " was not whitelisted before"); return false; } + if (!uidDeleted && !isUidValidForWhitelistRules(uid)) { + if (LOGD) Slog.d(TAG, "no need to remove whitelist for uid " + uid); + return false; + } Slog.i(TAG, "removing uid " + uid + " from restrict background whitelist"); - final boolean changed = mRestrictBackground && oldStatus; mRestrictBackgroundWhitelistUids.delete(uid); if (mDefaultRestrictBackgroundWhitelistUids.get(uid) && !mRestrictBackgroundWhitelistRevokedUids.get(uid)) { @@ -1970,13 +2003,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { + " to revoked restrict background whitelist"); mRestrictBackgroundWhitelistRevokedUids.append(uid, true); } + updateRuleForRestrictBackgroundLocked(uid, uidDeleted); if (updateNow) { - if (changed && hasInternetPermissions(uid)) { - setUidNetworkRules(uid, true); - } writePolicyLocked(); } - return changed; + // Status only changes if Data Saver is turned on (otherwise it is DISABLED, even if the + // app was whitelisted before). + return mRestrictBackground; } @Override @@ -2312,7 +2345,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { /** * Process state of UID changed; if needed, will trigger - * {@link #updateRestrictDataRulesForUidLocked(int)}. + * {@link #updateRuleForRestrictBackgroundLocked(int)}. */ private void updateUidStateLocked(int uid, int uidState) { final int oldUidState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); @@ -2492,7 +2525,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } void updateRuleForAppIdleLocked(int uid) { - if (!isUidValidForRules(uid)) return; + if (!isUidValidForBlacklistRules(uid)) return; int appId = UserHandle.getAppId(uid); if (!mPowerSaveTempWhitelistAppIds.get(appId) && isUidIdle(uid)) { @@ -2519,6 +2552,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateRulesForAppIdleLocked(); updateRulesForRestrictPowerLocked(); updateRulesForRestrictBackgroundLocked(); + setRestrictBackgroundLocked(mRestrictBackground); // If the set of restricted networks may have changed, re-evaluate those. if (restrictedNetworksChanged) { @@ -2552,10 +2586,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateRuleForRestrictBackgroundLocked(uid); } } - - // limit data usage for some internal system services - updateRuleForRestrictBackgroundLocked(android.os.Process.MEDIA_UID); - updateRuleForRestrictBackgroundLocked(android.os.Process.DRM_UID); } private void updateRulesForTempWhitelistChangeLocked() { @@ -2572,16 +2602,22 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - private boolean isUidValidForRules(int uid) { - // allow rules on specific system services, and any apps (that have network access) + // TODO: the MEDIA / DRM restriction might not be needed anymore, in which case both + // methods below could be merged into a isUidValidForRules() method. + private boolean isUidValidForBlacklistRules(int uid) { + // allow rules on specific system services, and any apps if (uid == android.os.Process.MEDIA_UID || uid == android.os.Process.DRM_UID - || (UserHandle.isApp(uid) && hasInternetPermissions(uid))) { + || (UserHandle.isApp(uid) && hasInternetPermissions(uid))) { return true; } return false; } + private boolean isUidValidForWhitelistRules(int uid) { + return UserHandle.isApp(uid) && hasInternetPermissions(uid); + } + private boolean isUidIdle(int uid) { final String[] packages = mContext.getPackageManager().getPackagesForUid(uid); final int userId = UserHandle.getUserId(uid); @@ -2626,45 +2662,142 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateRuleForRestrictBackgroundLocked(uid); } + /** + * Applies network rules to bandwidth controllers based on process state and user-defined + * restrictions (blacklist / whitelist). + * + *

+ * {@code netd} defines 3 firewall chains that govern whether an app has access to metered + * networks: + *