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;