diff --git a/service/src/com/android/server/connectivity/PermissionMonitor.java b/service/src/com/android/server/connectivity/PermissionMonitor.java index 0c883be193..f596c4acab 100755 --- a/service/src/com/android/server/connectivity/PermissionMonitor.java +++ b/service/src/com/android/server/connectivity/PermissionMonitor.java @@ -74,7 +74,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Set; /** @@ -86,8 +85,6 @@ import java.util.Set; public class PermissionMonitor { private static final String TAG = "PermissionMonitor"; private static final boolean DBG = true; - protected static final Boolean SYSTEM = Boolean.TRUE; - protected static final Boolean NETWORK = Boolean.FALSE; private static final int VERSION_Q = Build.VERSION_CODES.Q; private final PackageManager mPackageManager; @@ -102,7 +99,7 @@ public class PermissionMonitor { // Keys are appIds. Values are true for SYSTEM permission and false for NETWORK permission. @GuardedBy("this") - private final Map mApps = new HashMap<>(); + private final SparseIntArray mApps = new SparseIntArray(); // Keys are active non-bypassable and fully-routed VPN's interface name, Values are uid ranges // for apps under the VPN @@ -194,6 +191,23 @@ public class PermissionMonitor { mContext = context; } + private int getPackageNetdNetworkPermission(@NonNull final PackageInfo app) { + if (hasRestrictedNetworkPermission(app)) { + return PERMISSION_SYSTEM; + } + if (hasNetworkPermission(app)) { + return PERMISSION_NETWORK; + } + return PERMISSION_NONE; + } + + static boolean isHigherNetworkPermission(final int targetPermission, + final int currentPermission) { + // This is relied on strict order of network permissions (SYSTEM > NETWORK > NONE), and it + // is enforced in tests. + return targetPermission > currentPermission; + } + // Intended to be called only once at startup, after the system is ready. Installs a broadcast // receiver to monitor ongoing UID changes, so this shouldn't/needn't be called again. public synchronized void startMonitoring() { @@ -247,16 +261,9 @@ public class PermissionMonitor { final int appId = UserHandle.getAppId(uid); mAllApps.add(appId); - boolean hasNetworkPermission = hasNetworkPermission(app); - boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app); - - if (hasNetworkPermission || hasRestrictedPermission) { - final Boolean permission = mApps.get(appId); - // If multiple packages share a UID (cf: android:sharedUserId) and ask for different - // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is). - if (permission == null || permission == NETWORK) { - mApps.put(appId, hasRestrictedPermission); - } + final int permission = getPackageNetdNetworkPermission(app); + if (isHigherNetworkPermission(permission, mApps.get(appId, PERMISSION_NONE))) { + mApps.put(appId, permission); } //TODO: unify the management of the permissions into one codepath. @@ -353,25 +360,29 @@ public class PermissionMonitor { // networks. mApps contains the result of checks for both hasNetworkPermission and // hasRestrictedNetworkPermission. If uid is in the mApps list that means uid has one of // permissions at least. - return mApps.containsKey(UserHandle.getAppId(uid)); + return mApps.get(UserHandle.getAppId(uid), PERMISSION_NONE) != PERMISSION_NONE; } /** * Returns whether the given uid has permission to use restricted networks. */ public synchronized boolean hasRestrictedNetworksPermission(int uid) { - return Boolean.TRUE.equals(mApps.get(UserHandle.getAppId(uid))); + return PERMISSION_SYSTEM == mApps.get(UserHandle.getAppId(uid), PERMISSION_NONE); } - private void update(Set users, Map apps, boolean add) { + private void update(Set users, SparseIntArray apps, boolean add) { List network = new ArrayList<>(); List system = new ArrayList<>(); - for (Entry app : apps.entrySet()) { - List list = app.getValue() ? system : network; + for (int i = 0; i < apps.size(); i++) { + final int permission = apps.valueAt(i); + if (PERMISSION_NONE == permission) { + continue; // Normally NONE is not stored in this map, but just in case + } + List list = (PERMISSION_SYSTEM == permission) ? system : network; for (UserHandle user : users) { if (user == null) continue; - list.add(user.getUid(app.getKey())); + list.add(user.getUid(apps.keyAt(i))); } } try { @@ -396,10 +407,7 @@ public class PermissionMonitor { */ public synchronized void onUserAdded(@NonNull UserHandle user) { mUsers.add(user); - - Set users = new HashSet<>(); - users.add(user); - update(users, mApps, true); + update(Set.of(user), mApps, true); } /** @@ -411,10 +419,7 @@ public class PermissionMonitor { */ public synchronized void onUserRemoved(@NonNull UserHandle user) { mUsers.remove(user); - - Set users = new HashSet<>(); - users.add(user); - update(users, mApps, false); + update(Set.of(user), mApps, false); } /** @@ -426,17 +431,16 @@ public class PermissionMonitor { * permission. */ @VisibleForTesting - protected Boolean highestPermissionForUid(Boolean currentPermission, String name) { - if (currentPermission == SYSTEM) { + protected int highestPermissionForUid(int currentPermission, String name) { + if (currentPermission == PERMISSION_SYSTEM) { return currentPermission; } try { final PackageInfo app = mPackageManager.getPackageInfo(name, GET_PERMISSIONS | MATCH_ANY_USER); - final boolean isNetwork = hasNetworkPermission(app); - final boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app); - if (isNetwork || hasRestrictedPermission) { - currentPermission = hasRestrictedPermission; + final int permission = getPackageNetdNetworkPermission(app); + if (isHigherNetworkPermission(permission, currentPermission)) { + return permission; } } catch (NameNotFoundException e) { // App not found. @@ -465,6 +469,17 @@ public class PermissionMonitor { return permission; } + private synchronized void updateVpnUid(int uid, boolean add) { + for (Map.Entry> vpn : mVpnUidRanges.entrySet()) { + if (UidRange.containsUid(vpn.getValue(), uid)) { + final Set changedUids = new HashSet<>(); + changedUids.add(uid); + removeBypassingUids(changedUids, -1 /* vpnAppUid */); + updateVpnUidsInterfaceRules(vpn.getKey(), changedUids, add); + } + } + } + /** * Called when a package is added. * @@ -479,38 +494,32 @@ public class PermissionMonitor { // If multiple packages share a UID (cf: android:sharedUserId) and ask for different // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is). - final Boolean permission = highestPermissionForUid(mApps.get(appId), packageName); - if (permission != mApps.get(appId)) { + final int currentPermission = mApps.get(appId, PERMISSION_NONE); + final int permission = highestPermissionForUid(currentPermission, packageName); + if (permission != currentPermission) { mApps.put(appId, permission); - Map apps = new HashMap<>(); + SparseIntArray apps = new SparseIntArray(); apps.put(appId, permission); update(mUsers, apps, true); } // If the newly-installed package falls within some VPN's uid range, update Netd with it. - // This needs to happen after the mApps update above, since removeBypassingUids() depends - // on mApps to check if the package can bypass VPN. - for (Map.Entry> vpn : mVpnUidRanges.entrySet()) { - if (UidRange.containsUid(vpn.getValue(), uid)) { - final Set changedUids = new HashSet<>(); - changedUids.add(uid); - removeBypassingUids(changedUids, /* vpnAppUid */ -1); - updateVpnUids(vpn.getKey(), changedUids, true); - } - } + // This needs to happen after the mApps update above, since removeBypassingUids() in + // updateVpnUid() depends on mApps to check if the package can bypass VPN. + updateVpnUid(uid, true /* add */); mAllApps.add(appId); } - private Boolean highestUidNetworkPermission(int uid) { - Boolean permission = null; + private int highestUidNetworkPermission(int uid) { + int permission = PERMISSION_NONE; final String[] packages = mPackageManager.getPackagesForUid(uid); if (!CollectionUtils.isEmpty(packages)) { for (String name : packages) { // If multiple packages have the same UID, give the UID all permissions that // any package in that UID has. permission = highestPermissionForUid(permission, name); - if (permission == SYSTEM) { + if (permission == PERMISSION_SYSTEM) { break; } } @@ -531,40 +540,32 @@ public class PermissionMonitor { sendPackagePermissionsForAppId(appId, getPermissionForUid(uid)); // If the newly-removed package falls within some VPN's uid range, update Netd with it. - // This needs to happen before the mApps update below, since removeBypassingUids() depends - // on mApps to check if the package can bypass VPN. - for (Map.Entry> vpn : mVpnUidRanges.entrySet()) { - if (UidRange.containsUid(vpn.getValue(), uid)) { - final Set changedUids = new HashSet<>(); - changedUids.add(uid); - removeBypassingUids(changedUids, /* vpnAppUid */ -1); - updateVpnUids(vpn.getKey(), changedUids, false); - } - } + // This needs to happen before the mApps update below, since removeBypassingUids() in + // updateVpnUid() depends on mApps to check if the package can bypass VPN. + updateVpnUid(uid, false /* add */); // If the package has been removed from all users on the device, clear it form mAllApps. if (mPackageManager.getNameForUid(uid) == null) { mAllApps.remove(appId); } - Map apps = new HashMap<>(); - final Boolean permission = highestUidNetworkPermission(uid); - if (permission == SYSTEM) { + final int permission = highestUidNetworkPermission(uid); + if (permission == PERMISSION_SYSTEM) { // An app with this UID still has the SYSTEM permission. // Therefore, this UID must already have the SYSTEM permission. // Nothing to do. return; } + // If the permissions of this UID have not changed, do nothing. + if (permission == mApps.get(appId, PERMISSION_NONE)) return; - if (permission == mApps.get(appId)) { - // The permissions of this UID have not changed. Nothing to do. - return; - } else if (permission != null) { + final SparseIntArray apps = new SparseIntArray(); + if (permission != PERMISSION_NONE) { mApps.put(appId, permission); apps.put(appId, permission); update(mUsers, apps, true); } else { - mApps.remove(appId); - apps.put(appId, NETWORK); // doesn't matter which permission we pick here + mApps.delete(appId); + apps.put(appId, PERMISSION_NETWORK); // doesn't matter which permission we pick here update(mUsers, apps, false); } } @@ -611,7 +612,7 @@ public class PermissionMonitor { // but that's safe. final Set changedUids = intersectUids(rangesToAdd, mAllApps); removeBypassingUids(changedUids, vpnAppUid); - updateVpnUids(iface, changedUids, true); + updateVpnUidsInterfaceRules(iface, changedUids, true /* add */); if (mVpnUidRanges.containsKey(iface)) { mVpnUidRanges.get(iface).addAll(rangesToAdd); } else { @@ -632,7 +633,7 @@ public class PermissionMonitor { // ranges and update Netd about them. final Set changedUids = intersectUids(rangesToRemove, mAllApps); removeBypassingUids(changedUids, vpnAppUid); - updateVpnUids(iface, changedUids, false); + updateVpnUidsInterfaceRules(iface, changedUids, false /* add */); Set existingRanges = mVpnUidRanges.getOrDefault(iface, null); if (existingRanges == null) { loge("Attempt to remove unknown vpn uid Range iface = " + iface); @@ -671,7 +672,7 @@ public class PermissionMonitor { /** * Remove all apps which can elect to bypass the VPN from the list of uids * - * An app can elect to bypass the VPN if it hold SYSTEM permission, or if its the active VPN + * An app can elect to bypass the VPN if it holds SYSTEM permission, or if it's the active VPN * app itself. * * @param uids The list of uids to operate on @@ -679,7 +680,8 @@ public class PermissionMonitor { */ private void removeBypassingUids(Set uids, int vpnAppUid) { uids.remove(vpnAppUid); - uids.removeIf(uid -> mApps.getOrDefault(UserHandle.getAppId(uid), NETWORK) == SYSTEM); + uids.removeIf(uid -> + mApps.get(UserHandle.getAppId(uid), PERMISSION_NONE) == PERMISSION_SYSTEM); } /** @@ -693,7 +695,7 @@ public class PermissionMonitor { * @param add {@code true} if the uids are to be added to the interface, {@code false} if they * are to be removed from the interface. */ - private void updateVpnUids(String iface, Set uids, boolean add) { + private void updateVpnUidsInterfaceRules(String iface, Set uids, boolean add) { if (uids.size() == 0) { return; } @@ -811,17 +813,18 @@ public class PermissionMonitor { updateUidsAllowedOnRestrictedNetworks(mDeps.getUidsAllowedOnRestrictedNetworks(mContext)); uidsToUpdate.addAll(mUidsAllowedOnRestrictedNetworks); - final Map updatedUids = new HashMap<>(); - final Map removedUids = new HashMap<>(); + final SparseIntArray updatedUids = new SparseIntArray(); + final SparseIntArray removedUids = new SparseIntArray(); // Step2. For each uid to update, find out its new permission. for (Integer uid : uidsToUpdate) { - final Boolean permission = highestUidNetworkPermission(uid); + final int permission = highestUidNetworkPermission(uid); final int appId = UserHandle.getAppId(uid); - if (null == permission) { - removedUids.put(appId, NETWORK); // Doesn't matter which permission is set here. - mApps.remove(appId); + if (PERMISSION_NONE == permission) { + // Doesn't matter which permission is set here. + removedUids.put(appId, PERMISSION_NETWORK); + mApps.delete(appId); } else { updatedUids.put(appId, permission); mApps.put(appId, permission); diff --git a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java index b7b3e29713..ecda338b49 100644 --- a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java +++ b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java @@ -32,6 +32,7 @@ import static android.content.pm.PackageManager.GET_PERMISSIONS; import static android.content.pm.PackageManager.MATCH_ANY_USER; import static android.net.ConnectivitySettingsManager.UIDS_ALLOWED_ON_RESTRICTED_NETWORKS; import static android.net.INetd.PERMISSION_INTERNET; +import static android.net.INetd.PERMISSION_NETWORK; import static android.net.INetd.PERMISSION_NONE; import static android.net.INetd.PERMISSION_SYSTEM; import static android.net.INetd.PERMISSION_UNINSTALLED; @@ -39,8 +40,7 @@ import static android.net.INetd.PERMISSION_UPDATE_DEVICE_STATS; import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; import static android.os.Process.SYSTEM_UID; -import static com.android.server.connectivity.PermissionMonitor.NETWORK; -import static com.android.server.connectivity.PermissionMonitor.SYSTEM; +import static com.android.server.connectivity.PermissionMonitor.isHigherNetworkPermission; import static junit.framework.Assert.fail; @@ -102,7 +102,6 @@ import org.mockito.invocation.InvocationOnMock; import java.lang.reflect.Array; import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.Set; @@ -152,7 +151,7 @@ public class PermissionMonitorTest { MockitoAnnotations.initMocks(this); when(mContext.getPackageManager()).thenReturn(mPackageManager); when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager); - when(mUserManager.getUserHandles(eq(true))).thenReturn(List.of(MOCK_USER1, MOCK_USER2)); + doReturn(List.of(MOCK_USER1)).when(mUserManager).getUserHandles(eq(true)); when(mContext.getSystemServiceName(SystemConfigManager.class)) .thenReturn(Context.SYSTEM_CONFIG_SERVICE); when(mContext.getSystemService(Context.SYSTEM_CONFIG_SERVICE)) @@ -173,8 +172,7 @@ public class PermissionMonitorTest { mPermissionMonitor = new PermissionMonitor(mContext, mNetdService, mDeps); mNetdMonitor = new NetdMonitor(mNetdService); - when(mPackageManager.getInstalledPackages(anyInt())).thenReturn(/* empty app list */ null); - mPermissionMonitor.startMonitoring(); + doReturn(List.of()).when(mPackageManager).getInstalledPackages(anyInt()); } private boolean hasRestrictedNetworkPermission(String partition, int targetSdkVersion, @@ -506,21 +504,22 @@ public class PermissionMonitorTest { } private class NetdMonitor { - private final HashMap mUidsNetworkPermission = new HashMap<>(); - private final HashMap mAppIdsTrafficPermission = new HashMap<>(); + private final SparseIntArray mUidsNetworkPermission = new SparseIntArray(); + private final SparseIntArray mAppIdsTrafficPermission = new SparseIntArray(); + private static final int DOES_NOT_EXIST = -2; NetdMonitor(INetd mockNetd) throws Exception { // Add hook to verify and track result of networkSetPermission. doAnswer((InvocationOnMock invocation) -> { final Object[] args = invocation.getArguments(); - final Boolean isSystem = args[0].equals(PERMISSION_SYSTEM); + final int permission = (int) args[0]; for (final int uid : (int[]) args[1]) { // TODO: Currently, permission monitor will send duplicate commands for each uid // corresponding to each user. Need to fix that and uncomment below test. // if (mApps.containsKey(uid) && mApps.get(uid) == isSystem) { // fail("uid " + uid + " is already set to " + isSystem); // } - mUidsNetworkPermission.put(uid, isSystem); + mUidsNetworkPermission.put(uid, permission); } return null; }).when(mockNetd).networkSetPermissionForUser(anyInt(), any(int[].class)); @@ -534,7 +533,7 @@ public class PermissionMonitorTest { // if (!mApps.containsKey(uid)) { // fail("uid " + uid + " does not exist."); // } - mUidsNetworkPermission.remove(uid); + mUidsNetworkPermission.delete(uid); } return null; }).when(mockNetd).networkClearPermissionForUser(any(int[].class)); @@ -550,11 +549,11 @@ public class PermissionMonitorTest { }).when(mockNetd).trafficSetNetPermForUids(anyInt(), any(int[].class)); } - public void expectNetworkPerm(Boolean permission, UserHandle[] users, int... appIds) { + public void expectNetworkPerm(int permission, UserHandle[] users, int... appIds) { for (final UserHandle user : users) { for (final int appId : appIds) { final int uid = user.getUid(appId); - if (!mUidsNetworkPermission.containsKey(uid)) { + if (mUidsNetworkPermission.get(uid, DOES_NOT_EXIST) == DOES_NOT_EXIST) { fail("uid " + uid + " does not exist."); } if (mUidsNetworkPermission.get(uid) != permission) { @@ -568,7 +567,7 @@ public class PermissionMonitorTest { for (final UserHandle user : users) { for (final int appId : appIds) { final int uid = user.getUid(appId); - if (mUidsNetworkPermission.containsKey(uid)) { + if (mUidsNetworkPermission.get(uid, DOES_NOT_EXIST) != DOES_NOT_EXIST) { fail("uid " + uid + " has listed permissions, expected none."); } } @@ -577,7 +576,7 @@ public class PermissionMonitorTest { public void expectTrafficPerm(int permission, int... appIds) { for (final int appId : appIds) { - if (!mAppIdsTrafficPermission.containsKey(appId)) { + if (mAppIdsTrafficPermission.get(appId, DOES_NOT_EXIST) == DOES_NOT_EXIST) { fail("appId " + appId + " does not exist."); } if (mAppIdsTrafficPermission.get(appId) != permission) { @@ -599,17 +598,21 @@ public class PermissionMonitorTest { buildAndMockPackageInfoWithPermissions(SYSTEM_PACKAGE2, SYSTEM_APP_UID1, CHANGE_NETWORK_STATE); - // Add SYSTEM_PACKAGE2, expect only have network permission. + // Add user MOCK_USER1. mPermissionMonitor.onUserAdded(MOCK_USER1); + // Add SYSTEM_PACKAGE2, expect only have network permission. addPackageForUsers(new UserHandle[]{MOCK_USER1}, SYSTEM_PACKAGE2, SYSTEM_APPID1); - mNetdMonitor.expectNetworkPerm(NETWORK, new UserHandle[]{MOCK_USER1}, SYSTEM_APPID1); + mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1}, + SYSTEM_APPID1); // Add SYSTEM_PACKAGE1, expect permission upgrade. addPackageForUsers(new UserHandle[]{MOCK_USER1}, SYSTEM_PACKAGE1, SYSTEM_APPID1); - mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1}, SYSTEM_APPID1); + mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1}, + SYSTEM_APPID1); + // Add user MOCK_USER2. mPermissionMonitor.onUserAdded(MOCK_USER2); - mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2}, + mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2}, SYSTEM_APPID1); // Remove SYSTEM_PACKAGE2, expect keep system permission. @@ -619,19 +622,19 @@ public class PermissionMonitorTest { .thenReturn(new String[]{SYSTEM_PACKAGE1}); removePackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, SYSTEM_PACKAGE2, SYSTEM_APPID1); - mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2}, + mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2}, SYSTEM_APPID1); // Add SYSTEM_PACKAGE2, expect keep system permission. addPackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, SYSTEM_PACKAGE2, SYSTEM_APPID1); - mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2}, + mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2}, SYSTEM_APPID1); addPackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_APPID1); - mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2}, + mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2}, SYSTEM_APPID1); - mNetdMonitor.expectNetworkPerm(NETWORK, new UserHandle[]{MOCK_USER1, MOCK_USER2}, + mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_APPID1); // Remove MOCK_PACKAGE1, expect no permission left for all user. @@ -647,11 +650,12 @@ public class PermissionMonitorTest { .thenReturn(new String[]{SYSTEM_PACKAGE2}); removePackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, SYSTEM_PACKAGE1, SYSTEM_APPID1); - mNetdMonitor.expectNetworkPerm(NETWORK, new UserHandle[]{MOCK_USER1, MOCK_USER2}, + mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1, MOCK_USER2}, SYSTEM_APPID1); mPermissionMonitor.onUserRemoved(MOCK_USER1); - mNetdMonitor.expectNetworkPerm(NETWORK, new UserHandle[]{MOCK_USER2}, SYSTEM_APPID1); + mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER2}, + SYSTEM_APPID1); mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, SYSTEM_APPID1); // Remove all packages, expect no permission left. @@ -866,7 +870,6 @@ public class PermissionMonitorTest { @Test public void testUpdateUidPermissionsFromSystemConfig() throws Exception { - when(mPackageManager.getInstalledPackages(anyInt())).thenReturn(List.of()); when(mSystemConfigManager.getSystemPermissionUids(eq(INTERNET))) .thenReturn(new int[]{ MOCK_UID1, MOCK_UID2 }); when(mSystemConfigManager.getSystemPermissionUids(eq(UPDATE_DEVICE_STATS))) @@ -894,6 +897,7 @@ public class PermissionMonitorTest { @Test public void testIntentReceiver() throws Exception { + mPermissionMonitor.startMonitoring(); final BroadcastReceiver receiver = expectBroadcastReceiver( Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_REMOVED); @@ -925,10 +929,10 @@ public class PermissionMonitorTest { @Test public void testUidsAllowedOnRestrictedNetworksChanged() throws Exception { + mPermissionMonitor.startMonitoring(); final ContentObserver contentObserver = expectRegisterContentObserver( Settings.Global.getUriFor(UIDS_ALLOWED_ON_RESTRICTED_NETWORKS)); - mPermissionMonitor.onUserAdded(MOCK_USER1); // Prepare PackageInfo for MOCK_PACKAGE1 and MOCK_PACKAGE2 buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1); buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID2); @@ -937,14 +941,16 @@ public class PermissionMonitorTest { // should have SYSTEM permission. when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID1)); contentObserver.onChange(true /* selfChange */); - mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1}, MOCK_APPID1); + mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1}, + MOCK_APPID1); mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID2); // MOCK_UID2 is listed in setting that allow to use restricted networks, MOCK_UID2 // should have SYSTEM permission but MOCK_UID1 should revoke permission. when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID2)); contentObserver.onChange(true /* selfChange */); - mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1}, MOCK_APPID2); + mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1}, + MOCK_APPID2); mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID1); // No uid lists in setting, should revoke permission from all uids. @@ -955,27 +961,30 @@ public class PermissionMonitorTest { @Test public void testUidsAllowedOnRestrictedNetworksChangedWithSharedUid() throws Exception { + mPermissionMonitor.startMonitoring(); final ContentObserver contentObserver = expectRegisterContentObserver( Settings.Global.getUriFor(UIDS_ALLOWED_ON_RESTRICTED_NETWORKS)); - mPermissionMonitor.onUserAdded(MOCK_USER1); buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1, CHANGE_NETWORK_STATE); buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID1); // MOCK_PACKAGE1 have CHANGE_NETWORK_STATE, MOCK_UID1 should have NETWORK permission. addPackageForUsers(new UserHandle[]{MOCK_USER1}, MOCK_PACKAGE1, MOCK_APPID1); - mNetdMonitor.expectNetworkPerm(NETWORK, new UserHandle[]{MOCK_USER1}, MOCK_APPID1); + mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1}, + MOCK_APPID1); // MOCK_UID1 is listed in setting that allow to use restricted networks, MOCK_UID1 // should upgrade to SYSTEM permission. when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID1)); contentObserver.onChange(true /* selfChange */); - mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1}, MOCK_APPID1); + mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1}, + MOCK_APPID1); // No app lists in setting, MOCK_UID1 should downgrade to NETWORK permission. when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of()); contentObserver.onChange(true /* selfChange */); - mNetdMonitor.expectNetworkPerm(NETWORK, new UserHandle[]{MOCK_USER1}, MOCK_APPID1); + mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1}, + MOCK_APPID1); // MOCK_PACKAGE1 removed, should revoke permission from MOCK_UID1. when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{MOCK_PACKAGE2}); @@ -985,11 +994,10 @@ public class PermissionMonitorTest { @Test public void testUidsAllowedOnRestrictedNetworksChangedWithMultipleUsers() throws Exception { + mPermissionMonitor.startMonitoring(); final ContentObserver contentObserver = expectRegisterContentObserver( Settings.Global.getUriFor(UIDS_ALLOWED_ON_RESTRICTED_NETWORKS)); - // One user MOCK_USER1 - mPermissionMonitor.onUserAdded(MOCK_USER1); // Prepare PackageInfo for MOCK_PACKAGE1 and MOCK_PACKAGE2. buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1); buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID2); @@ -998,14 +1006,15 @@ public class PermissionMonitorTest { // should have SYSTEM permission and MOCK_UID2 has no permissions. when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID1)); contentObserver.onChange(true /* selfChange */); - mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1}, MOCK_APPID1); + mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1}, + MOCK_APPID1); mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID2); // Add user MOCK_USER2. mPermissionMonitor.onUserAdded(MOCK_USER2); // MOCK_APPID1 in both users should all have SYSTEM permission and MOCK_APPID2 has no // permissions in either user. - mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2}, + mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_APPID1); mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_APPID2); @@ -1014,13 +1023,14 @@ public class PermissionMonitorTest { // user. when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID2)); contentObserver.onChange(true /* selfChange */); - mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2}, + mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_APPID2); mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_APPID1); // Remove user MOCK_USER1 mPermissionMonitor.onUserRemoved(MOCK_USER1); - mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER2}, MOCK_APPID2); + mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER2}, + MOCK_APPID2); mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER2}, MOCK_APPID1); mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID2); @@ -1032,12 +1042,8 @@ public class PermissionMonitorTest { @Test public void testOnExternalApplicationsAvailable() throws Exception { - final BroadcastReceiver receiver = expectBroadcastReceiver( - Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); - // Initial the permission state. MOCK_PACKAGE1 and MOCK_PACKAGE2 are installed on external // and have different uids. There has no permission for both uids. - when(mUserManager.getUserHandles(eq(true))).thenReturn(List.of(MOCK_USER1)); when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn( List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID1), buildPackageInfo(MOCK_PACKAGE2, MOCK_UID2))); @@ -1045,6 +1051,8 @@ public class PermissionMonitorTest { mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID1, MOCK_APPID2); mNetdMonitor.expectTrafficPerm(PERMISSION_NONE, MOCK_APPID1, MOCK_APPID2); + final BroadcastReceiver receiver = expectBroadcastReceiver( + Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); // Verify receiving EXTERNAL_APPLICATIONS_AVAILABLE intent and update permission to netd. final Intent externalIntent = new Intent(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); externalIntent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST, @@ -1054,8 +1062,10 @@ public class PermissionMonitorTest { buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID2, CHANGE_NETWORK_STATE, UPDATE_DEVICE_STATS); receiver.onReceive(mContext, externalIntent); - mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1}, MOCK_APPID1); - mNetdMonitor.expectNetworkPerm(NETWORK, new UserHandle[]{MOCK_USER1}, MOCK_APPID2); + mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1}, + MOCK_APPID1); + mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1}, + MOCK_APPID2); mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID1); mNetdMonitor.expectTrafficPerm(PERMISSION_UPDATE_DEVICE_STATS, MOCK_APPID2); } @@ -1063,12 +1073,10 @@ public class PermissionMonitorTest { @Test public void testOnExternalApplicationsAvailable_AppsNotRegisteredOnStartMonitoring() throws Exception { + mPermissionMonitor.startMonitoring(); final BroadcastReceiver receiver = expectBroadcastReceiver( Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); - // One user MOCK_USER1 - mPermissionMonitor.onUserAdded(MOCK_USER1); - // Initial the permission state. MOCK_PACKAGE1 and MOCK_PACKAGE2 are installed on external // and have different uids. There has no permission for both uids. buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1, @@ -1081,8 +1089,10 @@ public class PermissionMonitorTest { externalIntent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST, new String[] { MOCK_PACKAGE1 , MOCK_PACKAGE2}); receiver.onReceive(mContext, externalIntent); - mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1}, MOCK_APPID1); - mNetdMonitor.expectNetworkPerm(NETWORK, new UserHandle[]{MOCK_USER1}, MOCK_APPID2); + mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1}, + MOCK_APPID1); + mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1}, + MOCK_APPID2); mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID1); mNetdMonitor.expectTrafficPerm(PERMISSION_UPDATE_DEVICE_STATS, MOCK_APPID2); } @@ -1090,12 +1100,8 @@ public class PermissionMonitorTest { @Test public void testOnExternalApplicationsAvailableWithSharedUid() throws Exception { - final BroadcastReceiver receiver = expectBroadcastReceiver( - Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); - // Initial the permission state. MOCK_PACKAGE1 and MOCK_PACKAGE2 are installed on external // storage and shared on MOCK_UID1. There has no permission for MOCK_UID1. - when(mUserManager.getUserHandles(eq(true))).thenReturn(List.of(MOCK_USER1)); when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn( List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID1), buildPackageInfo(MOCK_PACKAGE2, MOCK_UID1))); @@ -1103,34 +1109,36 @@ public class PermissionMonitorTest { mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID1); mNetdMonitor.expectTrafficPerm(PERMISSION_NONE, MOCK_APPID1); + final BroadcastReceiver receiver = expectBroadcastReceiver( + Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); // Verify receiving EXTERNAL_APPLICATIONS_AVAILABLE intent and update permission to netd. final Intent externalIntent = new Intent(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); externalIntent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST, new String[] {MOCK_PACKAGE1}); buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1, CHANGE_NETWORK_STATE); buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID1, UPDATE_DEVICE_STATS); receiver.onReceive(mContext, externalIntent); - mNetdMonitor.expectNetworkPerm(NETWORK, new UserHandle[]{MOCK_USER1}, MOCK_APPID1); + mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1}, + MOCK_APPID1); mNetdMonitor.expectTrafficPerm(PERMISSION_UPDATE_DEVICE_STATS, MOCK_APPID1); } @Test public void testOnExternalApplicationsAvailableWithSharedUid_DifferentStorage() throws Exception { - final BroadcastReceiver receiver = expectBroadcastReceiver( - Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); - // Initial the permission state. MOCK_PACKAGE1 is installed on external storage and // MOCK_PACKAGE2 is installed on device. These two packages are shared on MOCK_UID1. // MOCK_UID1 has NETWORK and INTERNET permissions. - when(mUserManager.getUserHandles(eq(true))).thenReturn(List.of(MOCK_USER1)); when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn( List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID1), buildPackageInfo(MOCK_PACKAGE2, MOCK_UID1, CHANGE_NETWORK_STATE, INTERNET))); mPermissionMonitor.startMonitoring(); - mNetdMonitor.expectNetworkPerm(NETWORK, new UserHandle[]{MOCK_USER1}, MOCK_APPID1); + mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1}, + MOCK_APPID1); mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID1); + final BroadcastReceiver receiver = expectBroadcastReceiver( + Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); // Verify receiving EXTERNAL_APPLICATIONS_AVAILABLE intent and update permission to netd. final Intent externalIntent = new Intent(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); externalIntent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST, new String[] {MOCK_PACKAGE1}); @@ -1139,7 +1147,21 @@ public class PermissionMonitorTest { buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID1, CHANGE_NETWORK_STATE, INTERNET); receiver.onReceive(mContext, externalIntent); - mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1}, MOCK_APPID1); + mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1}, + MOCK_APPID1); mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1); } + + @Test + public void testIsHigherNetworkPermission() { + assertFalse(isHigherNetworkPermission(PERMISSION_NONE, PERMISSION_NONE)); + assertFalse(isHigherNetworkPermission(PERMISSION_NONE, PERMISSION_NETWORK)); + assertFalse(isHigherNetworkPermission(PERMISSION_NONE, PERMISSION_SYSTEM)); + assertTrue(isHigherNetworkPermission(PERMISSION_NETWORK, PERMISSION_NONE)); + assertFalse(isHigherNetworkPermission(PERMISSION_NETWORK, PERMISSION_NETWORK)); + assertFalse(isHigherNetworkPermission(PERMISSION_NETWORK, PERMISSION_SYSTEM)); + assertTrue(isHigherNetworkPermission(PERMISSION_SYSTEM, PERMISSION_NONE)); + assertTrue(isHigherNetworkPermission(PERMISSION_SYSTEM, PERMISSION_NETWORK)); + assertFalse(isHigherNetworkPermission(PERMISSION_SYSTEM, PERMISSION_SYSTEM)); + } }