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
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ea0829f..a749fe70 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -5982,6 +5982,13 @@
     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 8cac6e0..73bb13a 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 @@
         // TODO(b/35385311): Keep track of metadata in TrustedCertificateStore instead.
         Set<String> 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 @@
             mInjector.binderRestoreCallingIdentity(token);
         }
     }
+
     @Override
     public boolean resetPassword(String passwordOrNull, int flags) throws RemoteException {
         final int callingUid = mInjector.binderGetCallingUid();
@@ -7375,9 +7376,8 @@
 
     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 @@
         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 @@
             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 @@
                 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 c58b733..bad9b5b 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.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.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 @@
         // 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 @@
         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 408ee9c..9702118 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.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 @@
         return mMockSystemServices.packageManager;
     }
 
+    public UserManagerInternal getUserManagerInternal() {
+        return mMockSystemServices.userManagerInternal;
+    }
+
     @Override
     public void enforceCallingOrSelfPermission(String permission, String message) {
         if (UserHandle.isSameApp(binder.getCallingUid(), SYSTEM_UID)) {