From 6a0976aa75dcd45df577bc5b78e98c2a51be11bd Mon Sep 17 00:00:00 2001 From: Sudheer Shanka Date: Wed, 15 Sep 2021 16:30:58 -0700 Subject: [PATCH 1/2] Include blocked reasons in the netpolicy dump. Bug: 202065451 Test: adb shell dumpsys netpolicy Change-Id: If07045c16d97935378f39c9c171ac909dfe1a0b2 Merged-In: If07045c16d97935378f39c9c171ac909dfe1a0b2 --- .../net/NetworkPolicyManagerService.java | 134 +++++++++++++++++- 1 file changed, 133 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 20687c6764dbe..23adc711925c6 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -3826,6 +3826,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final SparseBooleanArray knownUids = new SparseBooleanArray(); collectKeys(mUidState, knownUids); collectKeys(mUidRules, knownUids); + collectKeys(mUidBlockedState, knownUids); fout.println("Status for all known UIDs:"); fout.increaseIndent(); @@ -3846,6 +3847,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final int uidRules = mUidRules.get(uid, RULE_NONE); fout.print(" rules="); fout.print(uidRulesToString(uidRules)); + + final UidBlockedState uidBlockedState = mUidBlockedState.get(uid); + if (uidBlockedState == null) { + fout.print(" blocked_state={null}"); + } else { + fout.print(" blocked_state="); + fout.print(uidBlockedState.toString()); + } fout.println(); } fout.decreaseIndent(); @@ -4555,6 +4564,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private void onUidDeletedUL(int uid) { // First cleanup in-memory state synchronously... mUidRules.delete(uid); + mUidBlockedState.delete(uid); mUidPolicy.delete(uid); mUidFirewallStandbyRules.delete(uid); mUidFirewallDozableRules.delete(uid); @@ -5605,7 +5615,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - private static void collectKeys(SparseArray source, SparseBooleanArray target) { + private static void collectKeys(SparseArray source, SparseBooleanArray target) { final int size = source.size(); for (int i = 0; i < size; i++) { target.put(source.keyAt(i), true); @@ -6008,6 +6018,128 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } return effectiveBlockedReasons; } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("{"); + sb.append("blocked=").append(blockedReasonsToString(blockedReasons)).append(","); + sb.append("allowed=").append(allowedReasonsToString(allowedReasons)).append(","); + sb.append("effective=").append(blockedReasonsToString(effectiveBlockedReasons)); + sb.append("}"); + return sb.toString(); + } + + private static final int[] BLOCKED_REASONS = { + BLOCKED_REASON_BATTERY_SAVER, + BLOCKED_REASON_DOZE, + BLOCKED_REASON_APP_STANDBY, + BLOCKED_REASON_RESTRICTED_MODE, + BLOCKED_METERED_REASON_DATA_SAVER, + BLOCKED_METERED_REASON_USER_RESTRICTED, + BLOCKED_METERED_REASON_ADMIN_DISABLED, + }; + + private static final int[] ALLOWED_REASONS = { + ALLOWED_REASON_SYSTEM, + ALLOWED_REASON_FOREGROUND, + ALLOWED_REASON_POWER_SAVE_ALLOWLIST, + ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST, + ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS, + ALLOWED_METERED_REASON_USER_EXEMPTED, + ALLOWED_METERED_REASON_SYSTEM, + ALLOWED_METERED_REASON_FOREGROUND, + }; + + private static String blockedReasonToString(int blockedReason) { + switch (blockedReason) { + case BLOCKED_REASON_NONE: + return "NONE"; + case BLOCKED_REASON_BATTERY_SAVER: + return "BATTERY_SAVER"; + case BLOCKED_REASON_DOZE: + return "DOZE"; + case BLOCKED_REASON_APP_STANDBY: + return "APP_STANDBY"; + case BLOCKED_REASON_RESTRICTED_MODE: + return "RESTRICTED_MODE"; + case BLOCKED_METERED_REASON_DATA_SAVER: + return "DATA_SAVER"; + case BLOCKED_METERED_REASON_USER_RESTRICTED: + return "METERED_USER_RESTRICTED"; + case BLOCKED_METERED_REASON_ADMIN_DISABLED: + return "METERED_ADMIN_DISABLED"; + default: + Slog.wtfStack(TAG, "Unknown blockedReason: " + blockedReason); + return String.valueOf(blockedReason); + } + } + + private static String allowedReasonToString(int allowedReason) { + switch (allowedReason) { + case ALLOWED_REASON_NONE: + return "NONE"; + case ALLOWED_REASON_SYSTEM: + return "SYSTEM"; + case ALLOWED_REASON_FOREGROUND: + return "FOREGROUND"; + case ALLOWED_REASON_POWER_SAVE_ALLOWLIST: + return "POWER_SAVE_ALLOWLIST"; + case ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST: + return "POWER_SAVE_EXCEPT_IDLE_ALLOWLIST"; + case ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS: + return "RESTRICTED_MODE_PERMISSIONS"; + case ALLOWED_METERED_REASON_USER_EXEMPTED: + return "METERED_USER_EXEMPTED"; + case ALLOWED_METERED_REASON_SYSTEM: + return "METERED_SYSTEM"; + case ALLOWED_METERED_REASON_FOREGROUND: + return "METERED_FOREGROUND"; + default: + Slog.wtfStack(TAG, "Unknown allowedReason: " + allowedReason); + return String.valueOf(allowedReason); + } + } + + private static String blockedReasonsToString(int blockedReasons) { + if (blockedReasons == BLOCKED_REASON_NONE) { + return blockedReasonToString(BLOCKED_REASON_NONE); + } + final StringBuilder sb = new StringBuilder(); + for (int reason : BLOCKED_REASONS) { + if ((blockedReasons & reason) != 0) { + sb.append(sb.length() == 0 ? "" : "|"); + sb.append(blockedReasonToString(reason)); + blockedReasons &= ~reason; + } + } + if (blockedReasons != 0) { + sb.append(sb.length() == 0 ? "" : "|"); + sb.append(String.valueOf(blockedReasons)); + Slog.wtfStack(TAG, "Unknown blockedReasons: " + blockedReasons); + } + return sb.toString(); + } + + private static String allowedReasonsToString(int allowedReasons) { + if (allowedReasons == ALLOWED_REASON_NONE) { + return allowedReasonToString(ALLOWED_REASON_NONE); + } + final StringBuilder sb = new StringBuilder(); + for (int reason : ALLOWED_REASONS) { + if ((allowedReasons & reason) != 0) { + sb.append(sb.length() == 0 ? "" : "|"); + sb.append(allowedReasonToString(reason)); + allowedReasons &= ~reason; + } + } + if (allowedReasons != 0) { + sb.append(sb.length() == 0 ? "" : "|"); + sb.append(String.valueOf(allowedReasons)); + Slog.wtfStack(TAG, "Unknown allowedReasons: " + allowedReasons); + } + return sb.toString(); + } } private class NotificationId { From 2d17572ecad89b9c634f79a36ba75a28f45169c5 Mon Sep 17 00:00:00 2001 From: Sudheer Shanka Date: Wed, 22 Sep 2021 13:13:48 -0700 Subject: [PATCH 2/2] Remove uid rules computation in NPMS. Instead of computing uid rules separately by potentially calling into other system services, use blocked reasons to derive the uid rules for informing the listeners. Once we migrate the clients to use NetworkPolicyCallback, we can remove uid rules entirely. Bug: 202065802 Test: atest tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java Test: atest services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java Change-Id: Ifc3708c16ae7cf4d7ff26eac0ba1c6fbeb7ed7d9 Merged-In: Ifc3708c16ae7cf4d7ff26eac0ba1c6fbeb7ed7d9 --- .../server/net/NetworkPolicyLogger.java | 72 +-- .../net/NetworkPolicyManagerService.java | 556 +++++++----------- 2 files changed, 231 insertions(+), 397 deletions(-) diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java index 654b17fb97547..b45d87fbed5f8 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java +++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java @@ -39,6 +39,7 @@ import android.util.Slog; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.RingBuffer; import com.android.server.am.ProcessList; +import com.android.server.net.NetworkPolicyManagerService.UidBlockedState; import java.text.SimpleDateFormat; import java.util.Arrays; @@ -72,16 +73,6 @@ public class NetworkPolicyLogger { private static final int EVENT_UPDATE_METERED_RESTRICTED_PKGS = 13; private static final int EVENT_APP_IDLE_WL_CHANGED = 14; - static final int NTWK_BLOCKED_POWER = 0; - static final int NTWK_ALLOWED_NON_METERED = 1; - static final int NTWK_BLOCKED_DENYLIST = 2; - static final int NTWK_ALLOWED_ALLOWLIST = 3; - static final int NTWK_ALLOWED_TMP_ALLOWLIST = 4; - static final int NTWK_BLOCKED_BG_RESTRICT = 5; - static final int NTWK_ALLOWED_DEFAULT = 6; - static final int NTWK_ALLOWED_SYSTEM = 7; - static final int NTWK_BLOCKED_RESTRICTED_MODE = 8; - private final LogBuffer mNetworkBlockedBuffer = new LogBuffer(MAX_NETWORK_BLOCKED_LOG_SIZE); private final LogBuffer mUidStateChangeBuffer = new LogBuffer(MAX_LOG_SIZE); private final LogBuffer mEventsBuffer = new LogBuffer(MAX_LOG_SIZE); @@ -90,12 +81,13 @@ public class NetworkPolicyLogger { private final Object mLock = new Object(); - void networkBlocked(int uid, int reason) { + void networkBlocked(int uid, UidBlockedState uidBlockedState) { synchronized (mLock) { if (LOGD || uid == mDebugUid) { - Slog.d(TAG, uid + " is " + getBlockedReason(reason)); + Slog.d(TAG, "Blocked state of uid: " + uidBlockedState.toString()); } - mNetworkBlockedBuffer.networkBlocked(uid, reason); + mNetworkBlockedBuffer.networkBlocked(uid, uidBlockedState.blockedReasons, + uidBlockedState.allowedReasons, uidBlockedState.effectiveBlockedReasons); } } @@ -269,29 +261,6 @@ public class NetworkPolicyLogger { } } - private static String getBlockedReason(int reason) { - switch (reason) { - case NTWK_BLOCKED_POWER: - return "blocked by power restrictions"; - case NTWK_ALLOWED_NON_METERED: - return "allowed on unmetered network"; - case NTWK_BLOCKED_DENYLIST: - return "denylisted on metered network"; - case NTWK_ALLOWED_ALLOWLIST: - return "allowlisted on metered network"; - case NTWK_ALLOWED_TMP_ALLOWLIST: - return "temporary allowlisted on metered network"; - case NTWK_BLOCKED_BG_RESTRICT: - return "blocked when background is restricted"; - case NTWK_ALLOWED_DEFAULT: - return "allowed by default"; - case NTWK_BLOCKED_RESTRICTED_MODE: - return "blocked by restricted networking mode"; - default: - return String.valueOf(reason); - } - } - private static String getPolicyChangedLog(int uid, int oldPolicy, int newPolicy) { return "Policy for " + uid + " changed from " + NetworkPolicyManager.uidPoliciesToString(oldPolicy) + " to " @@ -402,14 +371,17 @@ public class NetworkPolicyLogger { data.timeStamp = System.currentTimeMillis(); } - public void networkBlocked(int uid, int reason) { + public void networkBlocked(int uid, int blockedReasons, int allowedReasons, + int effectiveBlockedReasons) { final Data data = getNextSlot(); if (data == null) return; data.reset(); data.type = EVENT_NETWORK_BLOCKED; data.ifield1 = uid; - data.ifield2 = reason; + data.ifield2 = blockedReasons; + data.ifield3 = allowedReasons; + data.ifield4 = effectiveBlockedReasons; data.timeStamp = System.currentTimeMillis(); } @@ -554,7 +526,8 @@ public class NetworkPolicyLogger { case EVENT_TYPE_GENERIC: return data.sfield1; case EVENT_NETWORK_BLOCKED: - return data.ifield1 + "-" + getBlockedReason(data.ifield2); + return data.ifield1 + "-" + UidBlockedState.toString( + data.ifield2, data.ifield3, data.ifield4); case EVENT_UID_STATE_CHANGED: return data.ifield1 + ":" + ProcessList.makeProcStateString(data.ifield2) + ":" + ActivityManager.getCapabilitiesSummary(data.ifield3) @@ -593,17 +566,18 @@ public class NetworkPolicyLogger { } } - public final static class Data { - int type; - long timeStamp; + private static final class Data { + public int type; + public long timeStamp; - int ifield1; - int ifield2; - int ifield3; - long lfield1; - boolean bfield1; - boolean bfield2; - String sfield1; + public int ifield1; + public int ifield2; + public int ifield3; + public int ifield4; + public long lfield1; + public boolean bfield1; + public boolean bfield2; + public String sfield1; public void reset(){ sfield1 = null; diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 23adc711925c6..367f33848c06b 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -79,14 +79,10 @@ import static android.net.NetworkPolicyManager.ALLOWED_REASON_RESTRICTED_MODE_PE import static android.net.NetworkPolicyManager.ALLOWED_REASON_SYSTEM; import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; -import static android.net.NetworkPolicyManager.MASK_ALL_NETWORKS; -import static android.net.NetworkPolicyManager.MASK_METERED_NETWORKS; -import static android.net.NetworkPolicyManager.MASK_RESTRICTED_MODE_NETWORKS; import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND; 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_NONE; import static android.net.NetworkPolicyManager.RULE_REJECT_ALL; import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; @@ -135,15 +131,6 @@ import static com.android.internal.util.XmlUtils.writeIntAttribute; import static com.android.internal.util.XmlUtils.writeLongAttribute; import static com.android.internal.util.XmlUtils.writeStringAttribute; import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT; -import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_ALLOWLIST; -import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_DEFAULT; -import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_NON_METERED; -import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_SYSTEM; -import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_TMP_ALLOWLIST; -import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_BG_RESTRICT; -import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_DENYLIST; -import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_POWER; -import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_RESTRICTED_MODE; import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; @@ -518,8 +505,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { /** Defined UID policies. */ @GuardedBy("mUidRulesFirstLock") final SparseIntArray mUidPolicy = new SparseIntArray(); - /** Currently derived rules for each UID. */ - @GuardedBy("mUidRulesFirstLock") final SparseIntArray mUidRules = new SparseIntArray(); @GuardedBy("mUidRulesFirstLock") final SparseIntArray mUidFirewallStandbyRules = new SparseIntArray(); @@ -598,6 +583,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mUidRulesFirstLock") private final SparseArray mUidBlockedState = new SparseArray<>(); + /** Objects used temporarily while computing the new blocked state for each uid. */ + @GuardedBy("mUidRulesFirstLock") + private final SparseArray mTmpUidBlockedState = new SparseArray<>(); + /** Map from network ID to last observed meteredness state */ @GuardedBy("mNetworkPoliciesSecondLock") private final SparseBooleanArray mNetworkMetered = new SparseBooleanArray(); @@ -3825,7 +3814,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final SparseBooleanArray knownUids = new SparseBooleanArray(); collectKeys(mUidState, knownUids); - collectKeys(mUidRules, knownUids); collectKeys(mUidBlockedState, knownUids); fout.println("Status for all known UIDs:"); @@ -3844,10 +3832,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { fout.print(uidState.toString()); } - final int uidRules = mUidRules.get(uid, RULE_NONE); - fout.print(" rules="); - fout.print(uidRulesToString(uidRules)); - final UidBlockedState uidBlockedState = mUidBlockedState.get(uid); if (uidBlockedState == null) { fout.print(" blocked_state={null}"); @@ -3859,20 +3843,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } fout.decreaseIndent(); - fout.println("Status for just UIDs with rules:"); - fout.increaseIndent(); - size = mUidRules.size(); - for (int i = 0; i < size; i++) { - final int uid = mUidRules.keyAt(i); - fout.print("UID="); - fout.print(uid); - final int uidRules = mUidRules.get(uid, RULE_NONE); - fout.print(" rules="); - fout.print(uidRulesToString(uidRules)); - fout.println(); - } - fout.decreaseIndent(); - fout.println("Admin restricted uids for metered data:"); fout.increaseIndent(); size = mMeteredRestrictedUids.size(); @@ -4019,22 +3989,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { void updateRestrictedModeAllowlistUL() { mUidFirewallRestrictedModeRules.clear(); forEachUid("updateRestrictedModeAllowlist", uid -> { - final int oldUidRule = mUidRules.get(uid); - final int newUidRule = getNewRestrictedModeUidRule(uid, oldUidRule); - final boolean hasUidRuleChanged = oldUidRule != newUidRule; - final int newFirewallRule = getRestrictedModeFirewallRule(newUidRule); + synchronized (mUidRulesFirstLock) { + final UidBlockedState uidBlockedState = updateBlockedReasonsForRestrictedModeUL( + uid); + final int newFirewallRule = getRestrictedModeFirewallRule(uidBlockedState); - // setUidFirewallRulesUL will allowlist all uids that are passed to it, so only add - // non-default rules. - if (newFirewallRule != FIREWALL_RULE_DEFAULT) { - mUidFirewallRestrictedModeRules.append(uid, newFirewallRule); + // setUidFirewallRulesUL will allowlist all uids that are passed to it, so only add + // non-default rules. + if (newFirewallRule != FIREWALL_RULE_DEFAULT) { + mUidFirewallRestrictedModeRules.append(uid, newFirewallRule); + } } - - if (hasUidRuleChanged) { - mUidRules.put(uid, newUidRule); - mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRule).sendToTarget(); - } - updateBlockedReasonsForRestrictedModeUL(uid); }); if (mRestrictedNetworkingMode) { // firewall rules only need to be set when this mode is being enabled. @@ -4047,15 +4012,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @VisibleForTesting @GuardedBy("mUidRulesFirstLock") void updateRestrictedModeForUidUL(int uid) { - final int oldUidRule = mUidRules.get(uid); - final int newUidRule = getNewRestrictedModeUidRule(uid, oldUidRule); - final boolean hasUidRuleChanged = oldUidRule != newUidRule; - - if (hasUidRuleChanged) { - mUidRules.put(uid, newUidRule); - mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRule).sendToTarget(); - } - updateBlockedReasonsForRestrictedModeUL(uid); + final UidBlockedState uidBlockedState = updateBlockedReasonsForRestrictedModeUL(uid); // if restricted networking mode is on, and the app has an access exemption, the uid rule // will not change, but the firewall rule will have to be updated. @@ -4063,16 +4020,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // Note: setUidFirewallRule also updates mUidFirewallRestrictedModeRules. // In this case, default firewall rules can also be added. setUidFirewallRule(FIREWALL_CHAIN_RESTRICTED, uid, - getRestrictedModeFirewallRule(newUidRule)); + getRestrictedModeFirewallRule(uidBlockedState)); } } - private void updateBlockedReasonsForRestrictedModeUL(int uid) { - UidBlockedState uidBlockedState = mUidBlockedState.get(uid); - if (uidBlockedState == null) { - uidBlockedState = new UidBlockedState(); - mUidBlockedState.put(uid, uidBlockedState); - } + @GuardedBy("mUidRulesFirstLock") + private UidBlockedState updateBlockedReasonsForRestrictedModeUL(int uid) { + final UidBlockedState uidBlockedState = getOrCreateUidBlockedStateForUid( + mUidBlockedState, uid); final int oldEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons; if (mRestrictedNetworkingMode) { uidBlockedState.blockedReasons |= BLOCKED_REASON_RESTRICTED_MODE; @@ -4086,23 +4041,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } uidBlockedState.updateEffectiveBlockedReasons(); if (oldEffectiveBlockedReasons != uidBlockedState.effectiveBlockedReasons) { - mHandler.obtainMessage(MSG_BLOCKED_REASON_CHANGED, uid, - uidBlockedState.effectiveBlockedReasons, oldEffectiveBlockedReasons) - .sendToTarget(); + postBlockedReasonsChangedMsg(uid, + uidBlockedState.effectiveBlockedReasons, oldEffectiveBlockedReasons); + + postUidRulesChangedMsg(uid, uidBlockedState.deriveUidRules()); } + return uidBlockedState; } - private int getNewRestrictedModeUidRule(int uid, int oldUidRule) { - int newRule = oldUidRule; - newRule &= ~MASK_RESTRICTED_MODE_NETWORKS; - if (mRestrictedNetworkingMode && !hasRestrictedModeAccess(uid)) { - newRule |= RULE_REJECT_RESTRICTED_MODE; - } - return newRule; - } - - private static int getRestrictedModeFirewallRule(int uidRule) { - if ((uidRule & RULE_REJECT_RESTRICTED_MODE) != 0) { + private static int getRestrictedModeFirewallRule(UidBlockedState uidBlockedState) { + if ((uidBlockedState.effectiveBlockedReasons & BLOCKED_REASON_RESTRICTED_MODE) != 0) { // rejected in restricted mode, this is the default behavior. return FIREWALL_RULE_DEFAULT; } else { @@ -4310,16 +4258,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (!isUidValidForDenylistRulesUL(uid)) { continue; } - int oldRules = mUidRules.get(uid); - if (enableChain) { - // Chain wasn't enabled before and the other power-related - // chains are allowlists, so we can clear the - // MASK_ALL_NETWORKS part of the rules and re-inform listeners if - // the effective rules result in blocking network access. - oldRules &= MASK_METERED_NETWORKS; - } else { - // Skip if it had no restrictions to begin with - if ((oldRules & MASK_ALL_NETWORKS) == 0) continue; + final UidBlockedState uidBlockedState = getOrCreateUidBlockedStateForUid( + mUidBlockedState, uid); + if (!enableChain && (uidBlockedState.blockedReasons & ~BLOCKED_METERED_REASON_MASK) + == BLOCKED_REASON_NONE) { + // Chain isn't enabled and the uid had no restrictions to begin with. + continue; } final boolean isUidIdle = !paroled && isUidIdle(uid); if (isUidIdle && !mPowerSaveTempWhitelistAppIds.get(UserHandle.getAppId(uid)) @@ -4329,13 +4273,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } else { mUidFirewallStandbyRules.put(uid, FIREWALL_RULE_DEFAULT); } - final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldRules, - isUidIdle); - if (newUidRules == RULE_NONE) { - mUidRules.delete(uid); - } else { - mUidRules.put(uid, newUidRules); - } + updateRulesForPowerRestrictionsUL(uid, isUidIdle); } setUidFirewallRulesUL(FIREWALL_CHAIN_STANDBY, blockedUids, enableChain ? CHAIN_TOGGLE_ENABLE : CHAIN_TOGGLE_DISABLE); @@ -4553,6 +4491,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mInternetPermissionMap.put(uid, hasPermission); return hasPermission; } catch (RemoteException e) { + // ignored; service lives in system_server } return true; } @@ -4563,7 +4502,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mUidRulesFirstLock") private void onUidDeletedUL(int uid) { // First cleanup in-memory state synchronously... - mUidRules.delete(uid); mUidBlockedState.delete(uid); mUidPolicy.delete(uid); mUidFirewallStandbyRules.delete(uid); @@ -4650,7 +4588,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * permission, since there is no need to change the {@code iptables} rule if the app does not * have permission to use the internet. * - *

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

The {@link #mUidBlockedState} map is used to define the transition of states of an UID. * */ private void updateRulesForDataUsageRestrictionsUL(int uid) { @@ -4665,6 +4603,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + @GuardedBy("mUidRulesFirstLock") private void updateRulesForDataUsageRestrictionsULInner(int uid) { if (!isUidValidForAllowlistRulesUL(uid)) { if (LOGD) Slog.d(TAG, "no need to update restrict data rules for uid " + uid); @@ -4672,38 +4611,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE); - final int oldUidRules = mUidRules.get(uid, RULE_NONE); final boolean isForeground = isUidForegroundOnRestrictBackgroundUL(uid); final boolean isRestrictedByAdmin = isRestrictedByAdminUL(uid); - UidBlockedState uidBlockedState = mUidBlockedState.get(uid); - if (uidBlockedState == null) { - uidBlockedState = new UidBlockedState(); - mUidBlockedState.put(uid, uidBlockedState); - } + final UidBlockedState uidBlockedState = getOrCreateUidBlockedStateForUid( + mUidBlockedState, uid); + final UidBlockedState previousUidBlockedState = getOrCreateUidBlockedStateForUid( + mTmpUidBlockedState, uid); + previousUidBlockedState.copyFrom(uidBlockedState); final boolean isDenied = (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0; final boolean isAllowed = (uidPolicy & POLICY_ALLOW_METERED_BACKGROUND) != 0; - // copy oldUidRules and clear out METERED_NETWORKS rules. - int newUidRules = oldUidRules & (~MASK_METERED_NETWORKS); - - // First step: define the new rule based on user restrictions and foreground state. - if (isRestrictedByAdmin) { - newUidRules |= RULE_REJECT_METERED; - } else if (isForeground) { - if (isDenied || (mRestrictBackground && !isAllowed)) { - newUidRules |= RULE_TEMPORARY_ALLOW_METERED; - } else if (isAllowed) { - newUidRules |= RULE_ALLOW_METERED; - } - } else { - if (isDenied) { - newUidRules |= RULE_REJECT_METERED; - } else if (mRestrictBackground && isAllowed) { - newUidRules |= RULE_ALLOW_METERED; - } - } - int newBlockedReasons = BLOCKED_REASON_NONE; int newAllowedReasons = ALLOWED_REASON_NONE; newBlockedReasons |= (isRestrictedByAdmin ? BLOCKED_METERED_REASON_ADMIN_DISABLED : 0); @@ -4714,16 +4632,48 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { newAllowedReasons |= (isForeground ? ALLOWED_METERED_REASON_FOREGROUND : 0); newAllowedReasons |= (isAllowed ? ALLOWED_METERED_REASON_USER_EXEMPTED : 0); + uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons + & ~BLOCKED_METERED_REASON_MASK) | newBlockedReasons; + uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons + & ~ALLOWED_METERED_REASON_MASK) | newAllowedReasons; + uidBlockedState.updateEffectiveBlockedReasons(); + final int oldEffectiveBlockedReasons = previousUidBlockedState.effectiveBlockedReasons; + final int newEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons; + if (oldEffectiveBlockedReasons != newEffectiveBlockedReasons) { + postBlockedReasonsChangedMsg(uid, + newEffectiveBlockedReasons, oldEffectiveBlockedReasons); + + postUidRulesChangedMsg(uid, uidBlockedState.deriveUidRules()); + } + + // Note that the conditionals below are for avoiding unnecessary calls to netd. + // TODO: Measure the performance for doing a no-op call to netd so that we can + // remove the conditionals to simplify the logic below. We can also further reduce + // some calls to netd if they turn out to be costly. + final int denylistReasons = BLOCKED_METERED_REASON_ADMIN_DISABLED + | BLOCKED_METERED_REASON_USER_RESTRICTED; + if ((oldEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE + || (newEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE) { + setMeteredNetworkDenylist(uid, + (newEffectiveBlockedReasons & denylistReasons) != BLOCKED_REASON_NONE); + } + final int allowlistReasons = ALLOWED_METERED_REASON_FOREGROUND + | ALLOWED_METERED_REASON_USER_EXEMPTED; + final int oldAllowedReasons = previousUidBlockedState.allowedReasons; + if ((oldAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE + || (newAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE) { + setMeteredNetworkAllowlist(uid, + (newAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE); + } + if (LOGV) { Log.v(TAG, "updateRuleForRestrictBackgroundUL(" + uid + ")" + ": isForeground=" +isForeground + ", isDenied=" + isDenied + ", isAllowed=" + isAllowed + ", isRestrictedByAdmin=" + isRestrictedByAdmin - + ", oldRule=" + uidRulesToString(oldUidRules & MASK_METERED_NETWORKS) - + ", newRule=" + uidRulesToString(newUidRules & MASK_METERED_NETWORKS) - + ", newUidRules=" + uidRulesToString(newUidRules) - + ", oldUidRules=" + uidRulesToString(oldUidRules) + + ", oldBlockedState=" + previousUidBlockedState.toString() + + ", newBlockedState=" + ", oldBlockedMeteredReasons=" + NetworkPolicyManager.blockedReasonsToString( uidBlockedState.blockedReasons & BLOCKED_METERED_REASON_MASK) + ", oldBlockedMeteredEffectiveReasons=" @@ -4732,84 +4682,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { + ", oldAllowedMeteredReasons=" + NetworkPolicyManager.blockedReasonsToString( uidBlockedState.allowedReasons & BLOCKED_METERED_REASON_MASK)); } - - if (newUidRules == RULE_NONE) { - mUidRules.delete(uid); - } else { - mUidRules.put(uid, newUidRules); - } - - // Second step: apply bw changes based on change of state. - if (newUidRules != oldUidRules) { - if (hasRule(newUidRules, RULE_TEMPORARY_ALLOW_METERED)) { - // Temporarily allow foreground app, removing from denylist if necessary - // (since bw_penalty_box prevails over bw_happy_box). - - setMeteredNetworkAllowlist(uid, true); - // TODO: if statement below is used to avoid an unnecessary call to netd / iptables, - // but ideally it should be just: - // setMeteredNetworkDenylist(uid, isDenied); - if (isDenied) { - setMeteredNetworkDenylist(uid, false); - } - } else if (hasRule(oldUidRules, RULE_TEMPORARY_ALLOW_METERED)) { - // Remove temporary exemption 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: - // setMeteredNetworkAllowlist(uid, isAllowed); - // setMeteredNetworkDenylist(uid, isDenied); - if (!isAllowed) { - setMeteredNetworkAllowlist(uid, false); - } - if (isDenied || isRestrictedByAdmin) { - setMeteredNetworkDenylist(uid, true); - } - } else if (hasRule(newUidRules, RULE_REJECT_METERED) - || hasRule(oldUidRules, RULE_REJECT_METERED)) { - // Flip state because app was explicitly added or removed to denylist. - setMeteredNetworkDenylist(uid, (isDenied || isRestrictedByAdmin)); - if (hasRule(oldUidRules, RULE_REJECT_METERED) && isAllowed) { - // Since denial prevails over allowance, we need to handle the special case - // where app is allowed and denied at the same time (although such - // scenario should be blocked by the UI), then it is removed from the denylist. - setMeteredNetworkAllowlist(uid, isAllowed); - } - } else if (hasRule(newUidRules, RULE_ALLOW_METERED) - || hasRule(oldUidRules, RULE_ALLOW_METERED)) { - // Flip state because app was explicitly added or removed to allowlist. - setMeteredNetworkAllowlist(uid, isAllowed); - } else { - // All scenarios should have been covered above. - Log.wtf(TAG, "Unexpected change of metered UID state for " + uid - + ": foreground=" + isForeground - + ", allowlisted=" + isAllowed - + ", denylisted=" + isDenied - + ", isRestrictedByAdmin=" + isRestrictedByAdmin - + ", newRule=" + uidRulesToString(newUidRules) - + ", oldRule=" + uidRulesToString(oldUidRules)); - } - - // Dispatch changed rule to existing listeners. - mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget(); - } - - final int oldEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons; - uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons - & ~BLOCKED_METERED_REASON_MASK) | newBlockedReasons; - uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons - & ~ALLOWED_METERED_REASON_MASK) | newAllowedReasons; - uidBlockedState.updateEffectiveBlockedReasons(); - if (oldEffectiveBlockedReasons != uidBlockedState.effectiveBlockedReasons) { - mHandler.obtainMessage(MSG_BLOCKED_REASON_CHANGED, uid, - uidBlockedState.effectiveBlockedReasons, oldEffectiveBlockedReasons) - .sendToTarget(); - } } /** - * Updates the power-related part of the {@link #mUidRules} for a given map, and notify external - * listeners in case of change. + * Updates the power-related part of the {@link #mUidBlockedState} 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 allowed for power @@ -4820,23 +4697,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { *

  • Battery Saver Mode is on: {@code fw_powersave} 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 UID allowlist state. + * This method updates the power-related part of the {@link #mUidBlockedState} for a given + * uid based on these modes, the UID process state (foreground or not), and the UID + * allowlist state. *

    * NOTE: This method does not update the firewall rules on {@code netd}. */ @GuardedBy("mUidRulesFirstLock") private void updateRulesForPowerRestrictionsUL(int uid) { - final int oldUidRules = mUidRules.get(uid, RULE_NONE); - - final int newUidRules = updateRulesForPowerRestrictionsUL(uid, oldUidRules, - isUidIdle(uid)); - - if (newUidRules == RULE_NONE) { - mUidRules.delete(uid); - } else { - mUidRules.put(uid, newUidRules); - } + updateRulesForPowerRestrictionsUL(uid, isUidIdle(uid)); } /** @@ -4845,56 +4714,37 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * @param uid the uid of the app to update rules for * @param oldUidRules the current rules for the uid, in order to determine if there's a change * @param isUidIdle whether uid is idle or not - * - * @return the new computed rules for the uid */ @GuardedBy("mUidRulesFirstLock") - private int updateRulesForPowerRestrictionsUL(int uid, int oldUidRules, boolean isUidIdle) { + private void updateRulesForPowerRestrictionsUL(int uid, boolean isUidIdle) { if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) { Trace.traceBegin(Trace.TRACE_TAG_NETWORK, - "updateRulesForPowerRestrictionsUL: " + uid + "/" + oldUidRules + "/" + "updateRulesForPowerRestrictionsUL: " + uid + "/" + (isUidIdle ? "I" : "-")); } try { - return updateRulesForPowerRestrictionsULInner(uid, oldUidRules, isUidIdle); + updateRulesForPowerRestrictionsULInner(uid, isUidIdle); } finally { Trace.traceEnd(Trace.TRACE_TAG_NETWORK); } } @GuardedBy("mUidRulesFirstLock") - private int updateRulesForPowerRestrictionsULInner(int uid, int oldUidRules, - boolean isUidIdle) { + private void updateRulesForPowerRestrictionsULInner(int uid, boolean isUidIdle) { if (!isUidValidForDenylistRulesUL(uid)) { if (LOGD) Slog.d(TAG, "no need to update restrict power rules for uid " + uid); - return RULE_NONE; + return; } - final boolean restrictMode = isUidIdle || mRestrictPower || mDeviceIdleMode; final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid); final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid, mDeviceIdleMode); - // Copy existing uid rules and clear ALL_NETWORK rules. - int newUidRules = oldUidRules & (~MASK_ALL_NETWORKS); - - UidBlockedState uidBlockedState = mUidBlockedState.get(uid); - if (uidBlockedState == null) { - uidBlockedState = new UidBlockedState(); - mUidBlockedState.put(uid, uidBlockedState); - } - - // 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) { - newUidRules |= RULE_ALLOW_ALL; - } - } else if (restrictMode) { - newUidRules |= isWhitelisted ? RULE_ALLOW_ALL : RULE_REJECT_ALL; - } + final UidBlockedState uidBlockedState = getOrCreateUidBlockedStateForUid( + mUidBlockedState, uid); + final UidBlockedState previousUidBlockedState = getOrCreateUidBlockedStateForUid( + mTmpUidBlockedState, uid); + previousUidBlockedState.copyFrom(uidBlockedState); int newBlockedReasons = BLOCKED_REASON_NONE; int newAllowedReasons = ALLOWED_REASON_NONE; @@ -4910,6 +4760,20 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { newAllowedReasons |= (isWhitelistedFromPowerSaveExceptIdleUL(uid) ? ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST : 0); + uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons + & BLOCKED_METERED_REASON_MASK) | newBlockedReasons; + uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons + & ALLOWED_METERED_REASON_MASK) | newAllowedReasons; + uidBlockedState.updateEffectiveBlockedReasons(); + if (previousUidBlockedState.effectiveBlockedReasons + != uidBlockedState.effectiveBlockedReasons) { + postBlockedReasonsChangedMsg(uid, + uidBlockedState.effectiveBlockedReasons, + previousUidBlockedState.effectiveBlockedReasons); + + postUidRulesChangedMsg(uid, uidBlockedState.deriveUidRules()); + } + if (LOGV) { Log.v(TAG, "updateRulesForPowerRestrictionsUL(" + uid + ")" + ", isIdle: " + isUidIdle @@ -4917,43 +4781,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { + ", mDeviceIdleMode: " + mDeviceIdleMode + ", isForeground=" + isForeground + ", isWhitelisted=" + isWhitelisted - + ", oldRule=" + uidRulesToString(oldUidRules & MASK_ALL_NETWORKS) - + ", newRule=" + uidRulesToString(newUidRules & MASK_ALL_NETWORKS) - + ", newUidRules=" + uidRulesToString(newUidRules) - + ", oldUidRules=" + uidRulesToString(oldUidRules)); + + ", oldUidBlockedState=" + previousUidBlockedState.toString() + + ", newUidBlockedState=" + uidBlockedState.toString()); } - - // Second step: notify listeners if state changed. - if (newUidRules != oldUidRules) { - if ((newUidRules & MASK_ALL_NETWORKS) == RULE_NONE || hasRule(newUidRules, - RULE_ALLOW_ALL)) { - if (LOGV) Log.v(TAG, "Allowing non-metered access for UID " + uid); - } else if (hasRule(newUidRules, RULE_REJECT_ALL)) { - 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=" + isWhitelisted - + ", newRule=" + uidRulesToString(newUidRules) - + ", oldRule=" + uidRulesToString(oldUidRules)); - } - mHandler.obtainMessage(MSG_RULES_CHANGED, uid, newUidRules).sendToTarget(); - } - - final int oldEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons; - uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons - & BLOCKED_METERED_REASON_MASK) | newBlockedReasons; - uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons - & ALLOWED_METERED_REASON_MASK) | newAllowedReasons; - uidBlockedState.updateEffectiveBlockedReasons(); - if (oldEffectiveBlockedReasons != uidBlockedState.effectiveBlockedReasons) { - mHandler.obtainMessage(MSG_BLOCKED_REASON_CHANGED, uid, - uidBlockedState.effectiveBlockedReasons, oldEffectiveBlockedReasons) - .sendToTarget(); - } - - return newUidRules; } private class NetPolicyAppIdleStateChangeListener extends AppIdleStateChangeListener { @@ -4981,10 +4811,23 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + private void postBlockedReasonsChangedMsg(int uid, int newEffectiveBlockedReasons, + int oldEffectiveBlockedReasons) { + mHandler.obtainMessage(MSG_BLOCKED_REASON_CHANGED, uid, + newEffectiveBlockedReasons, oldEffectiveBlockedReasons) + .sendToTarget(); + } + + private void postUidRulesChangedMsg(int uid, int uidRules) { + mHandler.obtainMessage(MSG_RULES_CHANGED, uid, uidRules) + .sendToTarget(); + } + private void dispatchUidRulesChanged(INetworkPolicyListener listener, int uid, int uidRules) { try { listener.onUidRulesChanged(uid, uidRules); } catch (RemoteException ignored) { + // Ignore if there is an error sending the callback to the client. } } @@ -4993,6 +4836,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { try { listener.onMeteredIfacesChanged(meteredIfaces); } catch (RemoteException ignored) { + // Ignore if there is an error sending the callback to the client. } } @@ -5001,6 +4845,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { try { listener.onRestrictBackgroundChanged(restrictBackground); } catch (RemoteException ignored) { + // Ignore if there is an error sending the callback to the client. } } @@ -5009,6 +4854,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { try { listener.onUidPoliciesChanged(uid, uidPolicies); } catch (RemoteException ignored) { + // Ignore if there is an error sending the callback to the client. } } @@ -5017,6 +4863,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { try { listener.onSubscriptionOverride(subId, overrideMask, overrideValue, networkTypes); } catch (RemoteException ignored) { + // Ignore if there is an error sending the callback to the client. } } @@ -5025,6 +4872,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { try { listener.onSubscriptionPlansChanged(subId, plans); } catch (RemoteException ignored) { + // Ignore if there is an error sending the callback to the client. } } @@ -5033,6 +4881,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { try { listener.onBlockedReasonChanged(uid, oldBlockedReasons, newBlockedReasons); } catch (RemoteException ignored) { + // Ignore if there is an error sending the callback to the client. } } @@ -5043,6 +4892,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { case MSG_RULES_CHANGED: { final int uid = msg.arg1; final int uidRules = msg.arg2; + if (LOGV) { + Slog.v(TAG, "Dispatching rules=" + uidRulesToString(uidRules) + + " for uid=" + uid); + } final int length = mListeners.beginBroadcast(); for (int i = 0; i < length; i++) { final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); @@ -5663,90 +5516,38 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final long startTime = mStatLogger.getTime(); mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG); - final int uidRules; - final boolean isBackgroundRestricted; + int blockedReasons; synchronized (mUidRulesFirstLock) { - uidRules = mUidRules.get(uid, RULE_NONE); - isBackgroundRestricted = mRestrictBackground; + final UidBlockedState uidBlockedState = mUidBlockedState.get(uid); + blockedReasons = uidBlockedState == null + ? BLOCKED_REASON_NONE : uidBlockedState.effectiveBlockedReasons; + if (!isNetworkMetered) { + blockedReasons &= ~BLOCKED_METERED_REASON_MASK; + } + mLogger.networkBlocked(uid, uidBlockedState); } - final boolean ret = isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered, - isBackgroundRestricted, mLogger); mStatLogger.logDurationStat(Stats.IS_UID_NETWORKING_BLOCKED, startTime); - return ret; + return blockedReasons != BLOCKED_REASON_NONE; } @Override public boolean isUidRestrictedOnMeteredNetworks(int uid) { mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG); - final int uidRules; - final boolean isBackgroundRestricted; synchronized (mUidRulesFirstLock) { - uidRules = mUidRules.get(uid, RULE_ALLOW_ALL); - isBackgroundRestricted = mRestrictBackground; + final UidBlockedState uidBlockedState = mUidBlockedState.get(uid); + int blockedReasons = uidBlockedState == null + ? BLOCKED_REASON_NONE : uidBlockedState.effectiveBlockedReasons; + blockedReasons &= BLOCKED_METERED_REASON_MASK; + return blockedReasons != BLOCKED_REASON_NONE; } - // TODO(b/177490332): The logic here might not be correct because it doesn't consider - // RULE_REJECT_METERED condition. And it could be replaced by - // isUidNetworkingBlockedInternal(). - return isBackgroundRestricted - && !hasRule(uidRules, RULE_ALLOW_METERED) - && !hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED); } private static boolean isSystem(int uid) { return uid < Process.FIRST_APPLICATION_UID; } - static boolean isUidNetworkingBlockedInternal(int uid, int uidRules, boolean isNetworkMetered, - boolean isBackgroundRestricted, @Nullable NetworkPolicyLogger logger) { - final int reason; - // Networks are never blocked for system components - if (isSystem(uid)) { - reason = NTWK_ALLOWED_SYSTEM; - } else if (hasRule(uidRules, RULE_REJECT_RESTRICTED_MODE)) { - reason = NTWK_BLOCKED_RESTRICTED_MODE; - } else if (hasRule(uidRules, RULE_REJECT_ALL)) { - reason = NTWK_BLOCKED_POWER; - } else if (!isNetworkMetered) { - reason = NTWK_ALLOWED_NON_METERED; - } else if (hasRule(uidRules, RULE_REJECT_METERED)) { - reason = NTWK_BLOCKED_DENYLIST; - } else if (hasRule(uidRules, RULE_ALLOW_METERED)) { - reason = NTWK_ALLOWED_ALLOWLIST; - } else if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) { - reason = NTWK_ALLOWED_TMP_ALLOWLIST; - } else if (isBackgroundRestricted) { - reason = NTWK_BLOCKED_BG_RESTRICT; - } else { - reason = NTWK_ALLOWED_DEFAULT; - } - - final boolean blocked; - switch(reason) { - case NTWK_ALLOWED_DEFAULT: - case NTWK_ALLOWED_NON_METERED: - case NTWK_ALLOWED_TMP_ALLOWLIST: - case NTWK_ALLOWED_ALLOWLIST: - case NTWK_ALLOWED_SYSTEM: - blocked = false; - break; - case NTWK_BLOCKED_RESTRICTED_MODE: - case NTWK_BLOCKED_POWER: - case NTWK_BLOCKED_DENYLIST: - case NTWK_BLOCKED_BG_RESTRICT: - blocked = true; - break; - default: - throw new IllegalArgumentException(); - } - if (logger != null) { - logger.networkBlocked(uid, reason); - } - - return blocked; - } - private class NetworkPolicyManagerInternalImpl extends NetworkPolicyManagerInternal { @Override @@ -5955,6 +5756,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return (bundle != null) ? bundle.getBoolean(key, defaultValue) : defaultValue; } + private static UidBlockedState getOrCreateUidBlockedStateForUid( + SparseArray uidBlockedStates, int uid) { + UidBlockedState uidBlockedState = uidBlockedStates.get(uid); + if (uidBlockedState == null) { + uidBlockedState = new UidBlockedState(); + uidBlockedStates.put(uid, uidBlockedState); + } + return uidBlockedState; + } + @VisibleForTesting static final class UidBlockedState { public int blockedReasons; @@ -6021,6 +5832,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @Override public String toString() { + return toString(blockedReasons, allowedReasons, effectiveBlockedReasons); + } + + public static String toString(int blockedReasons, int allowedReasons, + int effectiveBlockedReasons) { final StringBuilder sb = new StringBuilder(); sb.append("{"); sb.append("blocked=").append(blockedReasonsToString(blockedReasons)).append(","); @@ -6101,7 +5917,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - private static String blockedReasonsToString(int blockedReasons) { + public static String blockedReasonsToString(int blockedReasons) { if (blockedReasons == BLOCKED_REASON_NONE) { return blockedReasonToString(BLOCKED_REASON_NONE); } @@ -6121,7 +5937,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return sb.toString(); } - private static String allowedReasonsToString(int allowedReasons) { + public static String allowedReasonsToString(int allowedReasons) { if (allowedReasons == ALLOWED_REASON_NONE) { return allowedReasonToString(ALLOWED_REASON_NONE); } @@ -6140,9 +5956,53 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } return sb.toString(); } + + public void copyFrom(UidBlockedState uidBlockedState) { + blockedReasons = uidBlockedState.blockedReasons; + allowedReasons = uidBlockedState.allowedReasons; + effectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons; + } + + public int deriveUidRules() { + int uidRule = RULE_NONE; + if ((effectiveBlockedReasons & BLOCKED_REASON_RESTRICTED_MODE) != 0) { + uidRule |= RULE_REJECT_RESTRICTED_MODE; + } + + int powerBlockedReasons = BLOCKED_REASON_APP_STANDBY + | BLOCKED_REASON_DOZE + | BLOCKED_REASON_BATTERY_SAVER; + if ((effectiveBlockedReasons & powerBlockedReasons) != 0) { + uidRule |= RULE_REJECT_ALL; + } else if ((blockedReasons & powerBlockedReasons) != 0) { + uidRule |= RULE_ALLOW_ALL; + } + + // UidRule doesn't include RestrictBackground (DataSaver) state, so not including in + // metered blocked reasons below. + int meteredBlockedReasons = BLOCKED_METERED_REASON_ADMIN_DISABLED + | BLOCKED_METERED_REASON_USER_RESTRICTED; + if ((effectiveBlockedReasons & meteredBlockedReasons) != 0) { + uidRule |= RULE_REJECT_METERED; + } else if ((blockedReasons & BLOCKED_METERED_REASON_USER_RESTRICTED) != 0 + && (allowedReasons & ALLOWED_METERED_REASON_FOREGROUND) != 0) { + uidRule |= RULE_TEMPORARY_ALLOW_METERED; + } else if ((blockedReasons & BLOCKED_METERED_REASON_DATA_SAVER) != 0) { + if ((allowedReasons & ALLOWED_METERED_REASON_USER_EXEMPTED) != 0) { + uidRule |= RULE_ALLOW_ALL; + } else if ((allowedReasons & ALLOWED_METERED_REASON_FOREGROUND) != 0) { + uidRule |= RULE_TEMPORARY_ALLOW_METERED; + } + } + if (LOGV) { + Slog.v(TAG, "uidBlockedState=" + this.toString() + + " -> uidRule=" + uidRulesToString(uidRule)); + } + return uidRule; + } } - private class NotificationId { + private static class NotificationId { private final String mTag; private final int mId;