Work Challenge: Handle Recents launches
Intercept calls to start activities from the recents
stack and show the Work Challenge if needed. This requires
passing the taskId to ConfirmDeviceCredential so it can
launch the recents task itself when the credentials are
confirmed.
Change-Id: I013b134f3f31a35b551ad683c68cc89b8af44499
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 998f980..e1adc87 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4407,7 +4407,6 @@
throw new IllegalArgumentException("startActivityFromRecentsInner: Task "
+ taskId + " can't be launch in the home stack.");
}
-
task = mStackSupervisor.anyTaskForIdLocked(taskId, RESTORE_FROM_RECENTS, launchStackId);
if (task == null) {
throw new IllegalArgumentException(
@@ -4426,7 +4425,10 @@
}
}
- if (task.getRootActivity() != null) {
+ // If the user must confirm credentials (e.g. when first launching a work app and the
+ // Work Challenge is present) let startActivityInPackage handle the intercepting.
+ if (!mUserController.shouldConfirmCredentials(task.userId)
+ && task.getRootActivity() != null) {
moveTaskToFrontLocked(task.taskId, 0, bOptions);
return ActivityManager.START_TASK_TO_FRONT;
}
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index f2864d4..f712613 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1,6 +1,7 @@
package com.android.server.am;
import static android.app.Activity.RESULT_CANCELED;
+import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY;
import static android.app.ActivityManager.START_CLASS_NOT_FOUND;
import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
import static android.app.ActivityManager.START_FLAG_ONLY_IF_NEEDED;
@@ -15,8 +16,16 @@
import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.PendingIntent.FLAG_CANCEL_CURRENT;
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
+import static android.app.PendingIntent.FLAG_ONE_SHOT;
+import static android.content.Context.KEYGUARD_SERVICE;
+import static android.content.Intent.EXTRA_INTENT;
+import static android.content.Intent.EXTRA_PACKAGE_NAME;
+import static android.content.Intent.EXTRA_TASK_ID;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_TO_SIDE;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
@@ -66,7 +75,6 @@
import android.app.PendingIntent;
import android.app.ProfilerInfo;
import android.content.ComponentName;
-import android.content.Context;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentSender;
@@ -91,7 +99,6 @@
import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.widget.LockPatternUtils;
import com.android.server.am.ActivityStackSupervisor.PendingActivityLaunch;
import com.android.server.wm.WindowManagerService;
@@ -358,37 +365,25 @@
}
}
- UserInfo user = mSupervisor.getUserInfo(userId);
- KeyguardManager km = (KeyguardManager) mService.mContext
- .getSystemService(Context.KEYGUARD_SERVICE);
- if (user.isManagedProfile()
- && LockPatternUtils.isSeparateWorkChallengeEnabled()
- && km.isDeviceLocked(userId)) {
- IIntentSender target = mService.getIntentSenderLocked(
- ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
- Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ intent },
- new String[]{ resolvedType },
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
- | PendingIntent.FLAG_IMMUTABLE, null);
- final int flags = intent.getFlags();
- final Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, user.id);
- if (newIntent != null) {
- intent = newIntent;
- intent.setFlags(flags
- | Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- intent.putExtra(Intent.EXTRA_PACKAGE_NAME, aInfo.packageName);
- intent.putExtra(Intent.EXTRA_INTENT, new IntentSender(target));
-
- resolvedType = null;
- callingUid = realCallingUid;
- callingPid = realCallingPid;
-
- UserInfo parent = UserManager.get(mService.mContext).getProfileParent(userId);
- rInfo = mSupervisor.resolveIntent(intent, resolvedType, parent.id);
- aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags,
- null /*profilerInfo*/);
+ final Intent interceptingIntent = interceptWithConfirmCredentialsIfNeeded(intent,
+ resolvedType, aInfo, callingPackage, userId);
+ if (interceptingIntent != null) {
+ intent = interceptingIntent;
+ callingPid = realCallingPid;
+ callingUid = realCallingUid;
+ resolvedType = null;
+ // If we are intercepting and there was a task, convert it into an extra for the
+ // ConfirmCredentials intent and unassign it, as otherwise the task will move to
+ // front even if ConfirmCredentials is cancelled.
+ if (inTask != null) {
+ intent.putExtra(EXTRA_TASK_ID, inTask.taskId);
+ inTask = null;
}
+
+ final UserInfo parent = UserManager.get(mService.mContext).getProfileParent(userId);
+ rInfo = mSupervisor.resolveIntent(intent, resolvedType, parent.id);
+ aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags,
+ null /*profilerInfo*/);
}
if (abort) {
@@ -538,6 +533,34 @@
return err;
}
+ /**
+ * Creates an intent to intercept the current activity start with Confirm Credentials if needed.
+ *
+ * @return The intercepting intent if needed.
+ */
+ private Intent interceptWithConfirmCredentialsIfNeeded(Intent intent, String resolvedType,
+ ActivityInfo aInfo, String callingPackage, int userId) {
+ if (!mService.mUserController.shouldConfirmCredentials(userId)) {
+ return null;
+ }
+ final IIntentSender target = mService.getIntentSenderLocked(
+ INTENT_SENDER_ACTIVITY, callingPackage,
+ Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ intent },
+ new String[]{ resolvedType },
+ FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE, null);
+ final int flags = intent.getFlags();
+ final KeyguardManager km = (KeyguardManager) mService.mContext
+ .getSystemService(KEYGUARD_SERVICE);
+ final Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, userId);
+ if (newIntent == null) {
+ return null;
+ }
+ newIntent.setFlags(flags | FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ newIntent.putExtra(EXTRA_PACKAGE_NAME, aInfo.packageName);
+ newIntent.putExtra(EXTRA_INTENT, new IntentSender(target));
+ return newIntent;
+ }
+
void startHomeActivityLocked(Intent intent, ActivityInfo aInfo, String reason) {
mSupervisor.moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE, reason);
startActivityLocked(null /*caller*/, intent, null /*ephemeralIntent*/,
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index f2c5206..7172859 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -22,6 +22,7 @@
import static android.app.ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
import static android.app.ActivityManager.USER_OP_IS_CURRENT;
import static android.app.ActivityManager.USER_OP_SUCCESS;
+import static android.content.Context.KEYGUARD_SERVICE;
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;
@@ -43,6 +44,7 @@
import android.app.Dialog;
import android.app.IStopUserCallback;
import android.app.IUserSwitchObserver;
+import android.app.KeyguardManager;
import android.content.Context;
import android.content.IIntentReceiver;
import android.content.Intent;
@@ -73,6 +75,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.widget.LockPatternUtils;
import com.android.server.pm.UserManagerService;
import java.io.PrintWriter;
@@ -1286,6 +1289,20 @@
return mCurrentProfileIds;
}
+ /**
+ * Returns whether the given user requires credential entry at this time. This is used to
+ * intercept activity launches for work apps when the Work Challenge is present.
+ */
+ boolean shouldConfirmCredentials(int userId) {
+ final UserInfo user = getUserInfo(userId);
+ if (!user.isManagedProfile() || !LockPatternUtils.isSeparateWorkChallengeEnabled()) {
+ return false;
+ }
+ final KeyguardManager km = (KeyguardManager) mService.mContext
+ .getSystemService(KEYGUARD_SERVICE);
+ return km.isDeviceLocked(user.id);
+ }
+
void dump(PrintWriter pw, boolean dumpAll) {
pw.println(" mStartedUsers:");
for (int i = 0; i < mStartedUsers.size(); i++) {