diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 22480738c5878..ac6fc5855dfb8 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -1480,6 +1480,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return "/data/system/"; } + void registerContentObserver(Uri uri, boolean notifyForDescendents, + ContentObserver observer, int userHandle) { + mContext.getContentResolver().registerContentObserver(uri, notifyForDescendents, + observer, userHandle); + } + int settingsSecureGetIntForUser(String name, int def, int userHandle) { return Settings.Secure.getIntForUser(mContext.getContentResolver(), name, def, userHandle); @@ -2536,7 +2542,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { onStartUser(UserHandle.USER_SYSTEM); // Register an observer for watching for user setup complete. - new SetupContentObserver(mHandler).register(mContext.getContentResolver()); + new SetupContentObserver(mHandler).register(); // Initialize the user setup state, to handle the upgrade case. updateUserSetupComplete(); @@ -7847,9 +7853,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { super(handler); } - void register(ContentResolver resolver) { - resolver.registerContentObserver(mUserSetupComplete, false, this, UserHandle.USER_ALL); - resolver.registerContentObserver(mDeviceProvisioned, false, this, UserHandle.USER_ALL); + void register() { + mInjector.registerContentObserver(mUserSetupComplete, false, this, UserHandle.USER_ALL); + mInjector.registerContentObserver(mDeviceProvisioned, false, this, UserHandle.USER_ALL); } @Override diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java index 35777ce86c4a6..744443f1d7e74 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java @@ -15,14 +15,14 @@ */ package com.android.server.devicepolicy; -import com.android.internal.widget.LockPatternUtils; - import android.app.IActivityManager; import android.app.NotificationManager; import android.app.backup.IBackupManager; import android.content.pm.IPackageManager; import android.content.pm.PackageManagerInternal; +import android.database.ContentObserver; import android.media.IAudioService; +import android.net.Uri; import android.os.Looper; import android.os.PowerManagerInternal; import android.os.UserHandle; @@ -30,12 +30,15 @@ import android.os.UserManager; import android.os.UserManagerInternal; import android.os.storage.StorageManager; import android.telephony.TelephonyManager; +import android.util.ArrayMap; +import android.util.Log; +import android.util.Pair; import android.view.IWindowManager; -import java.io.File; +import com.android.internal.widget.LockPatternUtils; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.when; +import java.io.File; +import java.util.Map; /** * Overrides {@link #DevicePolicyManagerService} for dependency injection. @@ -77,6 +80,7 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi } public final DpmMockContext context; + private final MockInjector mMockInjector; public DevicePolicyManagerServiceTestable(DpmMockContext context, File dataDir) { this(new MockInjector(context, dataDir)); @@ -84,15 +88,36 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi private DevicePolicyManagerServiceTestable(MockInjector injector) { super(injector); + mMockInjector = injector; this.context = injector.context; } + + public void notifyChangeToContentObserver(Uri uri, int userHandle) { + ContentObserver co = mMockInjector.mContentObservers + .get(new Pair(uri, userHandle)); + if (co != null) { + co.onChange(false, uri, userHandle); // notify synchronously + } + + // Notify USER_ALL observer too. + co = mMockInjector.mContentObservers + .get(new Pair(uri, UserHandle.USER_ALL)); + if (co != null) { + co.onChange(false, uri, userHandle); // notify synchronously + } + } + + private static class MockInjector extends Injector { public final DpmMockContext context; public final File dataDir; + // Key is a pair of uri and userId + private final Map, ContentObserver> mContentObservers = new ArrayMap<>(); + private MockInjector(DpmMockContext context, File dataDir) { super(context); this.context = context; @@ -264,6 +289,12 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi return context.userManagerForMock.isSplitSystemUser(); } + @Override + void registerContentObserver(Uri uri, boolean notifyForDescendents, + ContentObserver observer, int userHandle) { + mContentObservers.put(new Pair(uri, userHandle), observer); + } + @Override int settingsSecureGetIntForUser(String name, int def, int userHandle) { return context.settings.settingsSecureGetIntForUser(name, def, userHandle); 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 e6963d52e88a6..3a2e946e617fb 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -543,10 +543,31 @@ public class DevicePolicyManagerTest extends DpmTestBase { } /** - * Test for: {@link DevicePolicyManager#setDeviceOwner} DO on system user installs - * successfully. + * Test for: {@link DevicePolicyManager#setDeviceOwner} DO on system user installs successfully. */ public void testSetDeviceOwner() throws Exception { + setDeviceOwner(); + + // Try to set a profile owner on the same user, which should fail. + setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_SYSTEM_USER_UID); + dpm.setActiveAdmin(admin2, /* refreshing= */ true, UserHandle.USER_SYSTEM); + try { + dpm.setProfileOwner(admin2, "owner-name", UserHandle.USER_SYSTEM); + fail("IllegalStateException not thrown"); + } catch (IllegalStateException expected) { + assertTrue("Message was: " + expected.getMessage(), + expected.getMessage().contains("already has a device owner")); + } + + // DO admin can't be deactivated. + dpm.removeActiveAdmin(admin1); + assertTrue(dpm.isAdminActive(admin1)); + + // TODO Test getDeviceOwnerName() too. To do so, we need to change + // DPMS.getApplicationLabel() because Context.createPackageContextAsUser() is not mockable. + } + + private void setDeviceOwner() throws Exception { mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS); mContext.callerPermissions.add(permission.MANAGE_USERS); mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS); @@ -594,24 +615,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { MockUtils.checkUserHandle(UserHandle.USER_SYSTEM)); assertEquals(admin1, dpm.getDeviceOwnerComponentOnAnyUser()); - - // Try to set a profile owner on the same user, which should fail. - setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_SYSTEM_USER_UID); - dpm.setActiveAdmin(admin2, /* refreshing= */ true, UserHandle.USER_SYSTEM); - try { - dpm.setProfileOwner(admin2, "owner-name", UserHandle.USER_SYSTEM); - fail("IllegalStateException not thrown"); - } catch (IllegalStateException expected) { - assertTrue("Message was: " + expected.getMessage(), - expected.getMessage().contains("already has a device owner")); - } - - // DO admin can't be deactivated. - dpm.removeActiveAdmin(admin1); - assertTrue(dpm.isAdminActive(admin1)); - - // TODO Test getDeviceOwnerName() too. To do so, we need to change - // DPMS.getApplicationLabel() because Context.createPackageContextAsUser() is not mockable. } private void checkGetDeviceOwnerInfoApi(DevicePolicyManager dpm, boolean hasDeviceOwner) { @@ -1934,5 +1937,211 @@ public class DevicePolicyManagerTest extends DpmTestBase { // TODO Verify calls to settingsGlobalPutInt. Tried but somehow mockito threw // UnfinishedVerificationException. } -} + public void testIsProvisioningAllowed_DeviceAdminFeatureOff() throws Exception { + when(mContext.packageManager.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN)) + .thenReturn(false); + when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)) + .thenReturn(false); + initializeDpms(); + when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(false); + when(mContext.userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true)) + .thenReturn(true); + setUserSetupCompleteForUser(false, UserHandle.USER_SYSTEM); + + mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; + + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, false); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE, + false); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER, false); + } + + public void testIsProvisioningAllowed_ManagedProfileFeatureOff() throws Exception { + when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)) + .thenReturn(false); + initializeDpms(); + when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(false); + when(mContext.userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true)) + .thenReturn(true); + setUserSetupCompleteForUser(false, UserHandle.USER_SYSTEM); + + mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; + + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, true); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE, + false); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER, false); + + // Test again when split user is on + when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, true); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE, + true); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER, false); + } + + public void testIsProvisioningAllowed_nonSplitUser_firstBoot_primaryUser() throws Exception { + when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)) + .thenReturn(true); + when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(false); + when(mContext.userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true)) + .thenReturn(true); + setUserSetupCompleteForUser(false, UserHandle.USER_SYSTEM); + + mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; + + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, true); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE, + false /* because of non-split user */); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER, + false /* because of non-split user */); + } + + public void testIsProvisioningAllowed_nonSplitUser_afterDeviceSetup_primaryUser() + throws Exception { + when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)) + .thenReturn(true); + when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(false); + when(mContext.userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true)) + .thenReturn(true); + setUserSetupCompleteForUser(true, UserHandle.USER_SYSTEM); + + mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; + + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, + false/* because of completed device setup */); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE, + false/* because of non-split user */); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER, + false/* because of non-split user */); + } + + public void testIsProvisioningAllowed_splitUser_firstBoot_systemUser() throws Exception { + when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)) + .thenReturn(true); + when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true); + when(mContext.userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true)) + .thenReturn(false); + setUserSetupCompleteForUser(false, UserHandle.USER_SYSTEM); + + mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; + + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, true); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, + false /* because canAddMoreManagedProfiles returns false */); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE, + true); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER, + false/* because calling uid is system user */); + + } + + public void testIsProvisioningAllowed_splitUser_afterDeviceSetup_systemUser() throws Exception { + when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)) + .thenReturn(true); + when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true); + when(mContext.userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true)) + .thenReturn(false); + setUserSetupCompleteForUser(true, UserHandle.USER_SYSTEM); + + mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; + + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, + true/* it's undefined behavior. Can be changed into false in the future */); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, + false /* because canAddMoreManagedProfiles returns false */); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE, + true/* it's undefined behavior. Can be changed into false in the future */); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER, + false/* because calling uid is system user */); + } + + public void testIsProvisioningAllowed_splitUser_firstBoot_primaryUser() throws Exception { + when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)) + .thenReturn(true); + when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true); + when(mContext.userManager.canAddMoreManagedProfiles(DpmMockContext.CALLER_USER_HANDLE, + true)).thenReturn(true); + setUserSetupCompleteForUser(false, DpmMockContext.CALLER_USER_HANDLE); + + mContext.binder.callingUid = DpmMockContext.CALLER_UID; + + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, true); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE, + true); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER, true); + + } + + public void testIsProvisioningAllowed_splitUser_afterDeviceSetup_primaryUser() + throws Exception { + when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)) + .thenReturn(true); + when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true); + when(mContext.userManager.canAddMoreManagedProfiles(DpmMockContext.CALLER_USER_HANDLE, + true)).thenReturn(true); + setUserSetupCompleteForUser(true, DpmMockContext.CALLER_USER_HANDLE); + + mContext.binder.callingUid = DpmMockContext.CALLER_UID; + + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE, + true/* it's undefined behavior. Can be changed into false in the future */); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE, + true/* it's undefined behavior. Can be changed into false in the future */); + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_USER, + false/* because user setup completed */); + } + + public void testIsProvisioningAllowed_provisionManagedProfileWithDeviceOwner_systemUser() + throws Exception { + setDeviceOwner(); + + when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)) + .thenReturn(true); + when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true); + when(mContext.userManager.canAddMoreManagedProfiles(UserHandle.USER_SYSTEM, true)) + .thenReturn(false); + setUserSetupCompleteForUser(true, UserHandle.USER_SYSTEM); + + mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; + + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, + false /* can't provision managed profile on system user */); + } + + public void testIsProvisioningAllowed_provisionManagedProfileWithDeviceOwner_primaryUser() + throws Exception { + setDeviceOwner(); + + when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)) + .thenReturn(true); + when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true); + when(mContext.userManager.canAddMoreManagedProfiles(DpmMockContext.CALLER_USER_HANDLE, + true)).thenReturn(true); + setUserSetupCompleteForUser(false, DpmMockContext.CALLER_USER_HANDLE); + + mContext.binder.callingUid = DpmMockContext.CALLER_UID; + + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true); + } + + private void setUserSetupCompleteForUser(boolean isUserSetupComplete, int userhandle) { + when(mContext.settings.settingsSecureGetIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, + userhandle)).thenReturn(isUserSetupComplete ? 1 : 0); + dpms.notifyChangeToContentObserver( + Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), userhandle); + } + + private void assertProvisioningAllowed(String action, boolean expected) { + assertEquals("isProvisioningAllowed(" + action + ") returning unexpected result", expected, + dpm.isProvisioningAllowed(action)); + } +} diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java index 8e2ef703f3c67..60d7382a72331 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java @@ -53,6 +53,7 @@ import java.io.File; import java.util.ArrayList; import java.util.List; +import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; @@ -172,36 +173,36 @@ public class DpmMockContext extends MockContext { } public static class SettingsForMock { - int settingsSecureGetIntForUser(String name, int def, int userHandle) { + public int settingsSecureGetIntForUser(String name, int def, int userHandle) { return 0; } - void settingsSecurePutIntForUser(String name, int value, int userHandle) { + public void settingsSecurePutIntForUser(String name, int value, int userHandle) { } - void settingsSecurePutStringForUser(String name, String value, int userHandle) { + public void settingsSecurePutStringForUser(String name, String value, int userHandle) { } - void settingsGlobalPutStringForUser(String name, String value, int userHandle) { + public void settingsGlobalPutStringForUser(String name, String value, int userHandle) { } - void settingsSecurePutInt(String name, int value) { + public void settingsSecurePutInt(String name, int value) { } - void settingsGlobalPutInt(String name, int value) { + public void settingsGlobalPutInt(String name, int value) { } - void settingsSecurePutString(String name, String value) { + public void settingsSecurePutString(String name, String value) { } - void settingsGlobalPutString(String name, String value) { + public void settingsGlobalPutString(String name, String value) { } - int settingsGlobalGetInt(String name, int def) { + public int settingsGlobalGetInt(String name, int value) { return 0; } - void securityLogSetLoggingEnabledProperty(boolean enabled) { + public void securityLogSetLoggingEnabledProperty(boolean enabled) { } public boolean securityLogGetLoggingEnabledProperty() { @@ -321,6 +322,8 @@ public class DpmMockContext extends MockContext { mUserInfos.add(uh); when(userManager.getUsers()).thenReturn(mUserInfos); + when(userManager.getUsers(anyBoolean())).thenReturn(mUserInfos); + when(userManager.isUserRunning(eq(new UserHandle(userId)))).thenReturn(true); when(userManager.getUserInfo(anyInt())).thenAnswer( new Answer() { @Override