From 361b825c0dc0d2b0bb1726da7c31c92f3c4e8a35 Mon Sep 17 00:00:00 2001 From: Christine Franks Date: Fri, 23 Jun 2017 18:12:46 -0700 Subject: [PATCH] Support demo mode and demo users Bug: 62712426 Test: run cts -m CtsDevicePolicyManagerTestCases -t \ com.android.cts.devicepolicy.DeviceOwnerTest#testCreateAndManageEphemeralUser and run cts -m CtsDevicePolicyManagerTestCases -t \ com.android.cts.devicepolicy.DeviceOwnerTest# \ testCreateAndManageEphemeralUserFailsWithoutSplitSystemUser and runtest -c com.android.server.devicepolicy.DevicePolicyManagerTest \ frameworks-services Change-Id: I77a71a994fe0f4f1f8c5df7c4ccf493aafa8fefe --- .../android/app/admin/DevicePolicyManager.java | 7 +++ .../devicepolicy/DevicePolicyManagerService.java | 36 ++++++++--- .../devicepolicy/DevicePolicyManagerTest.java | 72 +++++++++++++++++++++- .../server/devicepolicy/DpmMockContext.java | 5 ++ 4 files changed, 112 insertions(+), 8 deletions(-) diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index ea0829f2ef2d..a749fe70fb94 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -5982,6 +5982,13 @@ public class DevicePolicyManager { public static final int MAKE_USER_EPHEMERAL = 0x0002; /** + * Flag used by {@link #createAndManageUser} to specify that the user should be created as a + * demo user. + * @hide + */ + public static final int MAKE_USER_DEMO = 0x0004; + + /** * Called by a device owner to create a user with the specified name and a given component of * the calling package as profile owner. The UserHandle returned by this method should not be * persisted as user handles are recycled as users are removed and created. If you need to diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 8cac6e051e6b..73bb13a63612 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -526,7 +526,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // TODO(b/35385311): Keep track of metadata in TrustedCertificateStore instead. Set mOwnerInstalledCaCerts = new ArraySet<>(); - // Used for initialization of users created by createAndManageUsers. + // Used for initialization of users created by createAndManageUser. boolean mAdminBroadcastPending = false; PersistableBundle mInitBundle = null; @@ -4255,6 +4255,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mInjector.binderRestoreCallingIdentity(token); } } + @Override public boolean resetPassword(String passwordOrNull, int flags) throws RemoteException { final int callingUid = mInjector.binderGetCallingUid(); @@ -7375,9 +7376,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private void enableIfNecessary(String packageName, int userId) { try { - ApplicationInfo ai = mIPackageManager.getApplicationInfo(packageName, - PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, - userId); + final ApplicationInfo ai = mIPackageManager.getApplicationInfo(packageName, + PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, userId); if (ai.enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) { mIPackageManager.setApplicationEnabledSetting(packageName, @@ -8131,8 +8131,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!mInjector.binderGetCallingUserHandle().isSystem()) { throw new SecurityException("createAndManageUser was called from non-system user"); } - if (!mInjector.userManagerIsSplitSystemUser() - && (flags & DevicePolicyManager.MAKE_USER_EPHEMERAL) != 0) { + final boolean ephemeral = (flags & DevicePolicyManager.MAKE_USER_EPHEMERAL) != 0; + final boolean demo = (flags & DevicePolicyManager.MAKE_USER_DEMO) != 0 + && UserManager.isDeviceInDemoMode(mContext); + if (ephemeral && !mInjector.userManagerIsSplitSystemUser() && !demo) { throw new IllegalArgumentException( "Ephemeral users are only supported on systems with a split system user."); } @@ -8144,9 +8146,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { final long id = mInjector.binderClearCallingIdentity(); try { int userInfoFlags = 0; - if ((flags & DevicePolicyManager.MAKE_USER_EPHEMERAL) != 0) { + if (ephemeral) { userInfoFlags |= UserInfo.FLAG_EPHEMERAL; } + if (demo) { + userInfoFlags |= UserInfo.FLAG_DEMO; + } UserInfo userInfo = mUserManagerInternal.createUserEvenWhenDisallowed(name, userInfoFlags); if (userInfo != null) { @@ -8178,6 +8183,23 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return null; } + final UserInfo userInfo = getUserInfo(userHandle); + if (userInfo != null && userInfo.isDemo()) { + try { + final ApplicationInfo ai = mIPackageManager.getApplicationInfo(adminPkg, + PackageManager.MATCH_DISABLED_COMPONENTS, userHandle); + final boolean isSystemApp = + ai != null && (ai.flags & (ApplicationInfo.FLAG_SYSTEM + | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0; + if (isSystemApp) { + mIPackageManager.setApplicationEnabledSetting(adminPkg, + PackageManager.COMPONENT_ENABLED_STATE_ENABLED, + PackageManager.DONT_KILL_APP, userHandle, "DevicePolicyManager"); + } + } catch (RemoteException e) { + } + } + setActiveAdmin(profileOwner, true, userHandle); // User is not started yet, the broadcast by setActiveAdmin will not be received. // So we store adminExtras for broadcasting when the user starts for first time. 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 c58b733b8b54..bad9b5b97e1f 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -34,6 +34,7 @@ import static org.mockito.Matchers.isNull; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.nullable; import static org.mockito.Mockito.reset; @@ -53,6 +54,7 @@ import android.app.admin.DevicePolicyManagerInternal; import android.app.admin.PasswordMetrics; import android.content.BroadcastReceiver; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; @@ -338,7 +340,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Next, add one more admin. // Before doing so, update the application info, now it's enabled. setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_UID, - PackageManager.COMPONENT_ENABLED_STATE_ENABLED); + PackageManager.COMPONENT_ENABLED_STATE_DEFAULT); dpm.setActiveAdmin(admin2, /* replace =*/ false); @@ -380,6 +382,74 @@ public class DevicePolicyManagerTest extends DpmTestBase { mContext.callerPermissions.remove("android.permission.INTERACT_ACROSS_USERS_FULL"); } + public void testCreateAndManageUser_demoUserSystemApp() throws Exception { + mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS); + + setDeviceOwner(); + + final int id = UserHandle.getUserId(DpmMockContext.CALLER_UID); + + final UserInfo demoUserInfo = mock(UserInfo.class); + demoUserInfo.id = id; + doReturn(UserHandle.of(id)).when(demoUserInfo).getUserHandle(); + doReturn(true).when(demoUserInfo).isDemo(); + final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + doReturn(demoUserInfo).when(um).getUserInfo(id); + doReturn(demoUserInfo).when(mContext.getUserManagerInternal()) + .createUserEvenWhenDisallowed(anyString(), anyInt()); + + final ApplicationInfo applicationInfo = getServices().ipackageManager.getApplicationInfo( + admin2.getPackageName(), PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, id); + applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM; + doReturn(applicationInfo).when(getServices().ipackageManager).getApplicationInfo( + anyString(), anyInt(), anyInt()); + + final UserHandle userHandle = dpm.createAndManageUser(admin1, "", admin2, null, 0); + + verify(getServices().ipackageManager, times(1)).setApplicationEnabledSetting( + eq(admin2.getPackageName()), + eq(PackageManager.COMPONENT_ENABLED_STATE_ENABLED), + eq(PackageManager.DONT_KILL_APP), + eq(id), + anyString()); + + assertNotNull(userHandle); + } + + public void testCreateAndManageUser_demoUserSystemUpdatedApp() throws Exception { + mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS); + + setDeviceOwner(); + + final int id = UserHandle.getUserId(DpmMockContext.CALLER_UID); + + final UserInfo demoUserInfo = mock(UserInfo.class); + demoUserInfo.id = id; + doReturn(UserHandle.of(id)).when(demoUserInfo).getUserHandle(); + doReturn(true).when(demoUserInfo).isDemo(); + final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + doReturn(demoUserInfo).when(um).getUserInfo(id); + doReturn(demoUserInfo).when(mContext.getUserManagerInternal()) + .createUserEvenWhenDisallowed(anyString(), anyInt()); + + final ApplicationInfo applicationInfo = getServices().ipackageManager.getApplicationInfo( + admin2.getPackageName(), PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, id); + applicationInfo.flags = ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; + doReturn(applicationInfo).when(getServices().ipackageManager).getApplicationInfo( + anyString(), anyInt(), anyInt()); + + final UserHandle userHandle = dpm.createAndManageUser(admin1, "", admin2, null, 0); + + verify(getServices().ipackageManager, times(1)).setApplicationEnabledSetting( + eq(admin2.getPackageName()), + eq(PackageManager.COMPONENT_ENABLED_STATE_ENABLED), + eq(PackageManager.DONT_KILL_APP), + eq(id), + anyString()); + + assertNotNull(userHandle); + } + public void testSetActiveAdmin_multiUsers() throws Exception { final int ANOTHER_USER_ID = 100; 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 408ee9c933a9..97021181c384 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java @@ -29,6 +29,7 @@ import android.content.res.Resources; import android.os.Bundle; import android.os.Handler; import android.os.UserHandle; +import android.os.UserManagerInternal; import android.test.mock.MockContext; import android.util.ArrayMap; @@ -196,6 +197,10 @@ public class DpmMockContext extends MockContext { return mMockSystemServices.packageManager; } + public UserManagerInternal getUserManagerInternal() { + return mMockSystemServices.userManagerInternal; + } + @Override public void enforceCallingOrSelfPermission(String permission, String message) { if (UserHandle.isSameApp(binder.getCallingUid(), SYSTEM_UID)) { -- 2.11.0