diff options
3 files changed, 257 insertions, 86 deletions
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 74c80237d721..564d35a0936d 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -894,10 +894,18 @@ public class ConnectivityService extends IConnectivityManager.Stub intentFilter.addAction(Intent.ACTION_USER_REMOVED); intentFilter.addAction(Intent.ACTION_USER_UNLOCKED); mContext.registerReceiverAsUser( - mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null); + mIntentReceiver, UserHandle.ALL, intentFilter, null, null); mContext.registerReceiverAsUser(mUserPresentReceiver, UserHandle.SYSTEM, new IntentFilter(Intent.ACTION_USER_PRESENT), null, null); + // Listen to package add and removal events for all users. + intentFilter = new IntentFilter(); + intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); + intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); + intentFilter.addDataScheme("package"); + mContext.registerReceiverAsUser( + mIntentReceiver, UserHandle.ALL, intentFilter, null, null); + try { mNMS.registerObserver(mTethering); mNMS.registerObserver(mDataActivityObserver); @@ -4155,6 +4163,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } private void onUserAdded(int userId) { + mPermissionMonitor.onUserAdded(userId); synchronized (mVpns) { final int vpnsSize = mVpns.size(); for (int i = 0; i < vpnsSize; i++) { @@ -4165,6 +4174,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } private void onUserRemoved(int userId) { + mPermissionMonitor.onUserRemoved(userId); synchronized (mVpns) { final int vpnsSize = mVpns.size(); for (int i = 0; i < vpnsSize; i++) { @@ -4174,6 +4184,22 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + private void onPackageAdded(String packageName, int uid) { + if (TextUtils.isEmpty(packageName) || uid < 0) { + Slog.wtf(TAG, "Invalid package in onPackageAdded: " + packageName + " | " + uid); + return; + } + mPermissionMonitor.onPackageAdded(packageName, uid); + } + + private void onPackageRemoved(String packageName, int uid) { + if (TextUtils.isEmpty(packageName) || uid < 0) { + Slog.wtf(TAG, "Invalid package in onPackageRemoved: " + packageName + " | " + uid); + return; + } + mPermissionMonitor.onPackageRemoved(uid); + } + private void onUserUnlocked(int userId) { synchronized (mVpns) { // User present may be sent because of an unlock, which might mean an unlocked keystore. @@ -4185,11 +4211,15 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() { + private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); + final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); + final Uri packageData = intent.getData(); + final String packageName = + packageData != null ? packageData.getSchemeSpecificPart() : null; if (userId == UserHandle.USER_NULL) return; if (Intent.ACTION_USER_STARTED.equals(action)) { @@ -4202,6 +4232,10 @@ public class ConnectivityService extends IConnectivityManager.Stub onUserRemoved(userId); } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) { onUserUnlocked(userId); + } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { + onPackageAdded(packageName, uid); + } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { + onPackageRemoved(packageName, uid); } } }; diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java index 94c94a514dec..420b23e6a39f 100644 --- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java +++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java @@ -27,10 +27,7 @@ import static android.os.Process.INVALID_UID; import static android.os.Process.SYSTEM_UID; import android.annotation.NonNull; -import android.content.BroadcastReceiver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -42,7 +39,6 @@ import android.os.INetworkManagementService; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; -import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; @@ -64,15 +60,14 @@ import java.util.Set; public class PermissionMonitor { private static final String TAG = "PermissionMonitor"; private static final boolean DBG = true; - private static final Boolean SYSTEM = Boolean.TRUE; - private static final Boolean NETWORK = Boolean.FALSE; + 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 Context mContext; private final PackageManager mPackageManager; private final UserManager mUserManager; private final INetworkManagementService mNetd; - private final BroadcastReceiver mIntentReceiver; // Values are User IDs. private final Set<Integer> mUsers = new HashSet<>(); @@ -85,26 +80,6 @@ public class PermissionMonitor { mPackageManager = context.getPackageManager(); mUserManager = UserManager.get(context); mNetd = netd; - mIntentReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); - int appUid = intent.getIntExtra(Intent.EXTRA_UID, INVALID_UID); - Uri appData = intent.getData(); - String appName = appData != null ? appData.getSchemeSpecificPart() : null; - - if (Intent.ACTION_USER_ADDED.equals(action)) { - onUserAdded(user); - } else if (Intent.ACTION_USER_REMOVED.equals(action)) { - onUserRemoved(user); - } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { - onAppAdded(appName, appUid); - } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { - onAppRemoved(appUid); - } - } - }; } // Intended to be called only once at startup, after the system is ready. Installs a broadcast @@ -112,17 +87,6 @@ public class PermissionMonitor { public synchronized void startMonitoring() { log("Monitoring"); - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(Intent.ACTION_USER_ADDED); - intentFilter.addAction(Intent.ACTION_USER_REMOVED); - mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null); - - intentFilter = new IntentFilter(); - intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); - intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); - intentFilter.addDataScheme("package"); - mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, intentFilter, null, null); - List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS); if (apps == null) { loge("No apps"); @@ -260,7 +224,14 @@ public class PermissionMonitor { } } - private synchronized void onUserAdded(int user) { + /** + * Called when a user is added. See {link #ACTION_USER_ADDED}. + * + * @param user The integer userHandle of the added user. See {@link #EXTRA_USER_HANDLE}. + * + * @hide + */ + public synchronized void onUserAdded(int user) { if (user < 0) { loge("Invalid user in onUserAdded: " + user); return; @@ -272,7 +243,14 @@ public class PermissionMonitor { update(users, mApps, true); } - private synchronized void onUserRemoved(int user) { + /** + * Called when an user is removed. See {link #ACTION_USER_REMOVED}. + * + * @param user The integer userHandle of the removed user. See {@link #EXTRA_USER_HANDLE}. + * + * @hide + */ + public synchronized void onUserRemoved(int user) { if (user < 0) { loge("Invalid user in onUserRemoved: " + user); return; @@ -284,8 +262,8 @@ public class PermissionMonitor { update(users, mApps, false); } - - private Boolean highestPermissionForUid(Boolean currentPermission, String name) { + @VisibleForTesting + protected Boolean highestPermissionForUid(Boolean currentPermission, String name) { if (currentPermission == SYSTEM) { return currentPermission; } @@ -303,33 +281,39 @@ public class PermissionMonitor { return currentPermission; } - private synchronized void onAppAdded(String appName, int appUid) { - if (TextUtils.isEmpty(appName) || appUid < 0) { - loge("Invalid app in onAppAdded: " + appName + " | " + appUid); - return; - } - + /** + * Called when a package is added. See {link #ACTION_PACKAGE_ADDED}. + * + * @param packageName The name of the new package. + * @param uid The uid of the new package. + * + * @hide + */ + public synchronized void onPackageAdded(String packageName, int uid) { // 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(appUid), appName); - if (permission != mApps.get(appUid)) { - mApps.put(appUid, permission); + final Boolean permission = highestPermissionForUid(mApps.get(uid), packageName); + if (permission != mApps.get(uid)) { + mApps.put(uid, permission); Map<Integer, Boolean> apps = new HashMap<>(); - apps.put(appUid, permission); + apps.put(uid, permission); update(mUsers, apps, true); } } - private synchronized void onAppRemoved(int appUid) { - if (appUid < 0) { - loge("Invalid app in onAppRemoved: " + appUid); - return; - } + /** + * Called when a package is removed. See {link #ACTION_PACKAGE_REMOVED}. + * + * @param uid containing the integer uid previously assigned to the package. + * + * @hide + */ + public synchronized void onPackageRemoved(int uid) { Map<Integer, Boolean> apps = new HashMap<>(); Boolean permission = null; - String[] packages = mPackageManager.getPackagesForUid(appUid); + String[] packages = mPackageManager.getPackagesForUid(uid); if (packages != null && packages.length > 0) { for (String name : packages) { permission = highestPermissionForUid(permission, name); @@ -341,16 +325,16 @@ public class PermissionMonitor { } } } - if (permission == mApps.get(appUid)) { + if (permission == mApps.get(uid)) { // The permissions of this UID have not changed. Nothing to do. return; } else if (permission != null) { - mApps.put(appUid, permission); - apps.put(appUid, permission); + mApps.put(uid, permission); + apps.put(uid, permission); update(mUsers, apps, true); } else { - mApps.remove(appUid); - apps.put(appUid, NETWORK); // doesn't matter which permission we pick here + mApps.remove(uid); + apps.put(uid, NETWORK); // doesn't matter which permission we pick here update(mUsers, apps, false); } } diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java index af7123b84842..f2bd770d085a 100644 --- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java @@ -27,9 +27,17 @@ import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_VENDOR; import static android.content.pm.PackageManager.GET_PERMISSIONS; 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 junit.framework.Assert.fail; + import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.spy; @@ -40,6 +48,8 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Build; +import android.os.INetworkManagementService; +import android.os.UserHandle; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -48,12 +58,19 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; + +import java.util.HashMap; @RunWith(AndroidJUnit4.class) @SmallTest public class PermissionMonitorTest { - private static final int MOCK_UID = 10001; - private static final String[] MOCK_PACKAGE_NAMES = new String[] { "com.foo.bar" }; + private static final int MOCK_USER1 = 0; + private static final int MOCK_USER2 = 1; + private static final int MOCK_UID1 = 10001; + private static final String MOCK_PACKAGE1 = "appName1"; + private static final String SYSTEM_PACKAGE1 = "sysName1"; + private static final String SYSTEM_PACKAGE2 = "sysName2"; private static final String PARTITION_SYSTEM = "system"; private static final String PARTITION_OEM = "oem"; private static final String PARTITION_PRODUCT = "product"; @@ -63,6 +80,7 @@ public class PermissionMonitorTest { @Mock private Context mContext; @Mock private PackageManager mPackageManager; + @Mock private INetworkManagementService mNMS; private PermissionMonitor mPermissionMonitor; @@ -70,8 +88,7 @@ public class PermissionMonitorTest { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); when(mContext.getPackageManager()).thenReturn(mPackageManager); - when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(MOCK_PACKAGE_NAMES); - mPermissionMonitor = spy(new PermissionMonitor(mContext, null)); + mPermissionMonitor = spy(new PermissionMonitor(mContext, mNMS)); } private boolean hasBgPermission(String partition, int targetSdkVersion, int uid, @@ -80,7 +97,8 @@ public class PermissionMonitorTest { packageInfo.applicationInfo.targetSdkVersion = targetSdkVersion; packageInfo.applicationInfo.uid = uid; when(mPackageManager.getPackageInfoAsUser( - eq(MOCK_PACKAGE_NAMES[0]), eq(GET_PERMISSIONS), anyInt())).thenReturn(packageInfo); + eq(MOCK_PACKAGE1), eq(GET_PERMISSIONS), anyInt())).thenReturn(packageInfo); + when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[] {MOCK_PACKAGE1}); return mPermissionMonitor.hasUseBackgroundNetworksPermission(uid); } @@ -143,16 +161,16 @@ public class PermissionMonitorTest { @Test public void testHasUseBackgroundNetworksPermission() throws Exception { - assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID)); - assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CHANGE_NETWORK_STATE)); - assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, NETWORK_STACK)); - assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CONNECTIVITY_INTERNAL)); - assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, + assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1)); + assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE)); + assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, NETWORK_STACK)); + assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL)); + assertTrue(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); - assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID, CHANGE_WIFI_STATE)); + assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE)); - assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID)); - assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID, CHANGE_WIFI_STATE)); + assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1)); + assertFalse(hasBgPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1, CHANGE_WIFI_STATE)); } @Test @@ -172,15 +190,150 @@ public class PermissionMonitorTest { @Test public void testHasUseBackgroundNetworksPermissionVendorApp() throws Exception { - assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID)); - assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CHANGE_NETWORK_STATE)); - assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, NETWORK_STACK)); - assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CONNECTIVITY_INTERNAL)); - assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, + assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1)); + assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE)); + assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, NETWORK_STACK)); + assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL)); + assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); - assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID, CHANGE_WIFI_STATE)); + assertTrue(hasBgPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE)); + + assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1)); + assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CHANGE_WIFI_STATE)); + } + + private class NMSMonitor { + private final HashMap<Integer, Boolean> mApps = new HashMap<>(); + + NMSMonitor(INetworkManagementService mockNMS) throws Exception { + // Add hook to verify and track result of setPermission. + doAnswer((InvocationOnMock invocation) -> { + final Object[] args = invocation.getArguments(); + final Boolean isSystem = args[0].equals("SYSTEM"); + 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); + // } + mApps.put(uid, isSystem); + } + return null; + }).when(mockNMS).setPermission(anyString(), any(int[].class)); + + // Add hook to verify and track result of clearPermission. + doAnswer((InvocationOnMock invocation) -> { + final Object[] args = invocation.getArguments(); + for (final int uid : (int[]) args[0]) { + // 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)) { + // fail("uid " + uid + " does not exist."); + // } + mApps.remove(uid); + } + return null; + }).when(mockNMS).clearPermission(any(int[].class)); + } + + public void expectPermission(Boolean permission, int[] users, int[] apps) { + for (final int user : users) { + for (final int app : apps) { + final int uid = UserHandle.getUid(user, app); + if (!mApps.containsKey(uid)) { + fail("uid " + uid + " does not exist."); + } + if (mApps.get(uid) != permission) { + fail("uid " + uid + " has wrong permission: " + permission); + } + } + } + } + + public void expectNoPermission(int[] users, int[] apps) { + for (final int user : users) { + for (final int app : apps) { + final int uid = UserHandle.getUid(user, app); + if (mApps.containsKey(uid)) { + fail("uid " + uid + " has listed permissions, expected none."); + } + } + } + } + } + + @Test + public void testUserAndPackageAddRemove() throws Exception { + final NMSMonitor mNMSMonitor = new NMSMonitor(mNMS); - assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID)); - assertFalse(hasBgPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID, CHANGE_WIFI_STATE)); + // MOCK_UID1: MOCK_PACKAGE1 only has network permission. + // SYSTEM_UID: SYSTEM_PACKAGE1 has system permission. + // SYSTEM_UID: SYSTEM_PACKAGE2 only has network permission. + doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(eq(SYSTEM), anyString()); + doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(any(), + eq(SYSTEM_PACKAGE1)); + doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(), + eq(SYSTEM_PACKAGE2)); + doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(), + eq(MOCK_PACKAGE1)); + + // Add SYSTEM_PACKAGE2, expect only have network permission. + mPermissionMonitor.onUserAdded(MOCK_USER1); + addPackageForUsers(new int[]{MOCK_USER1}, SYSTEM_PACKAGE2, SYSTEM_UID); + mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1}, new int[]{SYSTEM_UID}); + + // Add SYSTEM_PACKAGE1, expect permission escalate. + addPackageForUsers(new int[]{MOCK_USER1}, SYSTEM_PACKAGE1, SYSTEM_UID); + mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1}, new int[]{SYSTEM_UID}); + + mPermissionMonitor.onUserAdded(MOCK_USER2); + mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1, MOCK_USER2}, + new int[]{SYSTEM_UID}); + + addPackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_UID1); + mNMSMonitor.expectPermission(SYSTEM, new int[]{MOCK_USER1, MOCK_USER2}, + new int[]{SYSTEM_UID}); + mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1, MOCK_USER2}, + new int[]{MOCK_UID1}); + + // Remove MOCK_UID1, expect no permission left for all user. + mPermissionMonitor.onPackageRemoved(MOCK_UID1); + removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, MOCK_UID1); + mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2}, new int[]{MOCK_UID1}); + + // Remove SYSTEM_PACKAGE1, expect permission downgrade. + when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{SYSTEM_PACKAGE2}); + removePackageForUsers(new int[]{MOCK_USER1, MOCK_USER2}, SYSTEM_UID); + mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER1, MOCK_USER2}, + new int[]{SYSTEM_UID}); + + mPermissionMonitor.onUserRemoved(MOCK_USER1); + mNMSMonitor.expectPermission(NETWORK, new int[]{MOCK_USER2}, new int[]{SYSTEM_UID}); + + // Remove all packages, expect no permission left. + when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{}); + removePackageForUsers(new int[]{MOCK_USER2}, SYSTEM_UID); + mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2}, + new int[]{SYSTEM_UID, MOCK_UID1}); + + // Remove last user, expect no redundant clearPermission is invoked. + mPermissionMonitor.onUserRemoved(MOCK_USER2); + mNMSMonitor.expectNoPermission(new int[]{MOCK_USER1, MOCK_USER2}, + new int[]{SYSTEM_UID, MOCK_UID1}); + } + + // Normal package add/remove operations will trigger multiple intent for uids corresponding to + // each user. To simulate generic package operations, the onPackageAdded/Removed will need to be + // called multiple times with the uid corresponding to each user. + private void addPackageForUsers(int[] users, String packageName, int uid) { + for (final int user : users) { + mPermissionMonitor.onPackageAdded(packageName, UserHandle.getUid(user, uid)); + } + } + + private void removePackageForUsers(int[] users, int uid) { + for (final int user : users) { + mPermissionMonitor.onPackageRemoved(UserHandle.getUid(user, uid)); + } } } |