Maybe decrypt user when quiet mode is disabled

When quiet mode is disabled for a user and that user is not currently
decrypted, we show a confirm credentials screen to trigger decryption
of that user. Only if that was successful, do we actually disable quiet
mode.

Bug: 27764124
Change-Id: Ib1f649194d89e225dad62c14f3ddba1fa3d79da2
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 67d3959..55b0d2a 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -20,6 +20,7 @@
 import android.os.Bundle;
 import android.os.PersistableBundle;
 import android.content.pm.UserInfo;
+import android.content.IntentSender;
 import android.content.RestrictionEntry;
 import android.graphics.Bitmap;
 import android.os.ParcelFileDescriptor;
@@ -70,6 +71,7 @@
     boolean markGuestForDeletion(int userHandle);
     void setQuietModeEnabled(int userHandle, boolean enableQuietMode);
     boolean isQuietModeEnabled(int userHandle);
+    boolean trySetQuietModeDisabled(int userHandle, in IntentSender target);
     void setSeedAccountData(int userHandle, in String accountName,
             in String accountType, in PersistableBundle accountOptions, boolean persist);
     String getSeedAccountName();
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index d5b3b35..086a977 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -29,6 +29,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentSender;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -1691,6 +1692,23 @@
     }
 
     /**
+     * Tries disabling quiet mode for a given user. If the user is still locked, we unlock the user
+     * first by showing the confirm credentials screen and disable quiet mode upon successful
+     * unlocking. If the user is already unlocked, we call through to {@link #setQuietModeEnabled}
+     * directly.
+     *
+     * @return true if the quiet mode was disabled immediately
+     * @hide
+     */
+    public boolean trySetQuietModeDisabled(@UserIdInt int userHandle, IntentSender target) {
+        try {
+            return mService.trySetQuietModeDisabled(userHandle, target);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * If the target user is a managed profile of the calling user or the caller
      * is itself a managed profile, then this returns a badged copy of the given
      * icon to be able to distinguish it from the original icon. For badging an
diff --git a/core/java/com/android/internal/app/UnlaunchableAppActivity.java b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
index 27588e9..d24cefe 100644
--- a/core/java/com/android/internal/app/UnlaunchableAppActivity.java
+++ b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
@@ -107,9 +107,8 @@
     @Override
     public void onClick(DialogInterface dialog, int which) {
         if (mReason == UNLAUNCHABLE_REASON_QUIET_MODE && which == DialogInterface.BUTTON_POSITIVE) {
-            UserManager.get(this).setQuietModeEnabled(mUserId, false);
-
-            if (mTarget != null) {
+            if (UserManager.get(this).trySetQuietModeDisabled(mUserId, mTarget)
+                    && mTarget != null) {
                 try {
                     startIntentSenderForResult(mTarget, -1, null, 0, 0, 0);
                 } catch (IntentSender.SendIntentException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java
index 41eed56..951b096 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ManagedProfileController.java
@@ -15,6 +15,7 @@
 package com.android.systemui.statusbar.phone;
 
 import android.app.ActivityManager;
+import android.app.StatusBarManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -57,10 +58,18 @@
         }
     }
 
-    public void setWorkModeEnabled(boolean enabled) {
+    public void setWorkModeEnabled(boolean enableWorkMode) {
         synchronized (mProfiles) {
             for (UserInfo ui : mProfiles) {
-                mUserManager.setQuietModeEnabled(ui.id, !enabled);
+                if (enableWorkMode) {
+                    if (!mUserManager.trySetQuietModeDisabled(ui.id, null)) {
+                        StatusBarManager statusBarManager = (StatusBarManager) mContext
+                                .getSystemService(android.app.Service.STATUS_BAR_SERVICE);
+                        statusBarManager.collapsePanels();
+                    }
+                } else {
+                    mUserManager.setQuietModeEnabled(ui.id, true);
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index f2b4e52..54237a9 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -702,6 +702,12 @@
             }
         };
 
+        // Check if the user is currently in quiet mode and start it otherwise
+        if (mUserManager.isQuietModeEnabled(new UserHandle(userId))
+                && mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
+            mUserManager.setQuietModeEnabled(userId, false);
+        }
+
         try {
             ActivityManagerNative.getDefault().unlockUser(userId, token, secret, listener);
         } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 60ea254..9b918f3 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -17,6 +17,9 @@
 
 package com.android.server.pm;
 
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -27,10 +30,12 @@
 import android.app.ActivityManagerNative;
 import android.app.IActivityManager;
 import android.app.IStopUserCallback;
+import android.app.KeyguardManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.IntentSender;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.UserInfo;
@@ -693,6 +698,37 @@
     }
 
     @Override
+    public boolean trySetQuietModeDisabled(int userHandle, IntentSender target) {
+        if (mContext.getSystemService(StorageManager.class).isUserKeyUnlocked(userHandle)
+                || !mLockPatternUtils.isSecure(userHandle)
+                || !mLockPatternUtils.isSeparateProfileChallengeEnabled(userHandle)) {
+            // if the user is already unlocked, no need to show a profile challenge
+            setQuietModeEnabled(userHandle, false);
+            return true;
+        }
+
+        long identity = Binder.clearCallingIdentity();
+        try {
+            // otherwise, we show a profile challenge to trigger decryption of the user
+            final KeyguardManager km = (KeyguardManager) mContext.getSystemService(
+                    Context.KEYGUARD_SERVICE);
+            final Intent unlockIntent = km.createConfirmDeviceCredentialIntent(null, null,
+                    userHandle);
+            if (unlockIntent == null) {
+                return false;
+            }
+            if (target != null) {
+                unlockIntent.putExtra(Intent.EXTRA_INTENT, target);
+            }
+            unlockIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+            mContext.startActivity(unlockIntent);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+        return false;
+    }
+
+    @Override
     public void setUserEnabled(int userId) {
         checkManageUsersPermission("enable user");
         synchronized (mPackagesLock) {