Evict CE key on request and when work mode is turned off.

DPMS.lockNow takes a flag which can request the managed profile CE key to
be evicted.

Test: com.android.cts.devicepolicy.ManagedProfileTest#testLockNowWithKeyEviction*
Bug: 31000719
Change-Id: I68f4d6eed4b041c39fd13375f7f284f5d6ac33da
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 4d6ffe6..a5552b8 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -176,12 +176,8 @@
         }
 
         @Override
-        public void onBootPhase(int phase) {
-            if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
-                mLockSettingsService.maybeShowEncryptionNotifications();
-            } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
-                // TODO
-            }
+        public void onStartUser(int userHandle) {
+            mLockSettingsService.onStartUser(userHandle);
         }
 
         @Override
@@ -313,27 +309,25 @@
      * If the account is credential-encrypted, show notification requesting the user to unlock
      * the device.
      */
-    private void maybeShowEncryptionNotifications() {
-        final List<UserInfo> users = mUserManager.getUsers();
-        for (int i = 0; i < users.size(); i++) {
-            UserInfo user = users.get(i);
-            UserHandle userHandle = user.getUserHandle();
-            final boolean isSecure = mStorage.hasPassword(user.id) || mStorage.hasPattern(user.id);
-            if (isSecure && !mUserManager.isUserUnlockingOrUnlocked(userHandle)) {
-                if (!user.isManagedProfile()) {
-                    // When the user is locked, we communicate it loud-and-clear
-                    // on the lockscreen; we only show a notification below for
-                    // locked managed profiles.
-                } else {
-                    UserInfo parent = mUserManager.getProfileParent(user.id);
-                    if (parent != null &&
-                            mUserManager.isUserUnlockingOrUnlocked(parent.getUserHandle()) &&
-                            !mUserManager.isQuietModeEnabled(userHandle)) {
-                        // Only show notifications for managed profiles once their parent
-                        // user is unlocked.
-                        showEncryptionNotificationForProfile(userHandle);
-                    }
-                }
+    private void maybeShowEncryptionNotificationForUser(@UserIdInt int userId) {
+        final UserInfo user = mUserManager.getUserInfo(userId);
+        if (!user.isManagedProfile()) {
+            // When the user is locked, we communicate it loud-and-clear
+            // on the lockscreen; we only show a notification below for
+            // locked managed profiles.
+            return;
+        }
+
+        final UserHandle userHandle = user.getUserHandle();
+        final boolean isSecure = mStorage.hasPassword(userId) || mStorage.hasPattern(userId);
+        if (isSecure && !mUserManager.isUserUnlockingOrUnlocked(userHandle)) {
+            UserInfo parent = mUserManager.getProfileParent(userId);
+            if (parent != null &&
+                    mUserManager.isUserUnlockingOrUnlocked(parent.getUserHandle()) &&
+                    !mUserManager.isQuietModeEnabled(userHandle)) {
+                // Only show notifications for managed profiles once their parent
+                // user is unlocked.
+                showEncryptionNotificationForProfile(userHandle);
             }
         }
     }
@@ -384,7 +378,7 @@
         mNotificationManager.notifyAsUser(null, FBE_ENCRYPTED_NOTIFICATION, notification, user);
     }
 
-    public void hideEncryptionNotification(UserHandle userHandle) {
+    private void hideEncryptionNotification(UserHandle userHandle) {
         if (DEBUG) Slog.v(TAG, "hide encryption notification, user: "+ userHandle.getIdentifier());
         mNotificationManager.cancelAsUser(null, FBE_ENCRYPTED_NOTIFICATION, userHandle);
     }
@@ -393,6 +387,10 @@
         hideEncryptionNotification(new UserHandle(userId));
     }
 
+    public void onStartUser(final int userId) {
+        maybeShowEncryptionNotificationForUser(userId);
+    }
+
     public void onUnlockUser(final int userId) {
         // Hide notification first, as tie managed profile lock takes time
         hideEncryptionNotification(new UserHandle(userId));
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index eae4905..f7c0275 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -22566,6 +22566,11 @@
         }
     }
 
+    @Override
+    public int restartUserInBackground(final int userId) {
+        return mUserController.restartUser(userId, /* foreground */ false);
+    }
+
     /**
      * Attach an agent to the specified process (proces name or PID)
      */
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index a0a04bb..45e06b0 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -442,6 +442,19 @@
         }
     }
 
+    int restartUser(final int userId, final boolean foreground) {
+        return stopUser(userId, /* force */ true, new IStopUserCallback.Stub() {
+            @Override
+            public void userStopped(final int userId) {
+                // Post to the same handler that this callback is called from to ensure the user
+                // cleanup is complete before restarting.
+                mHandler.post(() -> startUser(userId, foreground));
+            }
+            @Override
+            public void userStopAborted(final int userId) {}
+        });
+    }
+
     int stopUser(final int userId, final boolean force, final IStopUserCallback callback) {
         if (mInjector.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
                 != PackageManager.PERMISSION_GRANTED) {
@@ -634,6 +647,12 @@
         }
 
         if (stopped) {
+            // Evict the user's credential encryption key
+            try {
+                getStorageManager().lockUserKey(userId);
+            } catch (RemoteException re) {
+                throw re.rethrowAsRuntimeException();
+            }
             mInjector.systemServiceManagerCleanupUser(userId);
             synchronized (mLock) {
                 mInjector.stackSupervisorRemoveUserLocked(userId);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 05228ec..9b47beb 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -26,6 +26,7 @@
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
+import android.app.ActivityManagerNative;
 import android.app.AppGlobals;
 import android.app.IActivityManager;
 import android.app.IStopUserCallback;
@@ -857,6 +858,25 @@
         }
     }
 
+    /**
+     * Evicts a user's CE key by stopping and restarting the user.
+     *
+     * The key is evicted automatically by the user controller when the user has stopped.
+     */
+    @Override
+    public void evictCredentialEncryptionKey(@UserIdInt int userId) {
+        checkManageUsersPermission("evict CE key");
+        final IActivityManager am = ActivityManagerNative.getDefault();
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            am.restartUserInBackground(userId);
+        } catch (RemoteException re) {
+            throw re.rethrowAsRuntimeException();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
     @Override
     public UserInfo getUserInfo(int userId) {
         checkManageOrCreateUsersPermission("query user");
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index cca8cc8..c69b87c 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -25,6 +25,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import android.Manifest;
+import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.admin.DevicePolicyManager;
 import android.app.trust.ITrustListener;
@@ -103,6 +104,7 @@
     private static final int MSG_SWITCH_USER = 9;
     private static final int MSG_FLUSH_TRUST_USUALLY_MANAGED = 10;
     private static final int MSG_UNLOCK_USER = 11;
+    private static final int MSG_STOP_USER = 12;
 
     private static final int TRUST_USUALLY_MANAGED_FLUSH_DELAY = 2 * 60 * 1000;
 
@@ -414,15 +416,18 @@
                 }
             }
             boolean deviceLocked = secure && showingKeyguard && !trusted;
+            setDeviceLockedForUser(id, deviceLocked);
+        }
+    }
 
-            boolean changed;
-            synchronized (mDeviceLockedForUser) {
-                changed = isDeviceLockedInner(id) != deviceLocked;
-                mDeviceLockedForUser.put(id, deviceLocked);
-            }
-            if (changed) {
-                dispatchDeviceLocked(id, deviceLocked);
-            }
+    private void setDeviceLockedForUser(@UserIdInt int userId, boolean locked) {
+        final boolean changed;
+        synchronized (mDeviceLockedForUser) {
+            changed = isDeviceLockedInner(userId) != locked;
+            mDeviceLockedForUser.put(userId, locked);
+        }
+        if (changed) {
+            dispatchDeviceLocked(userId, locked);
         }
     }
 
@@ -724,6 +729,11 @@
         mHandler.obtainMessage(MSG_UNLOCK_USER, userId, 0, null).sendToTarget();
     }
 
+    @Override
+    public void onStopUser(@UserIdInt int userId) {
+        mHandler.obtainMessage(MSG_STOP_USER, userId, 0, null).sendToTarget();
+    }
+
     // Plumbing
 
     private final IBinder mService = new ITrustManager.Stub() {
@@ -982,6 +992,9 @@
                     mCurrentUser = msg.arg1;
                     refreshDeviceLockedForUser(UserHandle.USER_ALL);
                     break;
+                case MSG_STOP_USER:
+                    setDeviceLockedForUser(msg.arg1, true);
+                    break;
                 case MSG_FLUSH_TRUST_USUALLY_MANAGED:
                     SparseBooleanArray usuallyManaged;
                     synchronized (mTrustUsuallyManagedForUser) {