From 048bc5ea80b0b7b75988df2362945ea7cff59ed7 Mon Sep 17 00:00:00 2001 From: Patrick Rohr <prohr@google.com> Date: Fri, 23 Oct 2020 13:51:00 +0200 Subject: [PATCH] Add Restricted Mode handling to NetworkPolicyManager Adds Restricted Mode functionality to NetworkPolicyManager. When this mode is turned on (via setting), only apps with android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS will be able to use the network. For all other apps, the network will be blocked by the firewall. This is controlled by a new allowlist firewall chain fw_restricted_mode. As a first step, this implementation still requires a reboot after the enabling / disabling the mode to take effect. I will provide the dynamic configuration in the next CL. Test: atest CtsHostsideNetworkTests && atest NetworkPolicyManagerServiceTest Bug: 170322816 Bug: 157505406 Bug: 170322455 Bug: 175281879 Exempt-From-Owner-Approval: Change already merged on internal gerrit. Change-Id: I0731fa842c69683953baaf9ec3a9a03454f4c607 Merged-In: I0731fa842c69683953baaf9ec3a9a03454f4c607 --- .../android/net/NetworkPolicyManager.java | 13 +- core/java/android/provider/Settings.java | 11 ++ .../validators/GlobalSettingsValidators.java | 1 + .../android/provider/SettingsBackupTest.java | 1 + .../server/net/NetworkPolicyLogger.java | 3 + .../net/NetworkPolicyManagerService.java | 139 ++++++++++++++++-- .../net/NetworkPolicyManagerServiceTest.java | 109 +++++++++++++- 7 files changed, 256 insertions(+), 21 deletions(-) diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index ce16a7835179..36348b365158 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -122,17 +122,26 @@ public class NetworkPolicyManager { * @hide */ public static final int RULE_REJECT_ALL = 1 << 6; + /** + * Reject traffic on all networks for restricted networking mode. + */ + public static final int RULE_REJECT_RESTRICTED_MODE = 1 << 10; /** * Mask used to get the {@code RULE_xxx_METERED} rules * @hide */ - public static final int MASK_METERED_NETWORKS = 0b00001111; + public static final int MASK_METERED_NETWORKS = 0b000000001111; /** * Mask used to get the {@code RULE_xxx_ALL} rules * @hide */ - public static final int MASK_ALL_NETWORKS = 0b11110000; + public static final int MASK_ALL_NETWORKS = 0b000011110000; + /** + * Mask used to get the {@code RULE_xxx_RESTRICTED_MODE} rules + * @hide + */ + public static final int MASK_RESTRICTED_MODE_NETWORKS = 0b111100000000; /** @hide */ public static final int FIREWALL_RULE_DEFAULT = 0; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 1e14e3a4a701..4086161603a4 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -14406,6 +14406,17 @@ public final class Settings { */ public static final String NR_NSA_TRACKING_SCREEN_OFF_MODE = "nr_nsa_tracking_screen_off_mode"; + + /** + * Used to enable / disable the Restricted Networking Mode in which network access is + * restricted to apps holding the CONNECTIVITY_USE_RESTRICTED_NETWORKS permission. + * + * Values are: + * 0: disabled + * 1: enabled + * @hide + */ + public static final String RESTRICTED_NETWORKING_MODE = "restricted_networking_mode"; } /** diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java index dd94d2eb8fe0..c559678d4005 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java @@ -149,5 +149,6 @@ public class GlobalSettingsValidators { VALIDATORS.put(Global.CUSTOM_BUGREPORT_HANDLER_APP, ANY_STRING_VALIDATOR); VALIDATORS.put(Global.CUSTOM_BUGREPORT_HANDLER_USER, ANY_INTEGER_VALIDATOR); VALIDATORS.put(Global.DEVELOPMENT_SETTINGS_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put(Global.RESTRICTED_NETWORKING_MODE, BOOLEAN_VALIDATOR); } } diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 5ecb17102438..1345c3f071c7 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -420,6 +420,7 @@ public class SettingsBackupTest { Settings.Global.RADIO_WIMAX, Settings.Global.RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS, Settings.Global.READ_EXTERNAL_STORAGE_ENFORCED_DEFAULT, + Settings.Global.RESTRICTED_NETWORKING_MODE, Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT, Settings.Global.SAFE_BOOT_DISALLOWED, Settings.Global.SELINUX_STATUS, diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java index 5bd352c8f8e8..676f4218f745 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java +++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java @@ -78,6 +78,7 @@ public class NetworkPolicyLogger { 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); @@ -281,6 +282,8 @@ public class NetworkPolicyLogger { 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); } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index a039215487f8..fc94ebaa96b4 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -18,6 +18,7 @@ package com.android.server.net; import static android.Manifest.permission.ACCESS_NETWORK_STATE; import static android.Manifest.permission.CONNECTIVITY_INTERNAL; +import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; import static android.Manifest.permission.MANAGE_NETWORK_POLICY; import static android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS; import static android.Manifest.permission.NETWORK_SETTINGS; @@ -44,6 +45,7 @@ import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELI import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.INetd.FIREWALL_CHAIN_DOZABLE; import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE; +import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED; import static android.net.INetd.FIREWALL_CHAIN_STANDBY; import static android.net.INetd.FIREWALL_RULE_ALLOW; import static android.net.INetd.FIREWALL_RULE_DENY; @@ -57,6 +59,7 @@ 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; @@ -65,12 +68,14 @@ 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; +import static android.net.NetworkPolicyManager.RULE_REJECT_RESTRICTED_MODE; import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED; import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode; import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground; import static android.net.NetworkPolicyManager.resolveNetworkId; import static android.net.NetworkPolicyManager.uidPoliciesToString; import static android.net.NetworkPolicyManager.uidRulesToString; +import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; import static android.net.NetworkTemplate.MATCH_MOBILE; import static android.net.NetworkTemplate.MATCH_WIFI; import static android.net.NetworkTemplate.buildTemplateMobileAll; @@ -111,6 +116,7 @@ import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_TMP_ALLOWL 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; @@ -445,7 +451,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictPower; @GuardedBy("mUidRulesFirstLock") volatile boolean mDeviceIdleMode; // Store whether user flipped restrict background in battery saver mode - @GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictBackgroundChangedInBsm; + @GuardedBy("mUidRulesFirstLock") + volatile boolean mRestrictBackgroundChangedInBsm; + @GuardedBy("mUidRulesFirstLock") + volatile boolean mRestrictedNetworkingMode; private final boolean mSuppressDefaultPolicy; @@ -479,6 +488,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final SparseIntArray mUidFirewallDozableRules = new SparseIntArray(); @GuardedBy("mUidRulesFirstLock") final SparseIntArray mUidFirewallPowerSaveRules = new SparseIntArray(); + @GuardedBy("mUidRulesFirstLock") + final SparseIntArray mUidFirewallRestrictedModeRules = new SparseIntArray(); /** Set of states for the child firewall chains. True if the chain is active. */ @GuardedBy("mUidRulesFirstLock") @@ -786,6 +797,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mRestrictPower = mPowerManagerInternal.getLowPowerState( ServiceType.NETWORK_FIREWALL).batterySaverEnabled; + mRestrictedNetworkingMode = Settings.Global.getInt( + mContext.getContentResolver(), + Settings.Global.RESTRICTED_NETWORKING_MODE, 0) != 0; + mSystemReady = true; waitForAdminData(); @@ -3501,6 +3516,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { fout.print("Restrict background: "); fout.println(mRestrictBackground); fout.print("Restrict power: "); fout.println(mRestrictPower); fout.print("Device idle: "); fout.println(mDeviceIdleMode); + fout.print("Restricted networking mode: "); fout.println(mRestrictedNetworkingMode); synchronized (mMeteredIfacesLock) { fout.print("Metered ifaces: "); fout.println(mMeteredIfaces); @@ -3812,6 +3828,93 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + /** + * updates restricted mode state / access for all apps + * Called on initialization and when restricted mode is enabled / disabled. + */ + @VisibleForTesting + @GuardedBy("mUidRulesFirstLock") + 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); + + // 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(); + } + }); + if (mRestrictedNetworkingMode) { + // firewall rules only need to be set when this mode is being enabled. + setUidFirewallRulesUL(FIREWALL_CHAIN_RESTRICTED, mUidFirewallRestrictedModeRules); + } + enableFirewallChainUL(FIREWALL_CHAIN_RESTRICTED, mRestrictedNetworkingMode); + } + + // updates restricted mode state / access for a single app / uid. + @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(); + } + + // 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. + if (mRestrictedNetworkingMode) { + // Note: setUidFirewallRule also updates mUidFirewallRestrictedModeRules. + // In this case, default firewall rules can also be added. + setUidFirewallRule(FIREWALL_CHAIN_RESTRICTED, uid, + getRestrictedModeFirewallRule(newUidRule)); + } + } + + 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) { + // rejected in restricted mode, this is the default behavior. + return FIREWALL_RULE_DEFAULT; + } else { + return FIREWALL_RULE_ALLOW; + } + } + + private boolean hasRestrictedModeAccess(int uid) { + try { + // TODO: this needs to be kept in sync with + // PermissionMonitor#hasRestrictedNetworkPermission + return mIPm.checkUidPermission(CONNECTIVITY_USE_RESTRICTED_NETWORKS, uid) + == PERMISSION_GRANTED + || mIPm.checkUidPermission(NETWORK_STACK, uid) == PERMISSION_GRANTED + || mIPm.checkUidPermission(PERMISSION_MAINLINE_NETWORK_STACK, uid) + == PERMISSION_GRANTED; + } catch (RemoteException e) { + return false; + } + } + @GuardedBy("mUidRulesFirstLock") void updateRulesForPowerSaveUL() { Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForPowerSaveUL"); @@ -4033,6 +4136,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateRulesForAppIdleUL(); updateRulesForRestrictPowerUL(); updateRulesForRestrictBackgroundUL(); + updateRestrictedModeAllowlistUL(); // If the set of restricted networks may have changed, re-evaluate those. if (restrictedNetworksChanged) { @@ -4249,6 +4353,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mPowerSaveWhitelistAppIds.delete(uid); mPowerSaveTempWhitelistAppIds.delete(uid); mAppIdleTempWhitelistAppIds.delete(uid); + mUidFirewallRestrictedModeRules.delete(uid); // ...then update iptables asynchronously. mHandler.obtainMessage(MSG_RESET_FIREWALL_RULES_BY_UID, uid, 0).sendToTarget(); @@ -4274,6 +4379,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateRuleForAppIdleUL(uid); updateRuleForRestrictPowerUL(uid); + // If the uid has the necessary permissions, then it should be added to the restricted mode + // firewall allowlist. + updateRestrictedModeForUidUL(uid); + // Update internal state for power-related modes. updateRulesForPowerRestrictionsUL(uid); @@ -4999,6 +5108,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mUidFirewallStandbyRules.put(uid, rule); } else if (chain == FIREWALL_CHAIN_POWERSAVE) { mUidFirewallPowerSaveRules.put(uid, rule); + } else if (chain == FIREWALL_CHAIN_RESTRICTED) { + mUidFirewallRestrictedModeRules.put(uid, rule); } try { @@ -5044,6 +5155,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT); mNetworkManager .setFirewallUidRule(FIREWALL_CHAIN_POWERSAVE, uid, FIREWALL_RULE_DEFAULT); + mNetworkManager + .setFirewallUidRule(FIREWALL_CHAIN_RESTRICTED, uid, FIREWALL_RULE_DEFAULT); mNetworkManager.setUidOnMeteredNetworkAllowlist(uid, false); mNetworkManager.setUidOnMeteredNetworkDenylist(uid, false); } catch (IllegalStateException e) { @@ -5230,26 +5343,21 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // Networks are never blocked for system components if (isSystem(uid)) { reason = NTWK_ALLOWED_SYSTEM; - } - else if (hasRule(uidRules, RULE_REJECT_ALL)) { + } 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) { + } else if (!isNetworkMetered) { reason = NTWK_ALLOWED_NON_METERED; - } - else if (hasRule(uidRules, RULE_REJECT_METERED)) { + } else if (hasRule(uidRules, RULE_REJECT_METERED)) { reason = NTWK_BLOCKED_DENYLIST; - } - else if (hasRule(uidRules, RULE_ALLOW_METERED)) { + } else if (hasRule(uidRules, RULE_ALLOW_METERED)) { reason = NTWK_ALLOWED_ALLOWLIST; - } - else if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) { + } else if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) { reason = NTWK_ALLOWED_TMP_ALLOWLIST; - } - else if (isBackgroundRestricted) { + } else if (isBackgroundRestricted) { reason = NTWK_BLOCKED_BG_RESTRICT; - } - else { + } else { reason = NTWK_ALLOWED_DEFAULT; } @@ -5262,6 +5370,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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: 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 fec0273383e7..4db7ce2e6ef5 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -16,13 +16,18 @@ package com.android.server.net; +import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; +import static android.Manifest.permission.NETWORK_STACK; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.TYPE_WIFI; +import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED; +import static android.net.INetd.FIREWALL_RULE_ALLOW; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.net.NetworkPolicy.SNOOZE_NEVER; import static android.net.NetworkPolicy.WARNING_DISABLED; +import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; 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; @@ -34,6 +39,7 @@ import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED; import static android.net.NetworkPolicyManager.uidPoliciesToString; import static android.net.NetworkPolicyManager.uidRulesToString; +import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.SET_ALL; import static android.net.NetworkStats.TAG_ALL; @@ -74,6 +80,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; @@ -97,6 +104,7 @@ import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.Signature; +import android.content.pm.UserInfo; import android.net.ConnectivityManager; import android.net.IConnectivityManager; import android.net.INetworkManagementEventObserver; @@ -123,6 +131,7 @@ import android.os.RemoteException; import android.os.SimpleClock; import android.os.SystemClock; import android.os.UserHandle; +import android.os.UserManager; import android.platform.test.annotations.Presubmit; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionInfo; @@ -131,6 +140,7 @@ import android.telephony.SubscriptionPlan; import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.MediumTest; import android.text.TextUtils; +import android.util.ArrayMap; import android.util.DataUnit; import android.util.Log; import android.util.Pair; @@ -187,6 +197,7 @@ import java.util.Calendar; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.TimeZone; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; @@ -240,6 +251,7 @@ public class NetworkPolicyManagerServiceTest { private @Mock SubscriptionManager mSubscriptionManager; private @Mock CarrierConfigManager mCarrierConfigManager; private @Mock TelephonyManager mTelephonyManager; + private @Mock UserManager mUserManager; private ArgumentCaptor<ConnectivityManager.NetworkCallback> mNetworkCallbackCaptor = ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class); @@ -351,6 +363,8 @@ public class NetworkPolicyManagerServiceTest { return mNotifManager; case Context.CONNECTIVITY_SERVICE: return mConnectivityManager; + case Context.USER_SERVICE: + return mUserManager; default: return super.getSystemService(name); } @@ -407,11 +421,14 @@ public class NetworkPolicyManagerServiceTest { when(mPackageManager.getPackagesForUid(UID_B)).thenReturn(new String[] {PKG_NAME_B}); when(mPackageManager.getPackagesForUid(UID_C)).thenReturn(new String[] {PKG_NAME_C}); when(mPackageManager.getApplicationInfo(eq(PKG_NAME_A), anyInt())) - .thenReturn(buildApplicationInfo(PKG_NAME_A)); + .thenReturn(buildApplicationInfo(PKG_NAME_A, UID_A)); when(mPackageManager.getApplicationInfo(eq(PKG_NAME_B), anyInt())) - .thenReturn(buildApplicationInfo(PKG_NAME_B)); + .thenReturn(buildApplicationInfo(PKG_NAME_B, UID_B)); when(mPackageManager.getApplicationInfo(eq(PKG_NAME_C), anyInt())) - .thenReturn(buildApplicationInfo(PKG_NAME_C)); + .thenReturn(buildApplicationInfo(PKG_NAME_C, UID_C)); + when(mPackageManager.getInstalledApplications(anyInt())).thenReturn( + buildInstalledApplicationInfoList()); + when(mUserManager.getUsers()).thenReturn(buildUserInfoList()); when(mNetworkManager.isBandwidthControlEnabled()).thenReturn(true); when(mNetworkManager.setDataSaverModeEnabled(anyBoolean())).thenReturn(true); doNothing().when(mConnectivityManager) @@ -1874,6 +1891,66 @@ public class NetworkPolicyManagerServiceTest { } } + private void enableRestrictedMode(boolean enable) throws Exception { + mService.mRestrictedNetworkingMode = enable; + mService.updateRestrictedModeAllowlistUL(); + verify(mNetworkManager).setFirewallChainEnabled(FIREWALL_CHAIN_RESTRICTED, + enable); + } + + @Test + public void testUpdateRestrictedModeAllowlist() throws Exception { + // initialization calls setFirewallChainEnabled, so we want to reset the invocations. + clearInvocations(mNetworkManager); + expectHasUseRestrictedNetworksPermission(UID_A, true); + expectHasUseRestrictedNetworksPermission(UID_B, false); + + Map<Integer, Integer> firewallUidRules = new ArrayMap<>(); + doAnswer(arg -> { + int[] uids = arg.getArgument(1); + 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_RESTRICTED), + any(int[].class), any(int[].class)); + + enableRestrictedMode(true); + assertEquals(FIREWALL_RULE_ALLOW, firewallUidRules.get(UID_A).intValue()); + assertFalse(mService.isUidNetworkingBlocked(UID_A, false)); + assertTrue(mService.isUidNetworkingBlocked(UID_B, false)); + + enableRestrictedMode(false); + assertFalse(mService.isUidNetworkingBlocked(UID_A, false)); + assertFalse(mService.isUidNetworkingBlocked(UID_B, false)); + } + + @Test + public void testUpdateRestrictedModeForUid() throws Exception { + // initialization calls setFirewallChainEnabled, so we want to reset the invocations. + clearInvocations(mNetworkManager); + expectHasUseRestrictedNetworksPermission(UID_A, true); + expectHasUseRestrictedNetworksPermission(UID_B, false); + enableRestrictedMode(true); + + // UID_D and UID_E are not part of installed applications list, so it won't have any + // firewall rules set yet + expectHasUseRestrictedNetworksPermission(UID_D, false); + mService.updateRestrictedModeForUidUL(UID_D); + verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_RESTRICTED, UID_D, + FIREWALL_RULE_DEFAULT); + assertTrue(mService.isUidNetworkingBlocked(UID_D, false)); + + expectHasUseRestrictedNetworksPermission(UID_E, true); + mService.updateRestrictedModeForUidUL(UID_E); + verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_RESTRICTED, UID_E, + FIREWALL_RULE_ALLOW); + assertFalse(mService.isUidNetworkingBlocked(UID_E, false)); + } + private String formatBlockedStateError(int uid, int rule, boolean metered, boolean backgroundRestricted) { return String.format( @@ -1888,12 +1965,27 @@ public class NetworkPolicyManagerServiceTest { .build(); } - private ApplicationInfo buildApplicationInfo(String label) { + private ApplicationInfo buildApplicationInfo(String label, int uid) { final ApplicationInfo ai = new ApplicationInfo(); ai.nonLocalizedLabel = label; + ai.uid = uid; return ai; } + private List<ApplicationInfo> buildInstalledApplicationInfoList() { + final List<ApplicationInfo> installedApps = new ArrayList<>(); + installedApps.add(buildApplicationInfo(PKG_NAME_A, UID_A)); + installedApps.add(buildApplicationInfo(PKG_NAME_B, UID_B)); + installedApps.add(buildApplicationInfo(PKG_NAME_C, UID_C)); + return installedApps; + } + + private List<UserInfo> buildUserInfoList() { + final List<UserInfo> users = new ArrayList<>(); + users.add(new UserInfo(USER_ID, "user1", 0)); + return users; + } + private NetworkInfo buildNetworkInfo() { final NetworkInfo ni = new NetworkInfo(ConnectivityManager.TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_LTE, null, null); @@ -1967,6 +2059,15 @@ public class NetworkPolicyManagerServiceTest { hasIt ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED); } + private void expectHasUseRestrictedNetworksPermission(int uid, boolean hasIt) throws Exception { + when(mIpm.checkUidPermission(CONNECTIVITY_USE_RESTRICTED_NETWORKS, uid)).thenReturn( + hasIt ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED); + when(mIpm.checkUidPermission(NETWORK_STACK, uid)).thenReturn( + PackageManager.PERMISSION_DENIED); + when(mIpm.checkUidPermission(PERMISSION_MAINLINE_NETWORK_STACK, uid)).thenReturn( + PackageManager.PERMISSION_DENIED); + } + private void expectNetworkState(boolean roaming) throws Exception { when(mCarrierConfigManager.getConfigForSubId(eq(TEST_SUB_ID))) .thenReturn(mCarrierConfig); -- GitLab