Allow caller to specify target to start in setQuietModeEnabled

So, UMS can start the target once user is unlocked.

Test: No secure lock. Try turn off and on work mode by tapping work app.
Test: Have secure lock. Try turn off and on work mode by tapping work app.
Test: Turn off work mode. Reboot. Try to tap on any work app to turn on work mode.

BUG:69926710

Change-Id: Iaaccd5d763f7e36e5a43bad5261f1eb16060f9d6
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 21e4227..84c07ec 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -667,4 +667,10 @@
 
      void setShowWhenLocked(in IBinder token, boolean showWhenLocked);
      void setTurnScreenOn(in IBinder token, boolean turnScreenOn);
+
+     /**
+      *  Similar to {@link #startUserInBackground(int userId), but with a listener to report
+      *  user unlock progress.
+      */
+     boolean startUserInBackgroundWithListener(int userid, IProgressListener unlockProgressListener);
 }
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 0620fa3..9c90c38 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -79,7 +79,7 @@
     void setDefaultGuestRestrictions(in Bundle restrictions);
     Bundle getDefaultGuestRestrictions();
     boolean markGuestForDeletion(int userHandle);
-    void setQuietModeEnabled(int userHandle, boolean enableQuietMode);
+    void setQuietModeEnabled(int userHandle, boolean enableQuietMode, in IntentSender target);
     boolean isQuietModeEnabled(int userHandle);
     boolean trySetQuietModeDisabled(int userHandle, in IntentSender target);
     void setSeedAccountData(int userHandle, in String accountName,
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 22967af..61dd462 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2115,7 +2115,7 @@
      */
     public void setQuietModeEnabled(@UserIdInt int userHandle, boolean enableQuietMode) {
         try {
-            mService.setQuietModeEnabled(userHandle, enableQuietMode);
+            mService.setQuietModeEnabled(userHandle, enableQuietMode, null);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -2142,10 +2142,14 @@
      * unlocking. If the user is already unlocked, we call through to {@link #setQuietModeEnabled}
      * directly.
      *
-     * @return true if the quiet mode was disabled immediately
+     * @param userHandle The user that is going to disable quiet mode.
+     * @param target The target to launch when the user is unlocked.
+     * @return {@code true} if quiet mode is disabled without showing confirm credentials screen,
+     *         {@code false} otherwise.
      * @hide
      */
-    public boolean trySetQuietModeDisabled(@UserIdInt int userHandle, IntentSender target) {
+    public boolean trySetQuietModeDisabled(
+            @UserIdInt int userHandle, @Nullable IntentSender target) {
         try {
             return mService.trySetQuietModeDisabled(userHandle, target);
         } catch (RemoteException re) {
diff --git a/core/java/com/android/internal/app/UnlaunchableAppActivity.java b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
index 0a539f1..8016a65 100644
--- a/core/java/com/android/internal/app/UnlaunchableAppActivity.java
+++ b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
@@ -111,14 +111,7 @@
     @Override
     public void onClick(DialogInterface dialog, int which) {
         if (mReason == UNLAUNCHABLE_REASON_QUIET_MODE && which == DialogInterface.BUTTON_POSITIVE) {
-            if (UserManager.get(this).trySetQuietModeDisabled(mUserId, mTarget)
-                    && mTarget != null) {
-                try {
-                    startIntentSenderForResult(mTarget, -1, null, 0, 0, 0);
-                } catch (IntentSender.SendIntentException e) {
-                    /* ignore */
-                }
-            }
+            UserManager.get(this).trySetQuietModeDisabled(mUserId, mTarget);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5e67396..6c5129b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -23824,7 +23824,13 @@
      */
     @Override
     public boolean startUserInBackground(final int userId) {
-        return mUserController.startUser(userId, /* foreground */ false);
+        return startUserInBackgroundWithListener(userId, null);
+    }
+
+    @Override
+    public boolean startUserInBackgroundWithListener(final int userId,
+                @Nullable IProgressListener unlockListener) {
+        return mUserController.startUser(userId, /* foreground */ false, unlockListener);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 4e3d8d2..6bd599b 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -24,6 +24,7 @@
 import static android.app.ActivityManager.USER_OP_SUCCESS;
 import static android.os.Process.SHELL_UID;
 import static android.os.Process.SYSTEM_UID;
+
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -37,6 +38,7 @@
 import static com.android.server.am.UserState.STATE_RUNNING_UNLOCKING;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.AppGlobals;
@@ -830,6 +832,9 @@
     private IStorageManager getStorageManager() {
         return IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
     }
+    boolean startUser(final int userId, final boolean foreground) {
+        return startUser(userId, foreground, null);
+    }
 
     /**
      * Start user, if its not already running.
@@ -860,7 +865,10 @@
      * @param foreground true if user should be brought to the foreground
      * @return true if the user has been successfully started
      */
-    boolean startUser(final int userId, final boolean foreground) {
+    boolean startUser(
+            final int userId,
+            final boolean foreground,
+            @Nullable IProgressListener unlockListener) {
         if (mInjector.checkCallingPermission(INTERACT_ACROSS_USERS_FULL)
                 != PackageManager.PERMISSION_GRANTED) {
             String msg = "Permission Denial: switchUser() from pid="
@@ -918,6 +926,10 @@
                 final Integer userIdInt = userId;
                 mUserLru.remove(userIdInt);
                 mUserLru.add(userIdInt);
+
+                if (unlockListener != null) {
+                    uss.mUnlockProgress.addListener(unlockListener);
+                }
             }
             if (updateUmState) {
                 mInjector.getUserManagerInternal().setUserState(userId, uss.state);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index dbf413f..dc481ca 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -27,7 +27,6 @@
 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;
@@ -50,6 +49,7 @@
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.IProgressListener;
 import android.os.IUserManager;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
@@ -71,10 +71,6 @@
 import android.os.storage.StorageManager;
 import android.security.GateKeeper;
 import android.service.gatekeeper.IGateKeeperService;
-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;
@@ -113,9 +109,9 @@
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.io.IOException;
 import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
@@ -378,22 +374,45 @@
     private final BroadcastReceiver mDisableQuietModeCallback = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            if (ACTION_DISABLE_QUIET_MODE_AFTER_UNLOCK.equals(intent.getAction())) {
-                final IntentSender target = intent.getParcelableExtra(Intent.EXTRA_INTENT);
-                final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_ID, 0);
-                setQuietModeEnabled(userHandle, false);
-                if (target != null) {
-                    try {
-                        mContext.startIntentSender(target, null, 0, 0, 0);
-                    } catch (IntentSender.SendIntentException e) {
-                        /* ignore */
-                    }
-                }
+            if (!ACTION_DISABLE_QUIET_MODE_AFTER_UNLOCK.equals(intent.getAction())) {
+                return;
             }
+            final IntentSender target = intent.getParcelableExtra(Intent.EXTRA_INTENT);
+            final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_NULL);
+            setQuietModeEnabled(userHandle, false, target);
         }
     };
 
     /**
+     * Start an {@link IntentSender} when user is unlocked after disabling quiet mode.
+     *
+     * @see {@link #trySetQuietModeDisabled(int, IntentSender)}
+     */
+    private class DisableQuietModeUserUnlockedCallback extends IProgressListener.Stub {
+        private final IntentSender mTarget;
+
+        public DisableQuietModeUserUnlockedCallback(IntentSender target) {
+            Preconditions.checkNotNull(target);
+            mTarget = target;
+        }
+
+        @Override
+        public void onStarted(int id, Bundle extras) {}
+
+        @Override
+        public void onProgress(int id, int progress, Bundle extras) {}
+
+        @Override
+        public void onFinished(int id, Bundle extras) {
+            try {
+                mContext.startIntentSender(mTarget, null, 0, 0, 0);
+            } catch (IntentSender.SendIntentException e) {
+                Slog.e(LOG_TAG, "Failed to start the target in the callback", e);
+            }
+        }
+    }
+
+    /**
      * Whether all users should be created ephemeral.
      */
     @GuardedBy("mUsersLock")
@@ -765,7 +784,7 @@
     }
 
     @Override
-    public void setQuietModeEnabled(int userHandle, boolean enableQuietMode) {
+    public void setQuietModeEnabled(int userHandle, boolean enableQuietMode, IntentSender target) {
         checkManageUsersPermission("silence profile");
         boolean changed = false;
         UserInfo profile, parent;
@@ -792,7 +811,11 @@
                     LocalServices.getService(ActivityManagerInternal.class)
                             .killForegroundAppsForUser(userHandle);
                 } else {
-                    ActivityManager.getService().startUserInBackground(userHandle);
+                    IProgressListener callback = target != null
+                            ? new DisableQuietModeUserUnlockedCallback(target)
+                            : null;
+                    ActivityManager.getService().startUserInBackgroundWithListener(
+                            userHandle, callback);
                 }
             } catch (RemoteException e) {
                 Slog.e(LOG_TAG, "fail to start/stop user for quiet mode", e);
@@ -820,12 +843,13 @@
     }
 
     @Override
-    public boolean trySetQuietModeDisabled(int userHandle, IntentSender target) {
+    public boolean trySetQuietModeDisabled(
+            @UserIdInt int userHandle, @Nullable IntentSender target) {
         checkManageUsersPermission("silence profile");
         if (StorageManager.isUserKeyUnlocked(userHandle)
                 || !mLockPatternUtils.isSecure(userHandle)) {
             // if the user is already unlocked, no need to show a profile challenge
-            setQuietModeEnabled(userHandle, false);
+            setQuietModeEnabled(userHandle, false, target);
             return true;
         }