From 781ba14af2a8c8e85f684208055dba0ebfd5db30 Mon Sep 17 00:00:00 2001 From: Felipe Leme Date: Mon, 9 May 2016 16:24:48 -0700 Subject: [PATCH] Fixed connectivity state in some power saving scenarios. NetworkPolicyManagerService (NPMS) manages 4 type of network restriction when apps are running on background: - Data Saver Mode (data usage restriction on metered-networks) - Battery Saver Mode (power restriction on all networks) - Doze Mode (power restriction on all networks) - App Idle (power restriction on all networks) These restrictions affects 2 parts of the system: - Internal framework state on NPMS which is propagated to other internal classes. - External firewall rules (managed by netd). Although each of the power-related restrictions have their own external firewall rules, internally apps are whitelisted to them through the same whitelist, and the current code is only updating the internal state (and notifying the internal listeners) when Battery Saver Mode is on. As a consequence of this problem, there are scenarios where an app correctly does not have internet access (because the firewall rules are properly set), but the NetworkInfo state returns the wrong state (like CONNECTED / CONNECTED). This CL fixes this problem by splitting the power-related logic from updateRulesForRestrictBackgroundLocked() into its own method (updateRulesForPowerRestrictionsLocked()), and making sure such method is called whenever the firewall rules are updated. Externally to this change, the CTS tests were also improved to verify the apps get the proper connection state; it can be verified by running: cts-tradefed run commandAndExit cts -m CtsHostsideNetworkTests \ -t com.android.cts.net.HostsideRestrictBackgroundNetworkTests BUG: 28521946 Change-Id: Id5187eb7a59c549ef30e2b17627ae2d734afa789 --- .../android/net/INetworkPolicyListener.aidl | 1 - .../android/net/INetworkPolicyManager.aidl | 1 - .../statusbar/policy/DataSaverController.java | 4 - .../android/server/ConnectivityService.java | 37 +-- .../controllers/ConnectivityController.java | 5 - .../net/NetworkPolicyManagerService.java | 282 ++++++++++-------- 6 files changed, 174 insertions(+), 156 deletions(-) diff --git a/core/java/android/net/INetworkPolicyListener.aidl b/core/java/android/net/INetworkPolicyListener.aidl index 6523192b735a8..812f1fe5e563b 100644 --- a/core/java/android/net/INetworkPolicyListener.aidl +++ b/core/java/android/net/INetworkPolicyListener.aidl @@ -23,6 +23,5 @@ oneway interface INetworkPolicyListener { void onMeteredIfacesChanged(in String[] meteredIfaces); void onRestrictBackgroundChanged(boolean restrictBackground); void onRestrictBackgroundWhitelistChanged(int uid, boolean whitelisted); - void onRestrictPowerChanged(boolean restrictPower); } diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl index fc9b8dd5f5bb1..224ff5b395c78 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -54,7 +54,6 @@ interface INetworkPolicyManager { /** Control if background data is restricted system-wide. */ void setRestrictBackground(boolean restrictBackground); boolean getRestrictBackground(); - boolean getRestrictPower(); /** Callback used to change internal state on tethering */ void onTetheringChanged(String iface, boolean tethering); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java index 0bca241c88423..e6e189f0ffd8e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DataSaverController.java @@ -90,10 +90,6 @@ public class DataSaverController { }); } - @Override - public void onRestrictPowerChanged(boolean restrictPower) { - } - @Override public void onRestrictBackgroundWhitelistChanged(int uid, boolean whitelisted) { } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 8763b93f31b9f..53b29427b29c4 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -33,6 +33,7 @@ import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED; import static android.net.NetworkPolicyManager.MASK_METERED_NETWORKS; import static android.net.NetworkPolicyManager.MASK_ALL_NETWORKS; import static android.net.NetworkPolicyManager.RULE_NONE; +import static android.net.NetworkPolicyManager.RULE_REJECT_ALL; import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED; import static android.net.NetworkPolicyManager.uidRulesToString; @@ -218,9 +219,6 @@ public class ConnectivityService extends IConnectivityManager.Stub /** Flag indicating if background data is restricted. */ @GuardedBy("mRulesLock") private boolean mRestrictBackground; - /** Flag indicating if background data is restricted due to battery savings. */ - @GuardedBy("mRulesLock") - private boolean mRestrictPower; final private Context mContext; private int mNetworkPreference; @@ -669,7 +667,6 @@ public class ConnectivityService extends IConnectivityManager.Stub try { mPolicyManager.setConnectivityListener(mPolicyListener); mRestrictBackground = mPolicyManager.getRestrictBackground(); - mRestrictPower = mPolicyManager.getRestrictPower(); } catch (RemoteException e) { // ouch, no rules updates means some processes may never get network loge("unable to register INetworkPolicyListener" + e); @@ -942,13 +939,11 @@ public class ConnectivityService extends IConnectivityManager.Stub + ": " + allowed); } } - // ...then Battery Saver Mode. - if (allowed && mRestrictPower) { - allowed = (uidRules & RULE_ALLOW_ALL) != 0; + // ...then power restrictions. + if (allowed) { + allowed = (uidRules & RULE_REJECT_ALL) == 0; if (LOGD_RULES) Log.d(TAG, "allowed status for uid " + uid + " when" - + " mRestrictPower=" + mRestrictPower - + ", whitelisted=" + ((uidRules & RULE_ALLOW_ALL) != 0) - + ": " + allowed); + + " rule is " + uidRulesToString(uidRules) + ": " + allowed); } return !allowed; } @@ -1400,7 +1395,11 @@ public class ConnectivityService extends IConnectivityManager.Stub final int oldRules = mUidRules.get(uid, RULE_NONE); if (oldRules == uidRules) return; - mUidRules.put(uid, uidRules); + if (uidRules == RULE_NONE) { + mUidRules.delete(uid); + } else { + mUidRules.put(uid, uidRules); + } } // TODO: notify UID when it has requested targeted updates @@ -1438,18 +1437,6 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - @Override - public void onRestrictPowerChanged(boolean restrictPower) { - // caller is NPMS, since we only register with them - if (LOGD_RULES) { - log("onRestrictPowerChanged(restrictPower=" + restrictPower + ")"); - } - - synchronized (mRulesLock) { - mRestrictPower = restrictPower; - } - } - @Override public void onRestrictBackgroundWhitelistChanged(int uid, boolean whitelisted) { if (LOGD_RULES) { @@ -1891,10 +1878,6 @@ public class ConnectivityService extends IConnectivityManager.Stub pw.println(mRestrictBackground); pw.println(); - pw.print("Restrict power: "); - pw.println(mRestrictPower); - pw.println(); - pw.println("Status for known UIDs:"); pw.increaseIndent(); final int size = mUidRules.size(); diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java index e0179dcc95864..9fd22686a9a05 100644 --- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java +++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java @@ -169,11 +169,6 @@ public class ConnectivityController extends StateController implements updateTrackedJobs(-1); } - @Override - public void onRestrictPowerChanged(boolean restrictPower) { - updateTrackedJobs(-1); - } - @Override public void onRestrictBackgroundChanged(boolean restrictBackground) { updateTrackedJobs(-1); diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 344ba179e4c76..4bdc23730fbef 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -266,7 +266,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final int MSG_RESTRICT_BACKGROUND_WHITELIST_CHANGED = 9; private static final int MSG_UPDATE_INTERFACE_QUOTA = 10; private static final int MSG_REMOVE_INTERFACE_QUOTA = 11; - private static final int MSG_RESTRICT_POWER_CHANGED = 12; private final Context mContext; private final IActivityManager mActivityManager; @@ -557,18 +556,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateRulesForGlobalChangeLocked(true); } } - mHandler.obtainMessage(MSG_RESTRICT_POWER_CHANGED, - enabled ? 1 : 0, 0).sendToTarget(); } }); - final boolean oldRestrictPower = mRestrictPower; mRestrictPower = mPowerManagerInternal.getLowPowerModeEnabled(); - if (mRestrictPower != oldRestrictPower) { - // Some early services may have read the default value, - // so notify them that it's changed - mHandler.obtainMessage(MSG_RESTRICT_POWER_CHANGED, - mRestrictPower ? 1 : 0, 0).sendToTarget(); - } mSystemReady = true; @@ -740,7 +730,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (LOGV) Slog.v(TAG, "ACTION_UID_REMOVED for uid=" + uid); synchronized (mRulesLock) { mUidPolicy.delete(uid); - updateRuleForRestrictBackgroundLocked(uid); + updateRestrictionRulesForUidLocked(uid); writePolicyLocked(); } } @@ -1729,7 +1719,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mUidPolicy.put(uid, policy); // uid policy changed, recompute rules and persist policy. - updateRuleForRestrictBackgroundLocked(uid); + updateRulesForDataUsageRestrictionsLocked(uid); if (persist) { writePolicyLocked(); } @@ -2036,7 +2026,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } if (needFirewallRules) { // Only update firewall rules if necessary... - updateRuleForRestrictBackgroundLocked(uid); + updateRulesForDataUsageRestrictionsLocked(uid); } // ...but always persists the whitelist request. writePolicyLocked(); @@ -2081,7 +2071,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } if (needFirewallRules) { // Only update firewall rules if necessary... - updateRuleForRestrictBackgroundLocked(uid, uidDeleted); + updateRulesForDataUsageRestrictionsLocked(uid, uidDeleted); } if (updateNow) { // ...but always persists the whitelist request. @@ -2145,15 +2135,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - @Override - public boolean getRestrictPower() { - mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); - - synchronized (mRulesLock) { - return mRestrictPower; - } - } - @Override public void setDeviceIdleMode(boolean enabled) { mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); @@ -2446,6 +2427,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return isProcStateAllowedWhileOnRestrictBackgroundLocked(procState); } + private boolean isUidForegroundOnRestrictPowerLocked(int uid) { + final int procState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); + return isProcStateAllowedWhileIdleOrPowerSaveMode(procState); + } + private boolean isUidStateForegroundLocked(int state) { // only really in foreground when screen is also on return mScreenOn && state <= ActivityManager.PROCESS_STATE_TOP; @@ -2453,7 +2439,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { /** * Process state of UID changed; if needed, will trigger - * {@link #updateRuleForRestrictBackgroundLocked(int)}. + * {@link #updateRulesForDataUsageRestrictionsLocked(int)} and + * {@link #updateRulesForPowerRestrictionsLocked(int)} */ private void updateUidStateLocked(int uid, int uidState) { final int oldUidState = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); @@ -2463,12 +2450,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateRestrictBackgroundRulesOnUidStatusChangedLocked(uid, oldUidState, uidState); if (isProcStateAllowedWhileIdleOrPowerSaveMode(oldUidState) != isProcStateAllowedWhileIdleOrPowerSaveMode(uidState) ) { + if (isUidIdle(uid)) { + updateRuleForAppIdleLocked(uid); + } if (mDeviceIdleMode) { updateRuleForDeviceIdleLocked(uid); } if (mRestrictPower) { updateRuleForRestrictPowerLocked(uid); } + updateRulesForPowerRestrictionsLocked(uid); } updateNetworkStats(uid, isUidStateForegroundLocked(uidState)); } @@ -2488,6 +2479,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (mRestrictPower) { updateRuleForRestrictPowerLocked(uid); } + updateRulesForPowerRestrictionsLocked(uid); updateNetworkStats(uid, false); } } @@ -2509,7 +2501,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final boolean newForeground = isProcStateAllowedWhileOnRestrictBackgroundLocked(newUidState); if (oldForeground != newForeground) { - updateRuleForRestrictBackgroundLocked(uid); + updateRulesForDataUsageRestrictionsLocked(uid); } } @@ -2600,6 +2592,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { enableFirewallChainLocked(chain, enabled); } + private void updateRulesForNonMeteredNetworksLocked() { + + } + private boolean isWhitelistedBatterySaverLocked(int uid) { final int appId = UserHandle.getAppId(uid); return mPowerSaveTempWhitelistAppIds.get(appId) || mPowerSaveWhitelistAppIds.get(appId); @@ -2646,7 +2642,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (!isUidValidForBlacklistRules(uid)) return; int appId = UserHandle.getAppId(uid); - if (!mPowerSaveTempWhitelistAppIds.get(appId) && isUidIdle(uid)) { + if (!mPowerSaveTempWhitelistAppIds.get(appId) && isUidIdle(uid) + && !isUidForegroundOnRestrictPowerLocked(uid)) { setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DENY); } else { setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT); @@ -2701,7 +2698,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { for (int j = 0; j < appsSize; j++) { final ApplicationInfo app = apps.get(j); final int uid = UserHandle.getUid(user.id, app.uid); - updateRuleForRestrictBackgroundLocked(uid); + updateRulesForDataUsageRestrictionsLocked(uid); + updateRulesForPowerRestrictionsLocked(uid); } } } @@ -2713,9 +2711,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { for (int j = mPowerSaveTempWhitelistAppIds.size() - 1; j >= 0; j--) { int appId = mPowerSaveTempWhitelistAppIds.keyAt(j); int uid = UserHandle.getUid(user.id, appId); + // Update external firewall rules. updateRuleForAppIdleLocked(uid); updateRuleForDeviceIdleLocked(uid); updateRuleForRestrictPowerLocked(uid); + // Update internal rules. + updateRulesForPowerRestrictionsLocked(uid); } } } @@ -2769,15 +2770,27 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { /** * Applies network rules to bandwidth and firewall controllers based on uid policy. * - *

There are currently 2 types of restriction rules: + *

There are currently 4 types of restriction rules: *

+ * + *

This method changes both the external firewall rules and the internal state. */ private void updateRestrictionRulesForUidLocked(int uid) { + // Methods below only changes the firewall rules for the power-related modes. + updateRuleForDeviceIdleLocked(uid); + updateRuleForAppIdleLocked(uid); updateRuleForRestrictPowerLocked(uid); - updateRuleForRestrictBackgroundLocked(uid); + + // Update internal state for power-related modes. + updateRulesForPowerRestrictionsLocked(uid); + + // Update firewall and internal rules for Data Saver Mode. + updateRulesForDataUsageRestrictionsLocked(uid); } /** @@ -2799,7 +2812,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * {@link #setUidPolicy(int, int)} and {@link #addRestrictBackgroundWhitelistedUid(int)} / * {@link #removeRestrictBackgroundWhitelistedUid(int)} methods (for blacklist and whitelist * respectively): these methods set the proper internal state (blacklist / whitelist), then call - * this ({@link #updateRuleForRestrictBackgroundLocked(int)}) to propagate the rules to + * this ({@link #updateRulesForDataUsageRestrictionsLocked(int)}) to propagate the rules to * {@link INetworkManagementService}, but this method should also be called in events (like * Data Saver Mode flips or UID state changes) that might affect the foreground app, since the * following rules should also be applied: @@ -2818,19 +2831,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * *

The {@link #mUidRules} map is used to define the transtion of states of an UID. * - *

This method also updates the {@link #mUidRules} with the power-related status for the uid - * and send the proper {@value #MSG_RULES_CHANGED} notification, although it does not change - * the power-related firewall rules per se. */ - private void updateRuleForRestrictBackgroundLocked(int uid) { - updateRuleForRestrictBackgroundLocked(uid, false); + private void updateRulesForDataUsageRestrictionsLocked(int uid) { + updateRulesForDataUsageRestrictionsLocked(uid, false); } /** - * Overloaded version of {@link #updateRuleForRestrictBackgroundLocked(int)} called when an + * Overloaded version of {@link #updateRulesForDataUsageRestrictionsLocked(int)} called when an * app is removed - it ignores the UID validity check. */ - private void updateRuleForRestrictBackgroundLocked(int uid, boolean uidDeleted) { + private void updateRulesForDataUsageRestrictionsLocked(int uid, boolean uidDeleted) { if (!uidDeleted && !isUidValidForWhitelistRules(uid)) { if (LOGD) Slog.d(TAG, "no need to update restrict data rules for uid " + uid); return; @@ -2840,50 +2850,36 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final int oldUidRules = mUidRules.get(uid, RULE_NONE); final boolean isForeground = isUidForegroundOnRestrictBackgroundLocked(uid); - // Data Saver status. - final boolean isDsBlacklisted = (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0; - final boolean isDsWhitelisted = mRestrictBackgroundWhitelistUids.get(uid); - final int oldDsRule = oldUidRules & MASK_METERED_NETWORKS; - int newDsRule = RULE_NONE; - - // Battery Saver status. - final boolean isBsWhitelisted = isWhitelistedBatterySaverLocked(uid); - final int oldBsRule = oldUidRules & MASK_ALL_NETWORKS; - int newBsRule = RULE_NONE; + final boolean isBlacklisted = (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0; + final boolean isWhitelisted = mRestrictBackgroundWhitelistUids.get(uid); + final int oldRule = oldUidRules & MASK_METERED_NETWORKS; + int newRule = RULE_NONE; // First step: define the new rule based on user restrictions and foreground state. if (isForeground) { - // Data Saver rules - if (isDsBlacklisted || (mRestrictBackground && !isDsWhitelisted)) { - newDsRule = RULE_TEMPORARY_ALLOW_METERED; - } else if (isDsWhitelisted) { - newDsRule = RULE_ALLOW_METERED; - } - // Battery Saver rules - if (mRestrictPower) { - newBsRule = RULE_ALLOW_ALL; + if (isBlacklisted || (mRestrictBackground && !isWhitelisted)) { + newRule = RULE_TEMPORARY_ALLOW_METERED; + } else if (isWhitelisted) { + newRule = RULE_ALLOW_METERED; } } else { - // Data Saver rules - if (isDsBlacklisted) { - newDsRule = RULE_REJECT_METERED; - } else if (mRestrictBackground && isDsWhitelisted) { - newDsRule = RULE_ALLOW_METERED; - } - // Battery Saver rules - if (mRestrictPower) { - newBsRule = isBsWhitelisted ? RULE_ALLOW_ALL : RULE_REJECT_ALL; + if (isBlacklisted) { + newRule = RULE_REJECT_METERED; + } else if (mRestrictBackground && isWhitelisted) { + newRule = RULE_ALLOW_METERED; } } - final int newUidRules = newDsRule | newBsRule; + final int newUidRules = newRule | (oldUidRules & MASK_ALL_NETWORKS); if (LOGV) { - Log.v(TAG, "updateRuleForRestrictBackgroundLocked(" + uid + "):" - + " isForeground=" +isForeground + ", isBlacklisted: " + isDsBlacklisted - + ", isDsWhitelisted: " + isDsWhitelisted - + ", isBsWhitelisted: " + isBsWhitelisted - + ", newUidRules: " + uidRulesToString(newUidRules) - + ", oldUidRules: " + uidRulesToString(oldUidRules)); + Log.v(TAG, "updateRuleForRestrictBackgroundLocked(" + uid + ")" + + ": isForeground=" +isForeground + + ", isBlacklisted=" + isBlacklisted + + ", isWhitelisted=" + isWhitelisted + + ", oldRule=" + uidRulesToString(oldRule) + + ", newRule=" + uidRulesToString(newRule) + + ", newUidRules=" + uidRulesToString(newUidRules) + + ", oldUidRules=" + uidRulesToString(oldUidRules)); } if (newUidRules == RULE_NONE) { @@ -2895,12 +2891,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { boolean changed = false; // Second step: apply bw changes based on change of state. - - // Apply Data Saver rules. - if (newDsRule != oldDsRule) { + if (newRule != oldRule) { changed = true; - if ((newDsRule & RULE_TEMPORARY_ALLOW_METERED) != 0) { + if ((newRule & RULE_TEMPORARY_ALLOW_METERED) != 0) { // Temporarily whitelist foreground app, removing from blacklist if necessary // (since bw_penalty_box prevails over bw_happy_box). @@ -2908,68 +2902,132 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // TODO: if statement below is used to avoid an unnecessary call to netd / iptables, // but ideally it should be just: // setMeteredNetworkBlacklist(uid, isBlacklisted); - if (isDsBlacklisted) { + if (isBlacklisted) { setMeteredNetworkBlacklist(uid, false); } - } else if ((oldDsRule & RULE_TEMPORARY_ALLOW_METERED) != 0) { + } else if ((oldRule & RULE_TEMPORARY_ALLOW_METERED) != 0) { // Remove temporary whitelist from app that is not on foreground anymore. // TODO: if statements below are used to avoid unnecessary calls to netd / iptables, // but ideally they should be just: // setMeteredNetworkWhitelist(uid, isWhitelisted); // setMeteredNetworkBlacklist(uid, isBlacklisted); - if (!isDsWhitelisted) { + if (!isWhitelisted) { setMeteredNetworkWhitelist(uid, false); } - if (isDsBlacklisted) { + if (isBlacklisted) { setMeteredNetworkBlacklist(uid, true); } - } else if ((newDsRule & RULE_REJECT_METERED) != 0 - || (oldDsRule & RULE_REJECT_METERED) != 0) { + } else if ((newRule & RULE_REJECT_METERED) != 0 + || (oldRule & RULE_REJECT_METERED) != 0) { // Flip state because app was explicitly added or removed to blacklist. - setMeteredNetworkBlacklist(uid, isDsBlacklisted); - if ((oldDsRule & RULE_REJECT_METERED) != 0 && isDsWhitelisted) { + setMeteredNetworkBlacklist(uid, isBlacklisted); + if ((oldRule & RULE_REJECT_METERED) != 0 && isWhitelisted) { // Since blacklist prevails over whitelist, we need to handle the special case // where app is whitelisted and blacklisted at the same time (although such // scenario should be blocked by the UI), then blacklist is removed. - setMeteredNetworkWhitelist(uid, isDsWhitelisted); + setMeteredNetworkWhitelist(uid, isWhitelisted); } - } else if ((newDsRule & RULE_ALLOW_METERED) != 0 - || (oldDsRule & RULE_ALLOW_METERED) != 0) { + } else if ((newRule & RULE_ALLOW_METERED) != 0 + || (oldRule & RULE_ALLOW_METERED) != 0) { // Flip state because app was explicitly added or removed to whitelist. - setMeteredNetworkWhitelist(uid, isDsWhitelisted); + setMeteredNetworkWhitelist(uid, isWhitelisted); } else { - // All scenarios should have been covered above + // All scenarios should have been covered above. Log.wtf(TAG, "Unexpected change of metered UID state for " + uid + ": foreground=" + isForeground - + ", whitelisted=" + isDsWhitelisted - + ", blacklisted=" + isDsBlacklisted + + ", whitelisted=" + isWhitelisted + + ", blacklisted=" + isBlacklisted + ", newRule=" + uidRulesToString(newUidRules) + ", oldRule=" + uidRulesToString(oldUidRules)); } + + // Dispatch changed rule to existing listeners. + mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget(); + } + } + + /** + * Updates the power-related part of the {@link #mUidRules} for a given map, and notify external + * listeners in case of change. + *

+ * There are 3 power-related rules that affects whether an app has background access on + * non-metered networks, and when the condition applies and the UID is not whitelisted for power + * restriction, it's added to the equivalent firewall chain: + *

+ *

+ * This method updates the power-related part of the {@link #mUidRules} for a given uid based on + * these modes, the UID process state (foreground or not), and the UIDwhitelist state. + *

+ * NOTE: This method does not update the firewall rules on {@code netd}. + */ + private void updateRulesForPowerRestrictionsLocked(int uid) { + if (!isUidValidForBlacklistRules(uid)) { + if (LOGD) Slog.d(TAG, "no need to update restrict power rules for uid " + uid); + return; } - // Apply Battery Saver rules. - // NOTE: the firewall rules are changed outside this method, but it's still necessary to - // send the MSG_RULES_CHANGED so ConnectivityService can update its internal status. - if (newBsRule != oldBsRule) { - changed = true; - if (newBsRule == RULE_NONE || (newBsRule & RULE_ALLOW_ALL) != 0) { + final boolean isIdle = isUidIdle(uid); + final boolean restrictMode = isIdle || mRestrictPower || mDeviceIdleMode; + final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE); + final int oldUidRules = mUidRules.get(uid, RULE_NONE); + final boolean isForeground = isUidForegroundOnRestrictPowerLocked(uid); + + final boolean isWhitelisted = isWhitelistedBatterySaverLocked(uid); + final int oldRule = oldUidRules & MASK_ALL_NETWORKS; + int newRule = RULE_NONE; + + // First step: define the new rule based on user restrictions and foreground state. + + // NOTE: if statements below could be inlined, but it's easier to understand the logic + // by considering the foreground and non-foreground states. + if (isForeground) { + if (restrictMode) { + newRule = RULE_ALLOW_ALL; + } + } else if (restrictMode) { + newRule = isWhitelisted ? RULE_ALLOW_ALL : RULE_REJECT_ALL; + } + + final int newUidRules = (oldUidRules & MASK_METERED_NETWORKS) | newRule; + + if (LOGV) { + Log.v(TAG, "updateRulesForNonMeteredNetworksLocked(" + uid + ")" + + ", isIdle: " + isIdle + + ", mRestrictPower: " + mRestrictPower + + ", mDeviceIdleMode: " + mDeviceIdleMode + + ", isForeground=" + isForeground + + ", isWhitelisted=" + isWhitelisted + + ", oldRule=" + uidRulesToString(oldRule) + + ", newRule=" + uidRulesToString(newRule) + + ", newUidRules=" + uidRulesToString(newUidRules) + + ", oldUidRules=" + uidRulesToString(oldUidRules)); + } + + if (newUidRules == RULE_NONE) { + mUidRules.delete(uid); + } else { + mUidRules.put(uid, newUidRules); + } + + // Second step: notify listeners if state changed. + if (newRule != oldRule) { + if (newRule == RULE_NONE || (newRule & RULE_ALLOW_ALL) != 0) { if (LOGV) Log.v(TAG, "Allowing non-metered access for UID " + uid); - } else if ((newBsRule & RULE_REJECT_ALL) != 0) { + } else if ((newRule & RULE_REJECT_ALL) != 0) { if (LOGV) Log.v(TAG, "Rejecting non-metered access for UID " + uid); } else { // All scenarios should have been covered above Log.wtf(TAG, "Unexpected change of non-metered UID state for " + uid + ": foreground=" + isForeground - + ", whitelisted=" + isBsWhitelisted + + ", whitelisted=" + isWhitelisted + ", newRule=" + uidRulesToString(newUidRules) + ", oldRule=" + uidRulesToString(oldUidRules)); } - } - - // Final step: dispatch changed rule to existing listeners - if (changed) { mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget(); } } @@ -2982,8 +3040,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { try { final int uid = mContext.getPackageManager().getPackageUidAsUser(packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId); + if (LOGV) Log.v(TAG, "onAppIdleStateChanged(): uid=" + uid + ", idle=" + idle); synchronized (mRulesLock) { updateRuleForAppIdleLocked(uid); + updateRulesForPowerRestrictionsLocked(uid); } } catch (NameNotFoundException nnfe) { } @@ -3036,16 +3096,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - private void dispatchRestrictPowerChanged(INetworkPolicyListener listener, - boolean restrictPower) { - if (listener != null) { - try { - listener.onRestrictPowerChanged(restrictPower); - } catch (RemoteException ignored) { - } - } - } - private Handler.Callback mHandlerCallback = new Handler.Callback() { @Override public boolean handleMessage(Message msg) { @@ -3093,11 +3143,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } return true; } - case MSG_RESTRICT_POWER_CHANGED: { - final boolean restrictPower = msg.arg1 != 0; - dispatchRestrictPowerChanged(mConnectivityListener, restrictPower); - return true; - } case MSG_RESTRICT_BACKGROUND_CHANGED: { final boolean restrictBackground = msg.arg1 != 0; dispatchRestrictBackgroundChanged(mConnectivityListener, restrictBackground); @@ -3410,6 +3455,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (LOGV) Slog.v(TAG, "onPackageRemoved: " + packageName + " ->" + uid); synchronized (mRulesLock) { removeRestrictBackgroundWhitelistedUidLocked(uid, true, true); + updateRestrictionRulesForUidLocked(uid); } } }