Add managed profile whitelist to control NotificationListenerServices
Only let notification listeners installed in the primary profile
see work profile notification if allowed by policy
Bug: 36657192
Test: runtest systemui-notification
Test: runtest -c com.android.server.devicepolicy.DevicePolicyManagerTest frameworks-services
Change-Id: If719151644380e9162180a24d12f798e42867c0a
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 721d337..10f5ba4 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -1216,7 +1216,8 @@
}
/**
- * Setup a package in the package manager mock. Useful for faking installed applications.
+ * Setup a package in the package manager mock for {@link DpmMockContext#CALLER_USER_HANDLE}.
+ * Useful for faking installed applications.
*
* @param packageName the name of the package to be setup
* @param appId the application ID to be given to the package
@@ -1224,19 +1225,41 @@
*/
private int setupPackageInPackageManager(final String packageName, final int appId)
throws Exception {
+ return setupPackageInPackageManager(
+ packageName, DpmMockContext.CALLER_USER_HANDLE, appId,
+ ApplicationInfo.FLAG_HAS_CODE);
+ }
+
+ /**
+ * Setup a package in the package manager mock. Useful for faking installed applications.
+ *
+ * @param packageName the name of the package to be setup
+ * @param userId the user id where the package will be "installed"
+ * @param appId the application ID to be given to the package
+ * @param flags flags to set in the ApplicationInfo for this package
+ * @return the UID of the package as known by the mock package manager
+ */
+ private int setupPackageInPackageManager(
+ final String packageName, int userId, final int appId, int flags)
+ throws Exception {
// Make the PackageManager return the package instead of throwing a NameNotFoundException
final PackageInfo pi = new PackageInfo();
pi.applicationInfo = new ApplicationInfo();
- pi.applicationInfo.flags = ApplicationInfo.FLAG_HAS_CODE;
+ pi.applicationInfo.flags = flags;
doReturn(pi).when(mContext.ipackageManager).getPackageInfo(
eq(packageName),
anyInt(),
- eq(DpmMockContext.CALLER_USER_HANDLE));
+ eq(userId));
+ doReturn(pi.applicationInfo).when(mContext.ipackageManager).getApplicationInfo(
+ eq(packageName),
+ anyInt(),
+ eq(userId));
+
// Setup application UID with the PackageManager
- final int uid = UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE, appId);
+ final int uid = UserHandle.getUid(userId, appId);
doReturn(uid).when(mContext.packageManager).getPackageUidAsUser(
eq(packageName),
- eq(DpmMockContext.CALLER_USER_HANDLE));
+ eq(userId));
// Associate packageName to uid
doReturn(packageName).when(mContext.ipackageManager).getNameForUid(eq(uid));
doReturn(new String[]{packageName})
@@ -3970,6 +3993,210 @@
assertFalse(dpm.isCurrentInputMethodSetByOwner());
}
+ public void testSetPermittedCrossProfileNotificationListeners_unavailableForDo()
+ throws Exception {
+ // Set up a device owner.
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupDeviceOwner();
+ assertSetPermittedCrossProfileNotificationListenersUnavailable(mContext.binder.callingUid);
+ }
+
+ public void testSetPermittedCrossProfileNotificationListeners_unavailableForPoOnUser()
+ throws Exception {
+ // Set up a profile owner.
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ setupProfileOwner();
+ assertSetPermittedCrossProfileNotificationListenersUnavailable(mContext.binder.callingUid);
+ }
+
+ private void assertSetPermittedCrossProfileNotificationListenersUnavailable(
+ int adminUid) throws Exception {
+ mContext.binder.callingUid = adminUid;
+ final int userId = UserHandle.getUserId(adminUid);
+
+ final String packageName = "some.package";
+ assertFalse(dpms.setPermittedCrossProfileNotificationListeners(
+ admin1, Collections.singletonList(packageName)));
+ assertNull(dpms.getPermittedCrossProfileNotificationListeners(admin1));
+
+ mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+ assertTrue(dpms.isNotificationListenerServicePermitted(packageName, userId));
+
+ // Attempt to set to empty list (which means no listener is whitelisted)
+ mContext.binder.callingUid = adminUid;
+ assertFalse(dpms.setPermittedCrossProfileNotificationListeners(
+ admin1, Collections.<String>emptyList()));
+ assertNull(dpms.getPermittedCrossProfileNotificationListeners(admin1));
+
+ mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+ assertTrue(dpms.isNotificationListenerServicePermitted(packageName, userId));
+ }
+
+ public void testIsNotificationListenerServicePermitted_onlySystemCanCall() throws Exception {
+ // Set up a managed profile
+ final int MANAGED_PROFILE_USER_ID = 15;
+ final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 19436);
+ addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
+ mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
+
+ final String permittedListener = "some.package";
+ setupPackageInPackageManager(
+ permittedListener,
+ UserHandle.USER_SYSTEM, // We check the packageInfo from the primary user.
+ /*appId=*/ 12345, /*flags=*/ 0);
+
+ assertTrue(dpms.setPermittedCrossProfileNotificationListeners(
+ admin1, Collections.singletonList(permittedListener)));
+
+ try {
+ dpms.isNotificationListenerServicePermitted(
+ permittedListener, MANAGED_PROFILE_USER_ID);
+ fail("isNotificationListenerServicePermitted should throw if not called from System");
+ } catch (SecurityException expected) {
+ }
+
+ mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+ assertTrue(dpms.isNotificationListenerServicePermitted(
+ permittedListener, MANAGED_PROFILE_USER_ID));
+ }
+
+ public void testSetPermittedCrossProfileNotificationListeners_managedProfile()
+ throws Exception {
+ // Set up a managed profile
+ final int MANAGED_PROFILE_USER_ID = 15;
+ final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 19436);
+ addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
+ mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
+
+ final String permittedListener = "permitted.package";
+ int appId = 12345;
+ setupPackageInPackageManager(
+ permittedListener,
+ UserHandle.USER_SYSTEM, // We check the packageInfo from the primary user.
+ appId, /*flags=*/ 0);
+
+ final String notPermittedListener = "not.permitted.package";
+ setupPackageInPackageManager(
+ notPermittedListener,
+ UserHandle.USER_SYSTEM, // We check the packageInfo from the primary user.
+ ++appId, /*flags=*/ 0);
+
+ final String systemListener = "system.package";
+ setupPackageInPackageManager(
+ systemListener,
+ UserHandle.USER_SYSTEM, // We check the packageInfo from the primary user.
+ ++appId, ApplicationInfo.FLAG_SYSTEM);
+
+ // By default all packages are allowed
+ assertNull(dpms.getPermittedCrossProfileNotificationListeners(admin1));
+
+ mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+ assertTrue(dpms.isNotificationListenerServicePermitted(
+ permittedListener, MANAGED_PROFILE_USER_ID));
+ assertTrue(dpms.isNotificationListenerServicePermitted(
+ notPermittedListener, MANAGED_PROFILE_USER_ID));
+ assertTrue(dpms.isNotificationListenerServicePermitted(
+ systemListener, MANAGED_PROFILE_USER_ID));
+
+ // Setting only one package in the whitelist
+ mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
+ assertTrue(dpms.setPermittedCrossProfileNotificationListeners(
+ admin1, Collections.singletonList(permittedListener)));
+ List<String> permittedListeners =
+ dpms.getPermittedCrossProfileNotificationListeners(admin1);
+ assertEquals(1, permittedListeners.size());
+ assertEquals(permittedListener, permittedListeners.get(0));
+
+ mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+ assertTrue(dpms.isNotificationListenerServicePermitted(
+ permittedListener, MANAGED_PROFILE_USER_ID));
+ assertFalse(dpms.isNotificationListenerServicePermitted(
+ notPermittedListener, MANAGED_PROFILE_USER_ID));
+ // System packages are always allowed (even if not in the whitelist)
+ assertTrue(dpms.isNotificationListenerServicePermitted(
+ systemListener, MANAGED_PROFILE_USER_ID));
+
+ // Setting an empty whitelist - only system listeners allowed
+ mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
+ assertTrue(dpms.setPermittedCrossProfileNotificationListeners(
+ admin1, Collections.<String>emptyList()));
+ assertEquals(0, dpms.getPermittedCrossProfileNotificationListeners(admin1).size());
+
+ mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+ assertFalse(dpms.isNotificationListenerServicePermitted(
+ permittedListener, MANAGED_PROFILE_USER_ID));
+ assertFalse(dpms.isNotificationListenerServicePermitted(
+ notPermittedListener, MANAGED_PROFILE_USER_ID));
+ // System packages are always allowed (even if not in the whitelist)
+ assertTrue(dpms.isNotificationListenerServicePermitted(
+ systemListener, MANAGED_PROFILE_USER_ID));
+
+ // Setting a null whitelist - all listeners allowed
+ mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
+ assertTrue(dpms.setPermittedCrossProfileNotificationListeners(admin1, null));
+ assertNull(dpms.getPermittedCrossProfileNotificationListeners(admin1));
+
+ mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+ assertTrue(dpms.isNotificationListenerServicePermitted(
+ permittedListener, MANAGED_PROFILE_USER_ID));
+ assertTrue(dpms.isNotificationListenerServicePermitted(
+ notPermittedListener, MANAGED_PROFILE_USER_ID));
+ assertTrue(dpms.isNotificationListenerServicePermitted(
+ systemListener, MANAGED_PROFILE_USER_ID));
+ }
+
+ public void testSetPermittedCrossProfileNotificationListeners_doesNotAffectPrimaryProfile()
+ throws Exception {
+ // Set up a managed profile
+ final int MANAGED_PROFILE_USER_ID = 15;
+ final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 19436);
+ addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
+ mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
+
+ final String nonSystemPackage = "non.system.package";
+ int appId = 12345;
+ setupPackageInPackageManager(
+ nonSystemPackage,
+ UserHandle.USER_SYSTEM, // We check the packageInfo from the primary user.
+ appId, /*flags=*/ 0);
+
+ final String systemListener = "system.package";
+ setupPackageInPackageManager(
+ systemListener,
+ UserHandle.USER_SYSTEM, // We check the packageInfo from the primary user.
+ ++appId, ApplicationInfo.FLAG_SYSTEM);
+
+ // By default all packages are allowed (for all profiles)
+ assertNull(dpms.getPermittedCrossProfileNotificationListeners(admin1));
+
+ mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+ assertTrue(dpms.isNotificationListenerServicePermitted(
+ nonSystemPackage, MANAGED_PROFILE_USER_ID));
+ assertTrue(dpms.isNotificationListenerServicePermitted(
+ systemListener, MANAGED_PROFILE_USER_ID));
+ assertTrue(dpms.isNotificationListenerServicePermitted(
+ nonSystemPackage, UserHandle.USER_SYSTEM));
+ assertTrue(dpms.isNotificationListenerServicePermitted(
+ systemListener, UserHandle.USER_SYSTEM));
+
+ // Setting an empty whitelist - only system listeners allowed in managed profile, but
+ // all allowed in primary profile
+ mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
+ assertTrue(dpms.setPermittedCrossProfileNotificationListeners(
+ admin1, Collections.<String>emptyList()));
+ assertEquals(0, dpms.getPermittedCrossProfileNotificationListeners(admin1).size());
+
+ mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+ assertFalse(dpms.isNotificationListenerServicePermitted(
+ nonSystemPackage, MANAGED_PROFILE_USER_ID));
+ assertTrue(dpms.isNotificationListenerServicePermitted(
+ systemListener, MANAGED_PROFILE_USER_ID));
+ assertTrue(dpms.isNotificationListenerServicePermitted(
+ nonSystemPackage, UserHandle.USER_SYSTEM));
+ assertTrue(dpms.isNotificationListenerServicePermitted(
+ systemListener, UserHandle.USER_SYSTEM));
+ }
+
public void testGetOwnerInstalledCaCertsForDeviceOwner() throws Exception {
mContext.packageName = mRealTestContext.getPackageName();
setDeviceOwner();