Retail mode flag and some clients for it

Bug: 27280140
Change-Id: Ide33e941b9c71eb925b5977d0b0d62198537ca14
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index dd3a36c..6cd8403 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -28,8 +28,8 @@
  */
 public class UserInfo implements Parcelable {
 
-    /** 8 bits for user type */
-    public static final int FLAG_MASK_USER_TYPE = 0x000000FF;
+    /** 16 bits for user type */
+    public static final int FLAG_MASK_USER_TYPE = 0x0000FFFF;
 
     /**
      * *************************** NOTE ***************************
@@ -87,6 +87,11 @@
      */
     public static final int FLAG_EPHEMERAL = 0x00000100;
 
+    /**
+     * User is for demo purposes only and can be removed at any time.
+     */
+    public static final int FLAG_DEMO = 0x00000200;
+
     public static final int NO_PROFILE_GROUP_ID = UserHandle.USER_NULL;
 
     public int id;
@@ -153,6 +158,10 @@
         return (flags & FLAG_INITIALIZED) == FLAG_INITIALIZED;
     }
 
+    public boolean isDemo() {
+        return (flags & FLAG_DEMO) == FLAG_DEMO;
+    }
+
     /**
      * Returns true if the user is a split system user.
      * <p>If {@link UserManager#isSplitSystemUser split system user mode} is not enabled,
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index dcec982..2613994 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1972,6 +1972,10 @@
         if (!supportsMultipleUsers()) {
             return false;
         }
+        // If Demo Mode is on, don't show user switcher
+        if (isDeviceInDemoMode(mContext)) {
+            return false;
+        }
         List<UserInfo> users = getUsers(true);
         if (users == null) {
            return false;
@@ -1988,6 +1992,14 @@
     }
 
     /**
+     * @hide
+     */
+    public static boolean isDeviceInDemoMode(Context context) {
+        return Settings.Global.getInt(context.getContentResolver(),
+                Settings.Global.DEVICE_DEMO_MODE, 0) > 0;
+    }
+
+    /**
      * Returns a serial number on this device for a given userHandle. User handles can be recycled
      * when deleting and creating users, but serial numbers are not reused until the device is wiped.
      * @param userHandle
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f04b2d4..a5c2500 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8538,6 +8538,15 @@
         public static final String SAFE_BOOT_DISALLOWED = "safe_boot_disallowed";
 
         /**
+         * Whether this device is currently in retail demo mode. If true, device
+         * usage is severely limited.
+         * <p>
+         * Type: int (0 for false, 1 for true)
+         * @hide
+         */
+        public static final String DEVICE_DEMO_MODE = "device_demo_mode";
+
+        /**
          * Settings to backup. This is here so that it's in the same place as the settings
          * keys and easy to update.
          *
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index e64d905..904523a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2481,4 +2481,7 @@
     <string-array translatable="false" name="config_defaultPinnerServiceFiles">
     </string-array>
 
+    <!-- Component that is the default launcher when demo mode is enabled. -->
+    <string name="config_demoModeLauncherComponent"></string>
+
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9b7fbd8..ae38fef 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1104,6 +1104,7 @@
   <java-symbol type="string" name="lockscreen_transport_pause_description" />
   <java-symbol type="string" name="config_ethernet_tcp_buffers" />
   <java-symbol type="string" name="config_wifi_tcp_buffers" />
+  <java-symbol type="string" name="config_demoModeLauncherComponent" />
 
   <java-symbol type="plurals" name="bugreport_countdown" />
   <java-symbol type="plurals" name="duration_hours" />
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index cad7f64..c8e74ec 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -399,8 +399,7 @@
             sendUserPresentBroadcast();
             synchronized (KeyguardViewMediator.this) {
                 // If system user is provisioned, we might want to lock now to avoid showing launcher
-                if (UserManager.isSplitSystemUser()
-                        && KeyguardUpdateMonitor.getCurrentUser() == UserHandle.USER_SYSTEM) {
+                if (mustNotUnlockCurrentUser()) {
                     doKeyguardLocked(null);
                 }
             }
@@ -599,7 +598,6 @@
                 return KeyguardSecurityView.PROMPT_REASON_WRONG_CREDENTIAL;
             }
 
-
             return KeyguardSecurityView.PROMPT_REASON_NONE;
         }
     };
@@ -608,6 +606,11 @@
         mPM.userActivity(SystemClock.uptimeMillis(), false);
     }
 
+    boolean mustNotUnlockCurrentUser() {
+        return (UserManager.isSplitSystemUser() || UserManager.isDeviceInDemoMode(mContext))
+                && KeyguardUpdateMonitor.getCurrentUser() == UserHandle.USER_SYSTEM;
+    }
+
     private void setupLocked() {
         mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mWM = WindowManagerGlobal.getWindowManagerService();
@@ -1176,8 +1179,7 @@
         }
 
         // In split system user mode, we never unlock system user.
-        if (!UserManager.isSplitSystemUser()
-                || KeyguardUpdateMonitor.getCurrentUser() != UserHandle.USER_SYSTEM
+        if (!mustNotUnlockCurrentUser()
                 || !mUpdateMonitor.isDeviceProvisioned()) {
 
             // if the setup wizard hasn't run yet, don't show
@@ -1615,8 +1617,7 @@
         synchronized (KeyguardViewMediator.this) {
             if (DEBUG) Log.d(TAG, "handleHide");
 
-            if (UserManager.isSplitSystemUser()
-                    && KeyguardUpdateMonitor.getCurrentUser() == UserHandle.USER_SYSTEM) {
+            if (mustNotUnlockCurrentUser()) {
                 // In split system user mode, we never unlock system user. The end user has to
                 // switch to another user.
                 // TODO: We should stop it early by disabling the swipe up flow. Right now swipe up
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index c4e39c9..112a6bee 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -223,6 +223,8 @@
 
     private void finishUserBoot(UserState uss, IIntentReceiver resultTo) {
         final int userId = uss.mHandle.getIdentifier();
+        boolean deviceInDemoMode = UserManager.isDeviceInDemoMode(mService.mContext);
+        boolean switchToDemoUser = false;
         Slog.d(TAG, "Finishing user boot " + userId);
         synchronized (mService) {
             // Bail if we ended up with a stale user
@@ -259,7 +261,32 @@
             } else {
                 maybeUnlockUser(userId);
             }
+            if (deviceInDemoMode && mCurrentUserId == UserHandle.USER_SYSTEM) {
+                switchToDemoUser = true;
+            }
         }
+
+        if (switchToDemoUser) {
+            doSwitchToDemoUser();
+        }
+    }
+
+    private void doSwitchToDemoUser() {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                // Remove any demo users first
+                for (UserInfo user: mUserManager.getUsers(true)) {
+                    if (user.isDemo()) {
+                        mUserManager.removeUser(user.id);
+                    }
+                }
+                UserInfo demoUser = mUserManager.createUser("Demo", UserInfo.FLAG_DEMO);
+                if (demoUser != null) {
+                    mService.switchUser(demoUser.id);
+                }
+            }
+        });
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 33ccf16..ff17552 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -28,6 +28,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityManagerNative;
+import android.app.AppGlobals;
 import android.app.IActivityManager;
 import android.app.IStopUserCallback;
 import android.app.KeyguardManager;
@@ -70,6 +71,7 @@
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
+import android.text.TextUtils;
 import android.util.AtomicFile;
 import android.util.IntArray;
 import android.util.Log;
@@ -414,7 +416,9 @@
             // user restriction was not a default guest restriction.
             setUserRestriction(UserManager.DISALLOW_CONFIG_WIFI, true, currentGuestUser.id);
         }
-    }
+
+        maybeInitializeDemoMode(UserHandle.USER_SYSTEM);
+}
 
     @Override
     public String getUserAccount(int userId) {
@@ -2710,6 +2714,8 @@
                 mPm.onBeforeUserStartUninitialized(userId);
             }
         }
+
+        maybeInitializeDemoMode(userId);
     }
 
     /**
@@ -2724,6 +2730,7 @@
 
     /**
      * Make a note of the last started time of a user and do some cleanup.
+     * This is called with ActivityManagerService lock held.
      * @param userId the user that was just foregrounded
      */
     public void onUserLoggedIn(@UserIdInt int userId) {
@@ -2741,6 +2748,24 @@
         scheduleWriteUser(userData);
     }
 
+    private void maybeInitializeDemoMode(int userId) {
+        if (UserManager.isDeviceInDemoMode(mContext)) {
+            String demoLauncher =
+                    mContext.getResources().getString(
+                            com.android.internal.R.string.config_demoModeLauncherComponent);
+            if (!TextUtils.isEmpty(demoLauncher)) {
+                ComponentName componentToEnable = ComponentName.unflattenFromString(demoLauncher);
+                try {
+                    AppGlobals.getPackageManager().setComponentEnabledSetting(componentToEnable,
+                            PackageManager.COMPONENT_ENABLED_STATE_ENABLED, /* flags= */ 0,
+                            /* userId= */ userId);
+                } catch (RemoteException re) {
+                    // Internal, shouldn't happen
+                }
+            }
+        }
+    }
+
     /**
      * Returns the next available user id, filling in any holes in the ids.
      * TODO: May not be a good idea to recycle ids, in case it results in confusion