diff options
10 files changed, 509 insertions, 35 deletions
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 365f9130ddd7..594ec18d9996 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -16,6 +16,7 @@ package android.net; +import static android.app.ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK; import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN; import static android.app.ActivityManager.procStateToString; import static android.content.pm.PackageManager.GET_SIGNATURES; @@ -170,6 +171,8 @@ public class NetworkPolicyManager { public static final String FIREWALL_CHAIN_NAME_RESTRICTED = "restricted"; /** @hide */ public static final String FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY = "low_power_standby"; + /** @hide */ + public static final String FIREWALL_CHAIN_NAME_BACKGROUND = "background"; private static final boolean ALLOW_PLATFORM_APP_POLICY = true; @@ -180,6 +183,9 @@ public class NetworkPolicyManager { /** @hide */ public static final int TOP_THRESHOLD_STATE = ActivityManager.PROCESS_STATE_BOUND_TOP; + /** @hide */ + public static final int BACKGROUND_THRESHOLD_STATE = ActivityManager.PROCESS_STATE_TOP_SLEEPING; + /** * {@link Intent} extra that indicates which {@link NetworkTemplate} rule it * applies to. @@ -264,6 +270,16 @@ public class NetworkPolicyManager { * @hide */ public static final int ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST = 1 << 6; + + /** + * Flag to indicate that the app is exempt from always-on background network restrictions. + * Note that this is explicitly different to the flag NOT_FOREGROUND which is used to grant + * shared exception to apps from power restrictions like doze, battery saver and app-standby. + * + * @hide + */ + public static final int ALLOWED_REASON_NOT_IN_BACKGROUND = 1 << 7; + /** * Flag to indicate that app is exempt from certain metered network restrictions because user * explicitly exempted it. @@ -822,6 +838,21 @@ public class NetworkPolicyManager { } /** + * This is currently only used as an implementation detail for + * {@link com.android.server.net.NetworkPolicyManagerService}. + * Only put here to be together with other isProcStateAllowed* methods. + * + * @hide + */ + public static boolean isProcStateAllowedNetworkWhileBackground(@Nullable UidState uidState) { + if (uidState == null) { + return false; + } + return uidState.procState < BACKGROUND_THRESHOLD_STATE + || (uidState.capability & PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK) != 0; + } + + /** * Returns true if {@param procState} is considered foreground and as such will be allowed * to access network when the device is in data saver mode. Otherwise, false. * @hide diff --git a/services/core/Android.bp b/services/core/Android.bp index 21cfd242cf45..2dda76e06c56 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -190,6 +190,7 @@ java_library_static { "com.android.sysprop.watchdog", "ImmutabilityAnnotation", "securebox", + "net_flags_lib", ], javac_shard_size: 50, javacflags: [ diff --git a/services/core/java/com/android/server/net/Android.bp b/services/core/java/com/android/server/net/Android.bp new file mode 100644 index 000000000000..71d8e6ba367e --- /dev/null +++ b/services/core/java/com/android/server/net/Android.bp @@ -0,0 +1,10 @@ +aconfig_declarations { + name: "net_flags", + package: "com.android.server.net", + srcs: ["*.aconfig"], +} + +java_aconfig_library { + name: "net_flags_lib", + aconfig_declarations: "net_flags", +} diff --git a/services/core/java/com/android/server/net/NetworkManagementService.java b/services/core/java/com/android/server/net/NetworkManagementService.java index 681d1a0ee10a..d25f52973085 100644 --- a/services/core/java/com/android/server/net/NetworkManagementService.java +++ b/services/core/java/com/android/server/net/NetworkManagementService.java @@ -17,6 +17,7 @@ package com.android.server.net; import static android.Manifest.permission.CONNECTIVITY_INTERNAL; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND; import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE; import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY; import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE; @@ -27,6 +28,7 @@ import static android.net.INetd.FIREWALL_CHAIN_NONE; import static android.net.INetd.FIREWALL_DENYLIST; import static android.net.INetd.FIREWALL_RULE_ALLOW; import static android.net.INetd.FIREWALL_RULE_DENY; +import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_BACKGROUND; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE; @@ -187,6 +189,13 @@ public class NetworkManagementService extends INetworkManagementService.Stub { */ @GuardedBy("mRulesLock") private final SparseIntArray mUidFirewallLowPowerStandbyRules = new SparseIntArray(); + + /** + * Contains the per-UID firewall rules that are used when Background chain is enabled. + */ + @GuardedBy("mRulesLock") + private final SparseIntArray mUidFirewallBackgroundRules = new SparseIntArray(); + /** Set of states for the child firewall chains. True if the chain is active. */ @GuardedBy("mRulesLock") final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray(); @@ -449,13 +458,15 @@ public class NetworkManagementService extends INetworkManagementService.Stub { syncFirewallChainLocked(FIREWALL_CHAIN_POWERSAVE, "powersave "); syncFirewallChainLocked(FIREWALL_CHAIN_RESTRICTED, "restricted "); syncFirewallChainLocked(FIREWALL_CHAIN_LOW_POWER_STANDBY, "low power standby "); + syncFirewallChainLocked(FIREWALL_CHAIN_BACKGROUND, FIREWALL_CHAIN_NAME_BACKGROUND); final int[] chains = { FIREWALL_CHAIN_STANDBY, FIREWALL_CHAIN_DOZABLE, FIREWALL_CHAIN_POWERSAVE, FIREWALL_CHAIN_RESTRICTED, - FIREWALL_CHAIN_LOW_POWER_STANDBY + FIREWALL_CHAIN_LOW_POWER_STANDBY, + FIREWALL_CHAIN_BACKGROUND, }; for (int chain : chains) { @@ -1206,6 +1217,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub { return FIREWALL_CHAIN_NAME_RESTRICTED; case FIREWALL_CHAIN_LOW_POWER_STANDBY: return FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY; + case FIREWALL_CHAIN_BACKGROUND: + return FIREWALL_CHAIN_NAME_BACKGROUND; default: throw new IllegalArgumentException("Bad child chain: " + chain); } @@ -1223,6 +1236,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub { return FIREWALL_ALLOWLIST; case FIREWALL_CHAIN_LOW_POWER_STANDBY: return FIREWALL_ALLOWLIST; + case FIREWALL_CHAIN_BACKGROUND: + return FIREWALL_ALLOWLIST; default: return isFirewallEnabled() ? FIREWALL_ALLOWLIST : FIREWALL_DENYLIST; } @@ -1343,6 +1358,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub { return mUidFirewallRestrictedRules; case FIREWALL_CHAIN_LOW_POWER_STANDBY: return mUidFirewallLowPowerStandbyRules; + case FIREWALL_CHAIN_BACKGROUND: + return mUidFirewallBackgroundRules; case FIREWALL_CHAIN_NONE: return mUidFirewallRules; default: @@ -1395,6 +1412,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub { pw.println(getFirewallChainState(FIREWALL_CHAIN_LOW_POWER_STANDBY)); dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY, mUidFirewallLowPowerStandbyRules); + + pw.print("UID firewall background chain enabled: "); + pw.println(getFirewallChainState(FIREWALL_CHAIN_BACKGROUND)); + dumpUidFirewallRule(pw, FIREWALL_CHAIN_NAME_BACKGROUND, mUidFirewallBackgroundRules); } pw.print("Firewall enabled: "); pw.println(mFirewallEnabled); @@ -1494,6 +1515,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub { if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of low power standby"); return true; } + if (getFirewallChainState(FIREWALL_CHAIN_BACKGROUND) + && mUidFirewallBackgroundRules.get(uid) != FIREWALL_RULE_ALLOW) { + if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because it is in background"); + return true; + } if (mUidRejectOnMetered.get(uid)) { if (DBG) Slog.d(TAG, "Uid " + uid + " restricted because of no metered data" + " in the background"); diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java index d7188c7f10c6..8e2d7780204a 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java +++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java @@ -16,6 +16,7 @@ package com.android.server.net; import static android.net.ConnectivityManager.BLOCKED_REASON_NONE; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND; import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE; import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY; import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE; @@ -24,6 +25,7 @@ import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY; import static android.net.INetd.FIREWALL_RULE_ALLOW; import static android.net.INetd.FIREWALL_RULE_DENY; import static android.net.NetworkPolicyManager.ALLOWED_REASON_NONE; +import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_BACKGROUND; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE; @@ -389,6 +391,8 @@ public class NetworkPolicyLogger { return FIREWALL_CHAIN_NAME_RESTRICTED; case FIREWALL_CHAIN_LOW_POWER_STANDBY: return FIREWALL_CHAIN_NAME_LOW_POWER_STANDBY; + case FIREWALL_CHAIN_BACKGROUND: + return FIREWALL_CHAIN_NAME_BACKGROUND; default: return String.valueOf(chain); } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 52734a4ad3b0..b47458b88f40 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -47,6 +47,7 @@ import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_ADMIN_DISAB import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER; import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_MASK; import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED; +import static android.net.ConnectivityManager.BLOCKED_REASON_APP_BACKGROUND; import static android.net.ConnectivityManager.BLOCKED_REASON_APP_STANDBY; import static android.net.ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER; import static android.net.ConnectivityManager.BLOCKED_REASON_DOZE; @@ -54,6 +55,7 @@ import static android.net.ConnectivityManager.BLOCKED_REASON_LOW_POWER_STANDBY; import static android.net.ConnectivityManager.BLOCKED_REASON_NONE; import static android.net.ConnectivityManager.BLOCKED_REASON_RESTRICTED_MODE; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND; import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE; import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY; import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE; @@ -77,6 +79,7 @@ import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_USER_EXEMP import static android.net.NetworkPolicyManager.ALLOWED_REASON_FOREGROUND; import static android.net.NetworkPolicyManager.ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST; import static android.net.NetworkPolicyManager.ALLOWED_REASON_NONE; +import static android.net.NetworkPolicyManager.ALLOWED_REASON_NOT_IN_BACKGROUND; import static android.net.NetworkPolicyManager.ALLOWED_REASON_POWER_SAVE_ALLOWLIST; import static android.net.NetworkPolicyManager.ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST; import static android.net.NetworkPolicyManager.ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS; @@ -96,6 +99,7 @@ import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED; import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_UNMETERED; import static android.net.NetworkPolicyManager.allowedReasonsToString; import static android.net.NetworkPolicyManager.blockedReasonsToString; +import static android.net.NetworkPolicyManager.isProcStateAllowedNetworkWhileBackground; import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode; import static android.net.NetworkPolicyManager.isProcStateAllowedWhileInLowPowerStandby; import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground; @@ -201,12 +205,12 @@ import android.os.Message; import android.os.MessageQueue.IdleHandler; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; +import android.os.PowerExemptionManager; import android.os.PowerExemptionManager.ReasonCode; import android.os.PowerManager; import android.os.PowerManager.ServiceType; import android.os.PowerManagerInternal; import android.os.PowerSaveState; -import android.os.PowerWhitelistManager; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -242,6 +246,7 @@ import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.util.SparseLongArray; import android.util.SparseSetArray; +import android.util.TimeUtils; import android.util.Xml; import com.android.internal.R; @@ -457,6 +462,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { */ private static final int MSG_UIDS_BLOCKED_REASONS_CHANGED = 23; + /** + * Message to update background restriction rules for uids that should lose network access + * due to being in the background. + */ + private static final int MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS = 24; + private static final int UID_MSG_STATE_CHANGED = 100; private static final int UID_MSG_GONE = 101; @@ -475,7 +486,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private ConnectivityManager mConnManager; private PowerManagerInternal mPowerManagerInternal; - private PowerWhitelistManager mPowerWhitelistManager; + private PowerExemptionManager mPowerExemptionManager; @NonNull private final Dependencies mDeps; @@ -490,6 +501,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // Denotes the status of restrict background read from disk. private boolean mLoadedRestrictBackground; + /** + * Whether or not network for apps in proc-states greater than + * {@link NetworkPolicyManager#BACKGROUND_THRESHOLD_STATE} is always blocked. + */ + private boolean mBackgroundNetworkRestricted; + // See main javadoc for instructions on how to use these locks. final Object mUidRulesFirstLock = new Object(); final Object mNetworkPoliciesSecondLock = new Object(); @@ -514,6 +531,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private volatile boolean mNetworkManagerReady; + /** + * Delay after which a uid going into a process state greater than or equal to + * {@link NetworkPolicyManager#BACKGROUND_THRESHOLD_STATE} will lose network access. + * The delay is meant to prevent churn due to quick process-state changes. + * Note that there is no delay while granting network access. + */ + @VisibleForTesting + long mBackgroundRestrictionDelayMs = TimeUnit.SECONDS.toMillis(5); + /** Defined network policies. */ @GuardedBy("mNetworkPoliciesSecondLock") final ArrayMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = new ArrayMap<>(); @@ -545,6 +571,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mUidRulesFirstLock") final SparseIntArray mUidFirewallPowerSaveRules = new SparseIntArray(); @GuardedBy("mUidRulesFirstLock") + final SparseIntArray mUidFirewallBackgroundRules = new SparseIntArray(); + @GuardedBy("mUidRulesFirstLock") final SparseIntArray mUidFirewallRestrictedModeRules = new SparseIntArray(); @GuardedBy("mUidRulesFirstLock") final SparseIntArray mUidFirewallLowPowerStandbyModeRules = new SparseIntArray(); @@ -624,6 +652,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mUidRulesFirstLock") private final SparseArray<UidBlockedState> mTmpUidBlockedState = new SparseArray<>(); + /** + * Stores a map of uids to the time their transition to background is considered complete. They + * will lose network access after this time. This is used to prevent churn in rules due to quick + * process-state transitions. + */ + @GuardedBy("mUidRulesFirstLock") + private final SparseLongArray mBackgroundTransitioningUids = new SparseLongArray(); + /** Map from network ID to last observed meteredness state */ @GuardedBy("mNetworkPoliciesSecondLock") private final SparseBooleanArray mNetworkMetered = new SparseBooleanArray(); @@ -823,7 +859,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mContext = Objects.requireNonNull(context, "missing context"); mActivityManager = Objects.requireNonNull(activityManager, "missing activityManager"); mNetworkManager = Objects.requireNonNull(networkManagement, "missing networkManagement"); - mPowerWhitelistManager = mContext.getSystemService(PowerWhitelistManager.class); + mPowerExemptionManager = mContext.getSystemService(PowerExemptionManager.class); mClock = Objects.requireNonNull(clock, "missing Clock"); mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class); @@ -859,15 +895,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mUidRulesFirstLock") private void updatePowerSaveAllowlistUL() { - int[] whitelist = mPowerWhitelistManager.getWhitelistedAppIds(/* includingIdle */ false); + int[] allowlist = mPowerExemptionManager.getAllowListedAppIds(/* includingIdle */ false); mPowerSaveWhitelistExceptIdleAppIds.clear(); - for (int uid : whitelist) { + for (int uid : allowlist) { mPowerSaveWhitelistExceptIdleAppIds.put(uid, true); } - whitelist = mPowerWhitelistManager.getWhitelistedAppIds(/* includingIdle */ true); + allowlist = mPowerExemptionManager.getAllowListedAppIds(/* includingIdle */ true); mPowerSaveWhitelistAppIds.clear(); - for (int uid : whitelist) { + for (int uid : allowlist) { mPowerSaveWhitelistAppIds.put(uid, true); } } @@ -1017,6 +1053,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { writePolicyAL(); } + // The flag is boot-stable. + mBackgroundNetworkRestricted = Flags.networkBlockedForTopSleepingAndAbove(); + if (mBackgroundNetworkRestricted) { + // Firewall rules and UidBlockedState will get updated in + // updateRulesForGlobalChangeAL below. + enableFirewallChainUL(FIREWALL_CHAIN_BACKGROUND, true); + } + setRestrictBackgroundUL(mLoadedRestrictBackground, "init_service"); updateRulesForGlobalChangeAL(false); updateNotificationsNL(); @@ -1027,17 +1071,22 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final int changes = ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_CAPABILITY; + + final int cutpoint = mBackgroundNetworkRestricted ? PROCESS_STATE_UNKNOWN + : NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE; + // TODO (b/319728914): Filter out the unnecessary changes when using no cutpoint. + mActivityManagerInternal.registerNetworkPolicyUidObserver(mUidObserver, changes, - NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE, "android"); + cutpoint, "android"); mNetworkManager.registerObserver(mAlertObserver); } catch (RemoteException e) { // ignored; both services live in system_server } // listen for changes to power save allowlist - final IntentFilter whitelistFilter = new IntentFilter( + final IntentFilter allowlistFilter = new IntentFilter( PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED); - mContext.registerReceiver(mPowerSaveWhitelistReceiver, whitelistFilter, null, mHandler); + mContext.registerReceiver(mPowerSaveAllowlistReceiver, allowlistFilter, null, mHandler); // watch for network interfaces to be claimed final IntentFilter connFilter = new IntentFilter(CONNECTIVITY_ACTION); @@ -1188,12 +1237,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - final private BroadcastReceiver mPowerSaveWhitelistReceiver = new BroadcastReceiver() { + private final BroadcastReceiver mPowerSaveAllowlistReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // on background handler thread, and POWER_SAVE_WHITELIST_CHANGED is protected synchronized (mUidRulesFirstLock) { updatePowerSaveAllowlistUL(); + if (mBackgroundNetworkRestricted) { + updateRulesForBackgroundChainUL(); + } updateRulesForRestrictPowerUL(); updateRulesForAppIdleUL(); } @@ -3901,6 +3953,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } fout.println(); + fout.println("Flags:"); + fout.println("Network blocked for TOP_SLEEPING and above: " + + mBackgroundNetworkRestricted); + + fout.println(); fout.println("mRestrictBackgroundLowPowerMode: " + mRestrictBackgroundLowPowerMode); fout.println("mRestrictBackgroundBeforeBsm: " + mRestrictBackgroundBeforeBsm); fout.println("mLoadedRestrictBackground: " + mLoadedRestrictBackground); @@ -4042,6 +4099,22 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { fout.decreaseIndent(); } + size = mBackgroundTransitioningUids.size(); + if (size > 0) { + final long nowUptime = SystemClock.uptimeMillis(); + fout.println("Uids transitioning to background:"); + fout.increaseIndent(); + for (int i = 0; i < size; i++) { + fout.print("UID="); + fout.print(mBackgroundTransitioningUids.keyAt(i)); + fout.print(", "); + TimeUtils.formatDuration(mBackgroundTransitioningUids.valueAt(i), nowUptime, + fout); + fout.println(); + } + fout.decreaseIndent(); + } + final SparseBooleanArray knownUids = new SparseBooleanArray(); collectKeys(mUidState, knownUids); synchronized (mUidBlockedState) { @@ -4163,6 +4236,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return isProcStateAllowedWhileInLowPowerStandby(uidState); } + @GuardedBy("mUidRulesFirstLock") + private boolean isUidExemptFromBackgroundRestrictions(int uid) { + return mBackgroundTransitioningUids.indexOfKey(uid) >= 0 + || isProcStateAllowedNetworkWhileBackground(mUidState.get(uid)); + } + /** * Process state of UID changed; if needed, will trigger * {@link #updateRulesForDataUsageRestrictionsUL(int)} and @@ -4188,6 +4267,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // state changed, push updated rules mUidState.put(uid, newUidState); updateRestrictBackgroundRulesOnUidStatusChangedUL(uid, oldUidState, newUidState); + + boolean updatePowerRestrictionRules = false; boolean allowedWhileIdleOrPowerSaveModeChanged = isProcStateAllowedWhileIdleOrPowerSaveMode(oldUidState) != isProcStateAllowedWhileIdleOrPowerSaveMode(newUidState); @@ -4199,19 +4280,44 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (mRestrictPower) { updateRuleForRestrictPowerUL(uid); } - updateRulesForPowerRestrictionsUL(uid, procState); + updatePowerRestrictionRules = true; + } + if (mBackgroundNetworkRestricted) { + final boolean wasAllowed = isProcStateAllowedNetworkWhileBackground( + oldUidState); + final boolean isAllowed = isProcStateAllowedNetworkWhileBackground(newUidState); + if (!wasAllowed && isAllowed) { + mBackgroundTransitioningUids.delete(uid); + updateRuleForBackgroundUL(uid); + updatePowerRestrictionRules = true; + } else if (wasAllowed && !isAllowed) { + final long completionTimeMs = SystemClock.uptimeMillis() + + mBackgroundRestrictionDelayMs; + if (mBackgroundTransitioningUids.indexOfKey(uid) < 0) { + // This is just a defensive check in case the upstream code ever makes + // multiple calls for the same process state change. + mBackgroundTransitioningUids.put(uid, completionTimeMs); + } + if (!mHandler.hasMessages(MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS)) { + // Many uids may be in this "transitioning" state at the same time, so + // using one message at a time to avoid congestion in the MessageQueue. + mHandler.sendEmptyMessageAtTime( + MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS, completionTimeMs); + } + } } if (mLowPowerStandbyActive) { boolean allowedInLpsChanged = isProcStateAllowedWhileInLowPowerStandby(oldUidState) != isProcStateAllowedWhileInLowPowerStandby(newUidState); if (allowedInLpsChanged) { - if (!allowedWhileIdleOrPowerSaveModeChanged) { - updateRulesForPowerRestrictionsUL(uid, procState); - } updateRuleForLowPowerStandbyUL(uid); + updatePowerRestrictionRules = true; } } + if (updatePowerRestrictionRules) { + updateRulesForPowerRestrictionsUL(uid, procState); + } return true; } } finally { @@ -4234,6 +4340,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (mRestrictPower) { updateRuleForRestrictPowerUL(uid); } + if (mBackgroundNetworkRestricted) { + // Uid is no longer running, there is no point in any grace period of network + // access during transitions to lower importance proc-states. + mBackgroundTransitioningUids.delete(uid); + updateRuleForBackgroundUL(uid); + } updateRulesForPowerRestrictionsUL(uid); if (mLowPowerStandbyActive) { updateRuleForLowPowerStandbyUL(uid); @@ -4441,11 +4553,41 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + /** + * Updates the rules for apps allowlisted to use network while in the background. + */ + @GuardedBy("mUidRulesFirstLock") + private void updateRulesForBackgroundChainUL() { + Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForBackgroundChainUL"); + try { + final SparseIntArray uidRules = mUidFirewallBackgroundRules; + uidRules.clear(); + + final List<UserInfo> users = mUserManager.getUsers(); + for (int ui = users.size() - 1; ui >= 0; ui--) { + final UserInfo user = users.get(ui); + updateRulesForAllowlistedAppIds(uidRules, mPowerSaveTempWhitelistAppIds, user.id); + updateRulesForAllowlistedAppIds(uidRules, mPowerSaveWhitelistAppIds, user.id); + updateRulesForAllowlistedAppIds(uidRules, mPowerSaveWhitelistExceptIdleAppIds, + user.id); + } + for (int i = mUidState.size() - 1; i >= 0; i--) { + if (mBackgroundTransitioningUids.indexOfKey(mUidState.keyAt(i)) >= 0 + || isProcStateAllowedNetworkWhileBackground(mUidState.valueAt(i))) { + uidRules.put(mUidState.keyAt(i), FIREWALL_RULE_ALLOW); + } + } + setUidFirewallRulesUL(FIREWALL_CHAIN_BACKGROUND, uidRules); + } finally { + Trace.traceEnd(TRACE_TAG_NETWORK); + } + } + private void updateRulesForAllowlistedAppIds(final SparseIntArray uidRules, - final SparseBooleanArray whitelistedAppIds, int userId) { - for (int i = whitelistedAppIds.size() - 1; i >= 0; --i) { - if (whitelistedAppIds.valueAt(i)) { - final int appId = whitelistedAppIds.keyAt(i); + final SparseBooleanArray allowlistedAppIds, int userId) { + for (int i = allowlistedAppIds.size() - 1; i >= 0; --i) { + if (allowlistedAppIds.valueAt(i)) { + final int appId = allowlistedAppIds.keyAt(i); final int uid = UserHandle.getUid(userId, appId); uidRules.put(uid, FIREWALL_RULE_ALLOW); } @@ -4504,12 +4646,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mUidRulesFirstLock") private boolean isAllowlistedFromPowerSaveUL(int uid, boolean deviceIdleMode) { final int appId = UserHandle.getAppId(uid); - boolean isWhitelisted = mPowerSaveTempWhitelistAppIds.get(appId) + boolean allowlisted = mPowerSaveTempWhitelistAppIds.get(appId) || mPowerSaveWhitelistAppIds.get(appId); if (!deviceIdleMode) { - isWhitelisted = isWhitelisted || isAllowlistedFromPowerSaveExceptIdleUL(uid); + allowlisted = allowlisted || isAllowlistedFromPowerSaveExceptIdleUL(uid); } - return isWhitelisted; + return allowlisted; } /** @@ -4598,6 +4740,38 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } /** + * Update firewall rule for a single uid whenever there are any interesting changes in the uid. + * Currently, it is called when: + * - The uid is added to or removed from power allowlists + * - The uid undergoes a process-state change + * - A package belonging to this uid is added + * - The uid is evicted from memory + */ + @GuardedBy("mUidRulesFirstLock") + void updateRuleForBackgroundUL(int uid) { + if (!isUidValidForAllowlistRulesUL(uid)) { + return; + } + + Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRuleForBackgroundUL: " + uid); + try { + // The uid should be absent from mUidState and mBackgroundTransitioningUids if it is + // not running when this method is called. Then, the firewall state will depend on the + // allowlist alone. This is the desired behavior. + if (isAllowlistedFromPowerSaveUL(uid, false) + || isUidExemptFromBackgroundRestrictions(uid)) { + setUidFirewallRuleUL(FIREWALL_CHAIN_BACKGROUND, uid, FIREWALL_RULE_ALLOW); + if (LOGD) Log.d(TAG, "updateRuleForBackgroundUL ALLOW " + uid); + } else { + setUidFirewallRuleUL(FIREWALL_CHAIN_BACKGROUND, uid, FIREWALL_RULE_DEFAULT); + if (LOGD) Log.d(TAG, "updateRuleForBackgroundUL " + uid + " to DEFAULT"); + } + } finally { + Trace.traceEnd(Trace.TRACE_TAG_NETWORK); + } + } + + /** * Toggle the firewall standby chain and inform listeners if the uid rules have effectively * changed. */ @@ -4644,6 +4818,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { "updateRulesForGlobalChangeAL: " + (restrictedNetworksChanged ? "R" : "-")); } try { + if (mBackgroundNetworkRestricted) { + updateRulesForBackgroundChainUL(); + } updateRulesForAppIdleUL(); updateRulesForRestrictPowerUL(); updateRulesForRestrictBackgroundUL(); @@ -4803,6 +4980,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateRuleForAppIdleUL(uid, PROCESS_STATE_UNKNOWN); updateRuleForDeviceIdleUL(uid); updateRuleForRestrictPowerUL(uid); + if (mBackgroundNetworkRestricted) { + updateRuleForBackgroundUL(uid); + } // Update internal rules. updateRulesForPowerRestrictionsUL(uid); } @@ -4940,6 +5120,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mUidFirewallStandbyRules.delete(uid); mUidFirewallDozableRules.delete(uid); mUidFirewallPowerSaveRules.delete(uid); + mUidFirewallBackgroundRules.delete(uid); + mBackgroundTransitioningUids.delete(uid); mPowerSaveWhitelistExceptIdleAppIds.delete(uid); mPowerSaveWhitelistAppIds.delete(uid); mPowerSaveTempWhitelistAppIds.delete(uid); @@ -4973,6 +5155,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateRuleForDeviceIdleUL(uid); updateRuleForAppIdleUL(uid, PROCESS_STATE_UNKNOWN); updateRuleForRestrictPowerUL(uid); + if (mBackgroundNetworkRestricted) { + updateRuleForBackgroundUL(uid); + } // If the uid has the necessary permissions, then it should be added to the restricted mode // firewall allowlist. @@ -5157,7 +5342,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * Similar to above but ignores idle state if app standby is currently disabled by parole. * * @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 */ @GuardedBy("mUidRulesFirstLock") @@ -5203,6 +5387,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { newBlockedReasons |= (mLowPowerStandbyActive ? BLOCKED_REASON_LOW_POWER_STANDBY : 0); newBlockedReasons |= (isUidIdle ? BLOCKED_REASON_APP_STANDBY : 0); newBlockedReasons |= (uidBlockedState.blockedReasons & BLOCKED_REASON_RESTRICTED_MODE); + newBlockedReasons |= mBackgroundNetworkRestricted ? BLOCKED_REASON_APP_BACKGROUND : 0; newAllowedReasons |= (isSystem(uid) ? ALLOWED_REASON_SYSTEM : 0); newAllowedReasons |= (isForeground ? ALLOWED_REASON_FOREGROUND : 0); @@ -5215,6 +5400,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { & ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS); newAllowedReasons |= (isAllowlistedFromLowPowerStandbyUL(uid)) ? ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST : 0; + newAllowedReasons |= (mBackgroundNetworkRestricted + && isUidExemptFromBackgroundRestrictions(uid)) + ? ALLOWED_REASON_NOT_IN_BACKGROUND : 0; uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons & BLOCKED_METERED_REASON_MASK) | newBlockedReasons; @@ -5236,7 +5424,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { oldEffectiveBlockedReasons = previousUidBlockedState.effectiveBlockedReasons; newEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons; - uidRules = oldEffectiveBlockedReasons == newEffectiveBlockedReasons + uidRules = (oldEffectiveBlockedReasons == newEffectiveBlockedReasons) ? RULE_NONE : uidBlockedState.deriveUidRules(); } @@ -5429,6 +5617,28 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mContext.sendBroadcastAsUser(intent, UserHandle.ALL); return true; } + case MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS: { + final long now = SystemClock.uptimeMillis(); + long nextCheckTime = Long.MAX_VALUE; + synchronized (mUidRulesFirstLock) { + for (int i = mBackgroundTransitioningUids.size() - 1; i >= 0; i--) { + final long completionTimeMs = mBackgroundTransitioningUids.valueAt(i); + if (completionTimeMs > now) { + nextCheckTime = Math.min(nextCheckTime, completionTimeMs); + continue; + } + final int uid = mBackgroundTransitioningUids.keyAt(i); + mBackgroundTransitioningUids.removeAt(i); + updateRuleForBackgroundUL(uid); + updateRulesForPowerRestrictionsUL(uid, false); + } + } + if (nextCheckTime < Long.MAX_VALUE) { + mHandler.sendEmptyMessageAtTime(MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS, + nextCheckTime); + } + return true; + } case MSG_POLICIES_CHANGED: { final int uid = msg.arg1; final int policy = msg.arg2; @@ -5840,6 +6050,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mUidFirewallRestrictedModeRules.put(uid, rule); } else if (chain == FIREWALL_CHAIN_LOW_POWER_STANDBY) { mUidFirewallLowPowerStandbyModeRules.put(uid, rule); + } else if (chain == FIREWALL_CHAIN_BACKGROUND) { + mUidFirewallBackgroundRules.put(uid, rule); } try { @@ -5896,6 +6108,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { FIREWALL_RULE_DEFAULT); mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_LOW_POWER_STANDBY, uid, FIREWALL_RULE_DEFAULT); + mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, uid, + FIREWALL_RULE_DEFAULT); mNetworkManager.setUidOnMeteredNetworkAllowlist(uid, false); mLogger.meteredAllowlistChanged(uid, false); mNetworkManager.setUidOnMeteredNetworkDenylist(uid, false); @@ -6420,10 +6634,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { effectiveBlockedReasons &= ~BLOCKED_REASON_BATTERY_SAVER; effectiveBlockedReasons &= ~BLOCKED_REASON_DOZE; effectiveBlockedReasons &= ~BLOCKED_REASON_APP_STANDBY; + effectiveBlockedReasons &= ~BLOCKED_REASON_APP_BACKGROUND; } if ((allowedReasons & ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST) != 0) { effectiveBlockedReasons &= ~BLOCKED_REASON_BATTERY_SAVER; effectiveBlockedReasons &= ~BLOCKED_REASON_APP_STANDBY; + effectiveBlockedReasons &= ~BLOCKED_REASON_APP_BACKGROUND; } if ((allowedReasons & ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS) != 0) { effectiveBlockedReasons &= ~BLOCKED_REASON_RESTRICTED_MODE; @@ -6434,19 +6650,24 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if ((allowedReasons & ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST) != 0) { effectiveBlockedReasons &= ~BLOCKED_REASON_LOW_POWER_STANDBY; } + if ((allowedReasons & ALLOWED_REASON_NOT_IN_BACKGROUND) != 0) { + effectiveBlockedReasons &= ~BLOCKED_REASON_APP_BACKGROUND; + } return effectiveBlockedReasons; } static int getAllowedReasonsForProcState(int procState) { - if (procState > NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE) { - return ALLOWED_REASON_NONE; - } else if (procState <= NetworkPolicyManager.TOP_THRESHOLD_STATE) { + if (procState <= NetworkPolicyManager.TOP_THRESHOLD_STATE) { return ALLOWED_REASON_TOP | ALLOWED_REASON_FOREGROUND - | ALLOWED_METERED_REASON_FOREGROUND; - } else { - return ALLOWED_REASON_FOREGROUND | ALLOWED_METERED_REASON_FOREGROUND; + | ALLOWED_METERED_REASON_FOREGROUND | ALLOWED_REASON_NOT_IN_BACKGROUND; + } else if (procState <= NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE) { + return ALLOWED_REASON_FOREGROUND | ALLOWED_METERED_REASON_FOREGROUND + | ALLOWED_REASON_NOT_IN_BACKGROUND; + } else if (procState < NetworkPolicyManager.BACKGROUND_THRESHOLD_STATE) { + return ALLOWED_REASON_NOT_IN_BACKGROUND; } + return ALLOWED_REASON_NONE; } @Override @@ -6471,6 +6692,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { BLOCKED_REASON_APP_STANDBY, BLOCKED_REASON_RESTRICTED_MODE, BLOCKED_REASON_LOW_POWER_STANDBY, + BLOCKED_REASON_APP_BACKGROUND, BLOCKED_METERED_REASON_DATA_SAVER, BLOCKED_METERED_REASON_USER_RESTRICTED, BLOCKED_METERED_REASON_ADMIN_DISABLED, @@ -6484,6 +6706,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST, ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS, ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST, + ALLOWED_REASON_NOT_IN_BACKGROUND, ALLOWED_METERED_REASON_USER_EXEMPTED, ALLOWED_METERED_REASON_SYSTEM, ALLOWED_METERED_REASON_FOREGROUND, @@ -6503,6 +6726,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return "RESTRICTED_MODE"; case BLOCKED_REASON_LOW_POWER_STANDBY: return "LOW_POWER_STANDBY"; + case BLOCKED_REASON_APP_BACKGROUND: + return "APP_BACKGROUND"; case BLOCKED_METERED_REASON_DATA_SAVER: return "DATA_SAVER"; case BLOCKED_METERED_REASON_USER_RESTRICTED: @@ -6533,6 +6758,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return "RESTRICTED_MODE_PERMISSIONS"; case ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST: return "LOW_POWER_STANDBY_ALLOWLIST"; + case ALLOWED_REASON_NOT_IN_BACKGROUND: + return "NOT_IN_BACKGROUND"; case ALLOWED_METERED_REASON_USER_EXEMPTED: return "METERED_USER_EXEMPTED"; case ALLOWED_METERED_REASON_SYSTEM: @@ -6600,7 +6827,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { int powerBlockedReasons = BLOCKED_REASON_APP_STANDBY | BLOCKED_REASON_DOZE | BLOCKED_REASON_BATTERY_SAVER - | BLOCKED_REASON_LOW_POWER_STANDBY; + | BLOCKED_REASON_LOW_POWER_STANDBY + | BLOCKED_REASON_APP_BACKGROUND; if ((effectiveBlockedReasons & powerBlockedReasons) != 0) { uidRule |= RULE_REJECT_ALL; } else if ((blockedReasons & powerBlockedReasons) != 0) { diff --git a/services/core/java/com/android/server/net/flags.aconfig b/services/core/java/com/android/server/net/flags.aconfig new file mode 100644 index 000000000000..419665a0a5ab --- /dev/null +++ b/services/core/java/com/android/server/net/flags.aconfig @@ -0,0 +1,8 @@ +package: "com.android.server.net" + +flag { + name: "network_blocked_for_top_sleeping_and_above" + namespace: "backstage_power" + description: "Block network access for apps in a low importance background state" + bug: "304347838" +} diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 27fd8a66fff1..5cce1c2f6234 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -68,6 +68,7 @@ android_test { "coretests-aidl", "securebox", "flag-junit", + "net_flags_lib", ], libs: [ diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java index 13dc12032e7d..d6d2b6d9abd2 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkManagementServiceTest.java @@ -16,6 +16,7 @@ package com.android.server.net; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND; import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE; import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY; import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE; @@ -327,12 +328,20 @@ public class NetworkManagementServiceTest { isRestrictedForLowPowerStandby.put(INetd.FIREWALL_RULE_DENY, true); expected.put(FIREWALL_CHAIN_LOW_POWER_STANDBY, isRestrictedForLowPowerStandby); + // Background chain + final ArrayMap<Integer, Boolean> isRestrictedInBackground = new ArrayMap<>(); + isRestrictedInBackground.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, true); + isRestrictedInBackground.put(INetd.FIREWALL_RULE_ALLOW, false); + isRestrictedInBackground.put(INetd.FIREWALL_RULE_DENY, true); + expected.put(FIREWALL_CHAIN_BACKGROUND, isRestrictedInBackground); + final int[] chains = { FIREWALL_CHAIN_STANDBY, FIREWALL_CHAIN_POWERSAVE, FIREWALL_CHAIN_DOZABLE, FIREWALL_CHAIN_RESTRICTED, - FIREWALL_CHAIN_LOW_POWER_STANDBY + FIREWALL_CHAIN_LOW_POWER_STANDBY, + FIREWALL_CHAIN_BACKGROUND }; final int[] states = { INetd.FIREWALL_RULE_ALLOW, diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index 2a764526a436..4451cae8db42 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -26,12 +26,14 @@ import static android.app.ActivityManager.PROCESS_STATE_SERVICE; import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER; import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED; +import static android.net.ConnectivityManager.BLOCKED_REASON_APP_BACKGROUND; import static android.net.ConnectivityManager.BLOCKED_REASON_APP_STANDBY; import static android.net.ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER; import static android.net.ConnectivityManager.BLOCKED_REASON_DOZE; import static android.net.ConnectivityManager.BLOCKED_REASON_LOW_POWER_STANDBY; import static android.net.ConnectivityManager.BLOCKED_REASON_NONE; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; +import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND; import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY; import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED; import static android.net.ConnectivityManager.TYPE_MOBILE; @@ -48,8 +50,13 @@ import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_SYSTEM; import static android.net.NetworkPolicyManager.ALLOWED_REASON_FOREGROUND; import static android.net.NetworkPolicyManager.ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST; import static android.net.NetworkPolicyManager.ALLOWED_REASON_NONE; +import static android.net.NetworkPolicyManager.ALLOWED_REASON_NOT_IN_BACKGROUND; +import static android.net.NetworkPolicyManager.ALLOWED_REASON_POWER_SAVE_ALLOWLIST; +import static android.net.NetworkPolicyManager.ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST; +import static android.net.NetworkPolicyManager.ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS; import static android.net.NetworkPolicyManager.ALLOWED_REASON_SYSTEM; import static android.net.NetworkPolicyManager.ALLOWED_REASON_TOP; +import static android.net.NetworkPolicyManager.BACKGROUND_THRESHOLD_STATE; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND; import static android.net.NetworkPolicyManager.POLICY_NONE; @@ -64,6 +71,7 @@ import static android.net.NetworkStats.METERED_YES; import static android.net.NetworkTemplate.MATCH_CARRIER; import static android.net.NetworkTemplate.MATCH_MOBILE; import static android.net.NetworkTemplate.MATCH_WIFI; +import static android.os.PowerExemptionManager.REASON_OTHER; import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED; import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED; import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT; @@ -146,6 +154,8 @@ import android.os.Build; import android.os.Handler; import android.os.INetworkManagementService; import android.os.PersistableBundle; +import android.os.PowerExemptionManager; +import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.RemoteException; @@ -153,6 +163,9 @@ import android.os.SimpleClock; import android.os.UserHandle; import android.os.UserManager; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; @@ -169,6 +182,7 @@ import android.util.Pair; import android.util.Range; import android.util.RecurrenceRule; import android.util.SparseArray; +import android.util.SparseIntArray; import androidx.test.InstrumentationRegistry; import androidx.test.filters.FlakyTest; @@ -243,6 +257,9 @@ import java.util.stream.Collectors; public class NetworkPolicyManagerServiceTest { private static final String TAG = "NetworkPolicyManagerServiceTest"; + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + private static final long TEST_START = 1194220800000L; private static final String TEST_IFACE = "test0"; private static final String TEST_WIFI_NETWORK_KEY = "TestWifiNetworkKey"; @@ -285,6 +302,7 @@ public class NetworkPolicyManagerServiceTest { private @Mock TelephonyManager mTelephonyManager; private @Mock UserManager mUserManager; private @Mock NetworkStatsManager mStatsManager; + private @Mock PowerExemptionManager mPowerExemptionManager; private TestDependencies mDeps; private ArgumentCaptor<ConnectivityManager.NetworkCallback> mNetworkCallbackCaptor = @@ -302,6 +320,7 @@ public class NetworkPolicyManagerServiceTest { private NetworkPolicyManagerService mService; private final ArraySet<BroadcastReceiver> mRegisteredReceivers = new ArraySet<>(); + private BroadcastReceiver mPowerAllowlistReceiver; /** * In some of the tests while initializing NetworkPolicyManagerService, @@ -446,6 +465,7 @@ public class NetworkPolicyManagerServiceTest { @Before public void callSystemReady() throws Exception { MockitoAnnotations.initMocks(this); + when(mPowerExemptionManager.getAllowListedAppIds(anyBoolean())).thenReturn(new int[0]); final Context context = InstrumentationRegistry.getContext(); @@ -482,6 +502,8 @@ public class NetworkPolicyManagerServiceTest { return mUserManager; case Context.NETWORK_STATS_SERVICE: return mStatsManager; + case Context.POWER_EXEMPTION_SERVICE: + return mPowerExemptionManager; default: return super.getSystemService(name); } @@ -495,6 +517,9 @@ public class NetworkPolicyManagerServiceTest { @Override public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler) { + if (filter.hasAction(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED)) { + mPowerAllowlistReceiver = receiver; + } mRegisteredReceivers.add(receiver); return super.registerReceiver(receiver, filter, broadcastPermission, scheduler); } @@ -2066,6 +2091,12 @@ public class NetworkPolicyManagerServiceTest { expectHasUseRestrictedNetworksPermission(UID_A, true); expectHasUseRestrictedNetworksPermission(UID_B, false); + // Set low enough proc-states to ensure these uids are allowed in the background chain. + // To maintain clean separation between separate firewall chains, the tests could + // check for the specific blockedReasons in the uidBlockedState. + callAndWaitOnUidStateChanged(UID_A, BACKGROUND_THRESHOLD_STATE - 1, 21); + callAndWaitOnUidStateChanged(UID_B, BACKGROUND_THRESHOLD_STATE - 1, 21); + Map<Integer, Integer> firewallUidRules = new ArrayMap<>(); doAnswer(arg -> { int[] uids = arg.getArgument(1); @@ -2113,7 +2144,111 @@ public class NetworkPolicyManagerServiceTest { } @Test + @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) + public void testBackgroundChainEnabled() throws Exception { + verify(mNetworkManager).setFirewallChainEnabled(FIREWALL_CHAIN_BACKGROUND, true); + } + + + @Test + @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) + public void testBackgroundChainOnProcStateChange() throws Exception { + // initialization calls setFirewallChainEnabled, so we want to reset the invocations. + clearInvocations(mNetworkManager); + + mService.mBackgroundRestrictionDelayMs = 500; // To avoid waiting too long in tests. + + // The app will be blocked when there is no prior proc-state. + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + + int procStateSeq = 23; + callAndWaitOnUidStateChanged(UID_A, BACKGROUND_THRESHOLD_STATE - 1, procStateSeq++); + + verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A, + FIREWALL_RULE_ALLOW); + assertFalse(mService.isUidNetworkingBlocked(UID_A, false)); + + callAndWaitOnUidStateChanged(UID_A, BACKGROUND_THRESHOLD_STATE + 1, procStateSeq++); + + // The app should be blocked after a delay. Posting a message just after the delay and + // waiting for it to complete to ensure that the blocking code has executed. + waitForDelayedMessageOnHandler(mService.mBackgroundRestrictionDelayMs + 1); + + verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A, + FIREWALL_RULE_DEFAULT); + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) + public void testBackgroundChainOnAllowlistChange() throws Exception { + // initialization calls setFirewallChainEnabled, so we want to reset the invocations. + clearInvocations(mNetworkManager); + + // The apps will be blocked when there is no prior proc-state. + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + assertTrue(mService.isUidNetworkingBlocked(UID_B, false)); + + final int procStateSeq = 29; + callAndWaitOnUidStateChanged(UID_A, BACKGROUND_THRESHOLD_STATE + 1, procStateSeq); + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + + when(mPowerExemptionManager.getAllowListedAppIds(anyBoolean())) + .thenReturn(new int[]{APP_ID_A, APP_ID_B}); + final SparseIntArray firewallUidRules = new SparseIntArray(); + doAnswer(arg -> { + final int[] uids = arg.getArgument(1); + final int[] rules = arg.getArgument(2); + assertTrue(uids.length == rules.length); + + for (int i = 0; i < uids.length; ++i) { + firewallUidRules.put(uids[i], rules[i]); + } + return null; + }).when(mNetworkManager).setFirewallUidRules(eq(FIREWALL_CHAIN_BACKGROUND), + any(int[].class), any(int[].class)); + + mPowerAllowlistReceiver.onReceive(mServiceContext, null); + + assertEquals(FIREWALL_RULE_ALLOW, firewallUidRules.get(UID_A, -1)); + assertEquals(FIREWALL_RULE_ALLOW, firewallUidRules.get(UID_B, -1)); + + assertFalse(mService.isUidNetworkingBlocked(UID_A, false)); + assertFalse(mService.isUidNetworkingBlocked(UID_B, false)); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) + public void testBackgroundChainOnTempAllowlistChange() throws Exception { + // initialization calls setFirewallChainEnabled, so we want to reset the invocations. + clearInvocations(mNetworkManager); + + // The app will be blocked as is no prior proc-state. + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + + final int procStateSeq = 19; + callAndWaitOnUidStateChanged(UID_A, BACKGROUND_THRESHOLD_STATE + 1, procStateSeq); + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + + final NetworkPolicyManagerInternal internal = LocalServices.getService( + NetworkPolicyManagerInternal.class); + + internal.onTempPowerSaveWhitelistChange(APP_ID_A, true, REASON_OTHER, "testing"); + + verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A, + FIREWALL_RULE_ALLOW); + assertFalse(mService.isUidNetworkingBlocked(UID_A, false)); + + internal.onTempPowerSaveWhitelistChange(APP_ID_A, false, REASON_OTHER, "testing"); + + verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A, + FIREWALL_RULE_DEFAULT); + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + } + + @Test public void testLowPowerStandbyAllowlist() throws Exception { + // Chain background is also enabled but these procstates are important enough to be exempt. callAndWaitOnUidStateChanged(UID_A, PROCESS_STATE_TOP, 0); callAndWaitOnUidStateChanged(UID_B, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0); callAndWaitOnUidStateChanged(UID_C, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0); @@ -2200,7 +2335,21 @@ public class NetworkPolicyManagerServiceTest { ALLOWED_REASON_TOP), BLOCKED_REASON_NONE); effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_LOW_POWER_STANDBY, ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST), BLOCKED_REASON_NONE); - // TODO: test more combinations of blocked reasons. + + effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_APP_BACKGROUND, + ALLOWED_REASON_NOT_IN_BACKGROUND), BLOCKED_REASON_NONE); + effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_APP_BACKGROUND + | BLOCKED_REASON_BATTERY_SAVER, ALLOWED_REASON_NOT_IN_BACKGROUND), + BLOCKED_REASON_BATTERY_SAVER); + effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_APP_BACKGROUND + | BLOCKED_REASON_DOZE, ALLOWED_REASON_NOT_IN_BACKGROUND), + BLOCKED_REASON_DOZE); + effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_APP_BACKGROUND, + ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS), BLOCKED_REASON_APP_BACKGROUND); + effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_APP_BACKGROUND, + ALLOWED_REASON_POWER_SAVE_ALLOWLIST), BLOCKED_REASON_NONE); + effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_APP_BACKGROUND, + ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST), BLOCKED_REASON_NONE); for (Map.Entry<Pair<Integer, Integer>, Integer> test : effectiveBlockedReasons.entrySet()) { final int expectedEffectiveBlockedReasons = test.getValue(); @@ -2529,7 +2678,6 @@ public class NetworkPolicyManagerServiceTest { private FutureIntent mRestrictBackgroundChanged; private void postMsgAndWaitForCompletion() throws InterruptedException { - final Handler handler = mService.getHandlerForTesting(); final CountDownLatch latch = new CountDownLatch(1); mService.getHandlerForTesting().post(latch::countDown); if (!latch.await(5, TimeUnit.SECONDS)) { @@ -2537,6 +2685,14 @@ public class NetworkPolicyManagerServiceTest { } } + private void waitForDelayedMessageOnHandler(long delayMs) throws InterruptedException { + final CountDownLatch latch = new CountDownLatch(1); + mService.getHandlerForTesting().postDelayed(latch::countDown, delayMs); + if (!latch.await(delayMs + 5_000, TimeUnit.MILLISECONDS)) { + fail("Timed out waiting for delayed msg to be handled"); + } + } + private void setSubscriptionPlans(int subId, SubscriptionPlan[] plans, String callingPackage) throws InterruptedException { mService.setSubscriptionPlans(subId, plans, 0, callingPackage); |