Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ea9b10e6 authored by Clara Bayarri's avatar Clara Bayarri
Browse files

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
parent 514b2cf0
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -3603,6 +3603,15 @@ public class Intent implements Parcelable, Cloneable {
     */
    public static final String EXTRA_USER_ID = "android.intent.extra.USER_ID";

    /**
     * An int representing the task id to be retrieved. This is used when a launch from recents is
     * intercepted by another action such as credentials confirmation to remember which task should
     * be resumed when complete.
     *
     * @hide
     */
    public static final String EXTRA_TASK_ID = "android.intent.extra.TASK_ID";

    /**
     * An Intent[] describing additional, alternate choices you would like shown with
     * {@link #ACTION_CHOOSER}.
+4 −2
Original line number Diff line number Diff line
@@ -4407,7 +4407,6 @@ public final class ActivityManagerService extends ActivityManagerNative
                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 @@ public final class ActivityManagerService extends ActivityManagerNative
                }
            }
            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;
            }
+55 −32
Original line number Diff line number Diff line
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.FULLSCREEN_WORKSPACE_STACK_ID;
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.KeyguardManager;
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 android.view.Display;

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,38 +365,26 @@ class ActivityStarter {
            }
        }

        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;
        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;
            }

                UserInfo parent = UserManager.get(mService.mContext).getProfileParent(userId);
            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) {
            if (resultRecord != null) {
@@ -538,6 +533,34 @@ class ActivityStarter {
        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*/,
+17 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import static android.app.ActivityManager.USER_OP_ERROR_IS_SYSTEM;
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.AppOpsManager;
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 android.util.SparseIntArray;
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 @@ final class UserController {
        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++) {