Moved some activities implementation to ActivityTaskManagerService (2/n)

Second step in unifying the window hierarchy that is currently split
within AM and WM packages. We move some of the API implementation for
activities from ActivityManagerService.java to
ActivityTaskManagerService.java.

Test: Existing tests pass
Test: go/wm-smoke-auto
Bug: 80414790

Change-Id: I23dcd924493d8ad1e0b6e3a55386fd72b0146605
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index d7caa1a..c6edfe5 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -16,31 +16,140 @@
 
 package com.android.server.am;
 
+import static android.Manifest.permission.BIND_VOICE_INTERACTION;
+import static android.Manifest.permission.CHANGE_CONFIGURATION;
+import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
+import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
 import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
+import static android.Manifest.permission.READ_FRAME_BUFFER;
+import static android.Manifest.permission.REMOVE_TASKS;
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
+import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.app.ActivityManagerInternal.ASSIST_KEY_CONTENT;
+import static android.app.ActivityManagerInternal.ASSIST_KEY_DATA;
+import static android.app.ActivityManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
+import static android.app.ActivityManagerInternal.ASSIST_KEY_STRUCTURE;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
+import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
 import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.os.Process.SYSTEM_UID;
+import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_APPLICATION;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IMMERSIVE;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_FOCUS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_IMMERSIVE;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_LOCKTASK;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityManagerService.ALLOW_FULL_ONLY;
 import static com.android.server.am.ActivityManagerService.ANIMATE;
+import static com.android.server.am.ActivityManagerService.DISPATCH_SCREEN_KEYGUARD_MSG;
+import static com.android.server.am.ActivityManagerService.ENTER_ANIMATION_COMPLETE_MSG;
+import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
+import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
+import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_ONLY;
+import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
+import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
+import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
+import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
 import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
+import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
+import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
+import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
 
+import android.Manifest;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.Activity;
 import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
+import android.app.AppGlobals;
 import android.app.IActivityTaskManager;
+import android.app.IApplicationThread;
+import android.app.IAssistDataReceiver;
+import android.app.ITaskStackListener;
+import android.app.PictureInPictureParams;
+import android.app.ProfilerInfo;
+import android.app.RemoteAction;
+import android.app.WaitResult;
 import android.app.WindowConfiguration;
+import android.app.admin.DevicePolicyCache;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
+import android.app.usage.UsageEvents;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.Point;
 import android.graphics.Rect;
+import android.metrics.LogMaker;
+import android.net.Uri;
 import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.os.TransactionTooLargeException;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.voice.IVoiceInteractionSession;
+import android.service.voice.VoiceInteractionManagerInternal;
+import android.telecom.TelecomManager;
+import android.text.TextUtils;
 import android.util.Slog;
 
-import com.android.server.SystemService;
+import android.view.IRecentsAnimationRunner;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationDefinition;
 
+import com.android.internal.app.AssistUtils;
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.os.logging.MetricsLoggerWrapper;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.internal.policy.KeyguardDismissCallback;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.Watchdog;
+import com.android.server.vr.VrManagerInternal;
+import com.android.server.wm.PinnedStackWindowController;
+
+import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -52,6 +161,11 @@
 public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityTaskManagerService" : TAG_AM;
     private static final String TAG_STACK = TAG + POSTFIX_STACK;
+    private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
+    private static final String TAG_IMMERSIVE = TAG + POSTFIX_IMMERSIVE;
+    private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
+    private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
+    private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
 
     private Context mContext;
     private ActivityManagerService mAm;
@@ -59,10 +173,54 @@
     Object mGlobalLock;
     private ActivityStackSupervisor mStackSupervisor;
 
+    /** State of external calls telling us if the device is awake or asleep. */
+    private boolean mKeyguardShown = false;
+
+    // Wrapper around VoiceInteractionServiceManager
+    private AssistUtils mAssistUtils;
+
+    // VoiceInteraction session ID that changes for each new request except when
+    // being called for multi-window assist in a single session.
+    private int mViSessionId = 1000;
+
+    // How long to wait in getAssistContextExtras for the activity and foreground services
+    // to respond with the result.
+    private static final int PENDING_ASSIST_EXTRAS_TIMEOUT = 500;
+
+    // How long top wait when going through the modern assist (which doesn't need to block
+    // on getting this result before starting to launch its UI).
+    private static final int PENDING_ASSIST_EXTRAS_LONG_TIMEOUT = 2000;
+
+    // How long to wait in getAutofillAssistStructure() for the activity to respond with the result.
+    private static final int PENDING_AUTOFILL_ASSIST_STRUCTURE_TIMEOUT = 2000;
+
+    private final ArrayList<PendingAssistExtras> mPendingAssistExtras = new ArrayList<>();
+
+    boolean mSuppressResizeConfigChanges;
+
+    private final UpdateConfigurationResult mTmpUpdateConfigurationResult =
+            new UpdateConfigurationResult();
+
+    static final class UpdateConfigurationResult {
+        // Configuration changes that were updated.
+        int changes;
+        // If the activity was relaunched to match the new configuration.
+        boolean activityRelaunched;
+
+        void reset() {
+            changes = 0;
+            activityRelaunched = false;
+        }
+    }
+
     ActivityTaskManagerService(Context context) {
         mContext = context;
     }
 
+    void onSystemReady() {
+        mAssistUtils = new AssistUtils(mContext);
+    }
+
     // TODO: Will be converted to WM lock once transition is complete.
     void setActivityManagerService(ActivityManagerService am) {
         mAm = am;
@@ -89,6 +247,1036 @@
     }
 
     @Override
+    public final int startActivity(IApplicationThread caller, String callingPackage,
+            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
+        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
+                resultWho, requestCode, startFlags, profilerInfo, bOptions,
+                UserHandle.getCallingUserId());
+    }
+
+    @Override
+    public final int startActivities(IApplicationThread caller, String callingPackage,
+            Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle bOptions,
+            int userId) {
+        final String reason = "startActivities";
+        mAm.enforceNotIsolatedCaller(reason);
+        userId = mAm.mUserController.handleIncomingUser(Binder.getCallingPid(),
+                Binder.getCallingUid(), userId, false, ALLOW_FULL_ONLY, reason, null);
+        // TODO: Switch to user app stacks here.
+        return mAm.getActivityStartController().startActivities(caller, -1, callingPackage, intents,
+                resolvedTypes, resultTo, SafeActivityOptions.fromBundle(bOptions), userId, reason);
+    }
+
+    @Override
+    public int startActivityAsUser(IApplicationThread caller, String callingPackage,
+            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
+        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
+                resultWho, requestCode, startFlags, profilerInfo, bOptions, userId,
+                true /*validateIncomingUser*/);
+    }
+
+    int startActivityAsUser(IApplicationThread caller, String callingPackage,
+            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,
+            boolean validateIncomingUser) {
+        mAm.enforceNotIsolatedCaller("startActivityAsUser");
+
+        userId = mAm.getActivityStartController().checkTargetUser(userId, validateIncomingUser,
+                Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");
+
+        // TODO: Switch to user app stacks here.
+        return mAm.getActivityStartController().obtainStarter(intent, "startActivityAsUser")
+                .setCaller(caller)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setResultTo(resultTo)
+                .setResultWho(resultWho)
+                .setRequestCode(requestCode)
+                .setStartFlags(startFlags)
+                .setProfilerInfo(profilerInfo)
+                .setActivityOptions(bOptions)
+                .setMayWait(userId)
+                .execute();
+
+    }
+
+    @Override
+    public int startActivityIntentSender(IApplicationThread caller, IIntentSender target,
+            IBinder whitelistToken, Intent fillInIntent, String resolvedType, IBinder resultTo,
+            String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle bOptions)
+            throws TransactionTooLargeException {
+        mAm.enforceNotIsolatedCaller("startActivityIntentSender");
+        // Refuse possible leaked file descriptors
+        if (fillInIntent != null && fillInIntent.hasFileDescriptors()) {
+            throw new IllegalArgumentException("File descriptors passed in Intent");
+        }
+
+        if (!(target instanceof PendingIntentRecord)) {
+            throw new IllegalArgumentException("Bad PendingIntent object");
+        }
+
+        PendingIntentRecord pir = (PendingIntentRecord)target;
+
+        synchronized (mGlobalLock) {
+            // If this is coming from the currently resumed activity, it is
+            // effectively saying that app switches are allowed at this point.
+            final ActivityStack stack = mAm.getFocusedStack();
+            if (stack.mResumedActivity != null &&
+                    stack.mResumedActivity.info.applicationInfo.uid == Binder.getCallingUid()) {
+                mAm.mAppSwitchesAllowedTime = 0;
+            }
+        }
+        int ret = pir.sendInner(0, fillInIntent, resolvedType, whitelistToken, null, null,
+                resultTo, resultWho, requestCode, flagsMask, flagsValues, bOptions);
+        return ret;
+    }
+
+    @Override
+    public boolean startNextMatchingActivity(IBinder callingActivity, Intent intent,
+            Bundle bOptions) {
+        // Refuse possible leaked file descriptors
+        if (intent != null && intent.hasFileDescriptors()) {
+            throw new IllegalArgumentException("File descriptors passed in Intent");
+        }
+        SafeActivityOptions options = SafeActivityOptions.fromBundle(bOptions);
+
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(callingActivity);
+            if (r == null) {
+                SafeActivityOptions.abort(options);
+                return false;
+            }
+            if (r.app == null || r.app.thread == null) {
+                // The caller is not running...  d'oh!
+                SafeActivityOptions.abort(options);
+                return false;
+            }
+            intent = new Intent(intent);
+            // The caller is not allowed to change the data.
+            intent.setDataAndType(r.intent.getData(), r.intent.getType());
+            // And we are resetting to find the next component...
+            intent.setComponent(null);
+
+            final boolean debug = ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
+
+            ActivityInfo aInfo = null;
+            try {
+                List<ResolveInfo> resolves =
+                        AppGlobals.getPackageManager().queryIntentActivities(
+                                intent, r.resolvedType,
+                                PackageManager.MATCH_DEFAULT_ONLY | STOCK_PM_FLAGS,
+                                UserHandle.getCallingUserId()).getList();
+
+                // Look for the original activity in the list...
+                final int N = resolves != null ? resolves.size() : 0;
+                for (int i=0; i<N; i++) {
+                    ResolveInfo rInfo = resolves.get(i);
+                    if (rInfo.activityInfo.packageName.equals(r.packageName)
+                            && rInfo.activityInfo.name.equals(r.info.name)) {
+                        // We found the current one...  the next matching is
+                        // after it.
+                        i++;
+                        if (i<N) {
+                            aInfo = resolves.get(i).activityInfo;
+                        }
+                        if (debug) {
+                            Slog.v(TAG, "Next matching activity: found current " + r.packageName
+                                    + "/" + r.info.name);
+                            Slog.v(TAG, "Next matching activity: next is " + ((aInfo == null)
+                                    ? "null" : aInfo.packageName + "/" + aInfo.name));
+                        }
+                        break;
+                    }
+                }
+            } catch (RemoteException e) {
+            }
+
+            if (aInfo == null) {
+                // Nobody who is next!
+                SafeActivityOptions.abort(options);
+                if (debug) Slog.d(TAG, "Next matching activity: nothing found");
+                return false;
+            }
+
+            intent.setComponent(new ComponentName(
+                    aInfo.applicationInfo.packageName, aInfo.name));
+            intent.setFlags(intent.getFlags()&~(
+                    Intent.FLAG_ACTIVITY_FORWARD_RESULT|
+                            Intent.FLAG_ACTIVITY_CLEAR_TOP|
+                            Intent.FLAG_ACTIVITY_MULTIPLE_TASK|
+                            FLAG_ACTIVITY_NEW_TASK));
+
+            // Okay now we need to start the new activity, replacing the currently running activity.
+            // This is a little tricky because we want to start the new one as if the current one is
+            // finished, but not finish the current one first so that there is no flicker.
+            // And thus...
+            final boolean wasFinishing = r.finishing;
+            r.finishing = true;
+
+            // Propagate reply information over to the new activity.
+            final ActivityRecord resultTo = r.resultTo;
+            final String resultWho = r.resultWho;
+            final int requestCode = r.requestCode;
+            r.resultTo = null;
+            if (resultTo != null) {
+                resultTo.removeResultsLocked(r, resultWho, requestCode);
+            }
+
+            final long origId = Binder.clearCallingIdentity();
+            // TODO(b/64750076): Check if calling pid should really be -1.
+            final int res = mAm.getActivityStartController()
+                    .obtainStarter(intent, "startNextMatchingActivity")
+                    .setCaller(r.app.thread)
+                    .setResolvedType(r.resolvedType)
+                    .setActivityInfo(aInfo)
+                    .setResultTo(resultTo != null ? resultTo.appToken : null)
+                    .setResultWho(resultWho)
+                    .setRequestCode(requestCode)
+                    .setCallingPid(-1)
+                    .setCallingUid(r.launchedFromUid)
+                    .setCallingPackage(r.launchedFromPackage)
+                    .setRealCallingPid(-1)
+                    .setRealCallingUid(r.launchedFromUid)
+                    .setActivityOptions(options)
+                    .execute();
+            Binder.restoreCallingIdentity(origId);
+
+            r.finishing = wasFinishing;
+            if (res != ActivityManager.START_SUCCESS) {
+                return false;
+            }
+            return true;
+        }
+    }
+
+    @Override
+    public final WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,
+            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
+        final WaitResult res = new WaitResult();
+        synchronized (mGlobalLock) {
+            mAm.enforceNotIsolatedCaller("startActivityAndWait");
+            userId = mAm.mUserController.handleIncomingUser(Binder.getCallingPid(),
+                    Binder.getCallingUid(), userId, false, ALLOW_FULL_ONLY,
+                    "startActivityAndWait", null);
+            // TODO: Switch to user app stacks here.
+            mAm.getActivityStartController().obtainStarter(intent, "startActivityAndWait")
+                    .setCaller(caller)
+                    .setCallingPackage(callingPackage)
+                    .setResolvedType(resolvedType)
+                    .setResultTo(resultTo)
+                    .setResultWho(resultWho)
+                    .setRequestCode(requestCode)
+                    .setStartFlags(startFlags)
+                    .setActivityOptions(bOptions)
+                    .setMayWait(userId)
+                    .setProfilerInfo(profilerInfo)
+                    .setWaitResult(res)
+                    .execute();
+        }
+        return res;
+    }
+
+    @Override
+    public final int startActivityWithConfig(IApplicationThread caller, String callingPackage,
+            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+            int startFlags, Configuration config, Bundle bOptions, int userId) {
+        synchronized (mGlobalLock) {
+            mAm.enforceNotIsolatedCaller("startActivityWithConfig");
+            userId = mAm.mUserController.handleIncomingUser(Binder.getCallingPid(),
+                    Binder.getCallingUid(), userId, false, ALLOW_FULL_ONLY,
+                    "startActivityWithConfig", null);
+            // TODO: Switch to user app stacks here.
+            return mAm.getActivityStartController().obtainStarter(intent, "startActivityWithConfig")
+                    .setCaller(caller)
+                    .setCallingPackage(callingPackage)
+                    .setResolvedType(resolvedType)
+                    .setResultTo(resultTo)
+                    .setResultWho(resultWho)
+                    .setRequestCode(requestCode)
+                    .setStartFlags(startFlags)
+                    .setGlobalConfiguration(config)
+                    .setActivityOptions(bOptions)
+                    .setMayWait(userId)
+                    .execute();
+        }
+    }
+
+    @Override
+    public final int startActivityAsCaller(IApplicationThread caller, String callingPackage,
+            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, boolean ignoreTargetSecurity,
+            int userId) {
+
+        // This is very dangerous -- it allows you to perform a start activity (including
+        // permission grants) as any app that may launch one of your own activities.  So
+        // we will only allow this to be done from activities that are part of the core framework,
+        // and then only when they are running as the system.
+        final ActivityRecord sourceRecord;
+        final int targetUid;
+        final String targetPackage;
+        final boolean isResolver;
+        synchronized (mGlobalLock) {
+            if (resultTo == null) {
+                throw new SecurityException("Must be called from an activity");
+            }
+            sourceRecord = mStackSupervisor.isInAnyStackLocked(resultTo);
+            if (sourceRecord == null) {
+                throw new SecurityException("Called with bad activity token: " + resultTo);
+            }
+            if (!sourceRecord.info.packageName.equals("android")) {
+                throw new SecurityException(
+                        "Must be called from an activity that is declared in the android package");
+            }
+            if (sourceRecord.app == null) {
+                throw new SecurityException("Called without a process attached to activity");
+            }
+            if (UserHandle.getAppId(sourceRecord.app.uid) != SYSTEM_UID) {
+                // This is still okay, as long as this activity is running under the
+                // uid of the original calling activity.
+                if (sourceRecord.app.uid != sourceRecord.launchedFromUid) {
+                    throw new SecurityException(
+                            "Calling activity in uid " + sourceRecord.app.uid
+                                    + " must be system uid or original calling uid "
+                                    + sourceRecord.launchedFromUid);
+                }
+            }
+            if (ignoreTargetSecurity) {
+                if (intent.getComponent() == null) {
+                    throw new SecurityException(
+                            "Component must be specified with ignoreTargetSecurity");
+                }
+                if (intent.getSelector() != null) {
+                    throw new SecurityException(
+                            "Selector not allowed with ignoreTargetSecurity");
+                }
+            }
+            targetUid = sourceRecord.launchedFromUid;
+            targetPackage = sourceRecord.launchedFromPackage;
+            isResolver = sourceRecord.isResolverOrChildActivity();
+        }
+
+        if (userId == UserHandle.USER_NULL) {
+            userId = UserHandle.getUserId(sourceRecord.app.uid);
+        }
+
+        // TODO: Switch to user app stacks here.
+        try {
+            return mAm.getActivityStartController().obtainStarter(intent, "startActivityAsCaller")
+                    .setCallingUid(targetUid)
+                    .setCallingPackage(targetPackage)
+                    .setResolvedType(resolvedType)
+                    .setResultTo(resultTo)
+                    .setResultWho(resultWho)
+                    .setRequestCode(requestCode)
+                    .setStartFlags(startFlags)
+                    .setActivityOptions(bOptions)
+                    .setMayWait(userId)
+                    .setIgnoreTargetSecurity(ignoreTargetSecurity)
+                    .setFilterCallingUid(isResolver ? 0 /* system */ : targetUid)
+                    .execute();
+        } catch (SecurityException e) {
+            // XXX need to figure out how to propagate to original app.
+            // A SecurityException here is generally actually a fault of the original
+            // calling activity (such as a fairly granting permissions), so propagate it
+            // back to them.
+            /*
+            StringBuilder msg = new StringBuilder();
+            msg.append("While launching");
+            msg.append(intent.toString());
+            msg.append(": ");
+            msg.append(e.getMessage());
+            */
+            throw e;
+        }
+    }
+
+    @Override
+    public int startVoiceActivity(String callingPackage, int callingPid, int callingUid,
+            Intent intent, String resolvedType, IVoiceInteractionSession session,
+            IVoiceInteractor interactor, int startFlags, ProfilerInfo profilerInfo,
+            Bundle bOptions, int userId) {
+        mAm.enforceCallingPermission(BIND_VOICE_INTERACTION, "startVoiceActivity()");
+        if (session == null || interactor == null) {
+            throw new NullPointerException("null session or interactor");
+        }
+        userId = mAm.mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
+                ALLOW_FULL_ONLY, "startVoiceActivity", null);
+        // TODO: Switch to user app stacks here.
+        return mAm.getActivityStartController().obtainStarter(intent, "startVoiceActivity")
+                .setCallingUid(callingUid)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setVoiceSession(session)
+                .setVoiceInteractor(interactor)
+                .setStartFlags(startFlags)
+                .setProfilerInfo(profilerInfo)
+                .setActivityOptions(bOptions)
+                .setMayWait(userId)
+                .execute();
+    }
+
+    @Override
+    public int startAssistantActivity(String callingPackage, int callingPid, int callingUid,
+            Intent intent, String resolvedType, Bundle bOptions, int userId) {
+        mAm.enforceCallingPermission(BIND_VOICE_INTERACTION, "startAssistantActivity()");
+        userId = mAm.mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
+                ALLOW_FULL_ONLY, "startAssistantActivity", null);
+
+        return mAm.getActivityStartController().obtainStarter(intent, "startAssistantActivity")
+                .setCallingUid(callingUid)
+                .setCallingPackage(callingPackage)
+                .setResolvedType(resolvedType)
+                .setActivityOptions(bOptions)
+                .setMayWait(userId)
+                .execute();
+    }
+
+    @Override
+    public void startRecentsActivity(Intent intent, IAssistDataReceiver assistDataReceiver,
+            IRecentsAnimationRunner recentsAnimationRunner) {
+        mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "startRecentsActivity()");
+        final int callingPid = Binder.getCallingPid();
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ComponentName recentsComponent = mAm.getRecentTasks().getRecentsComponent();
+                final int recentsUid = mAm.getRecentTasks().getRecentsComponentUid();
+
+                // Start a new recents animation
+                final RecentsAnimation anim = new RecentsAnimation(mAm, mStackSupervisor,
+                        mAm.getActivityStartController(), mAm.mWindowManager, mAm.mUserController,
+                        callingPid);
+                anim.startRecentsActivity(intent, recentsAnimationRunner, recentsComponent,
+                        recentsUid, assistDataReceiver);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public final int startActivityFromRecents(int taskId, Bundle bOptions) {
+        mAm.enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS,
+                "startActivityFromRecents()");
+
+        final int callingPid = Binder.getCallingPid();
+        final int callingUid = Binder.getCallingUid();
+        final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(bOptions);
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                return mStackSupervisor.startActivityFromRecents(callingPid, callingUid, taskId,
+                        safeOptions);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    /**
+     * This is the internal entry point for handling Activity.finish().
+     *
+     * @param token The Binder token referencing the Activity we want to finish.
+     * @param resultCode Result code, if any, from this Activity.
+     * @param resultData Result data (Intent), if any, from this Activity.
+     * @param finishTask Whether to finish the task associated with this Activity.
+     *
+     * @return Returns true if the activity successfully finished, or false if it is still running.
+     */
+    @Override
+    public final boolean finishActivity(IBinder token, int resultCode, Intent resultData,
+            int finishTask) {
+        // Refuse possible leaked file descriptors
+        if (resultData != null && resultData.hasFileDescriptors()) {
+            throw new IllegalArgumentException("File descriptors passed in Intent");
+        }
+
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return true;
+            }
+            // Keep track of the root activity of the task before we finish it
+            TaskRecord tr = r.getTask();
+            ActivityRecord rootR = tr.getRootActivity();
+            if (rootR == null) {
+                Slog.w(TAG, "Finishing task with all activities already finished");
+            }
+            // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps can
+            // finish.
+            if (mAm.getLockTaskController().activityBlockedFromFinish(r)) {
+                return false;
+            }
+
+            if (mAm.mController != null) {
+                // Find the first activity that is not finishing.
+                ActivityRecord next = r.getStack().topRunningActivityLocked(token, 0);
+                if (next != null) {
+                    // ask watcher if this is allowed
+                    boolean resumeOK = true;
+                    try {
+                        resumeOK = mAm.mController.activityResuming(next.packageName);
+                    } catch (RemoteException e) {
+                        mAm.mController = null;
+                        Watchdog.getInstance().setActivityController(null);
+                    }
+
+                    if (!resumeOK) {
+                        Slog.i(TAG, "Not finishing activity because controller resumed");
+                        return false;
+                    }
+                }
+            }
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                boolean res;
+                final boolean finishWithRootActivity =
+                        finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY;
+                if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY
+                        || (finishWithRootActivity && r == rootR)) {
+                    // If requested, remove the task that is associated to this activity only if it
+                    // was the root activity in the task. The result code and data is ignored
+                    // because we don't support returning them across task boundaries. Also, to
+                    // keep backwards compatibility we remove the task from recents when finishing
+                    // task with root activity.
+                    res = mStackSupervisor.removeTaskByIdLocked(tr.taskId, false,
+                            finishWithRootActivity, "finish-activity");
+                    if (!res) {
+                        Slog.i(TAG, "Removing task failed to finish activity");
+                    }
+                } else {
+                    res = tr.getStack().requestFinishActivityLocked(token, resultCode,
+                            resultData, "app-request", true);
+                    if (!res) {
+                        Slog.i(TAG, "Failed to finish by app-request");
+                    }
+                }
+                return res;
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public boolean finishActivityAffinity(IBinder token) {
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r == null) {
+                    return false;
+                }
+
+                // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps
+                // can finish.
+                final TaskRecord task = r.getTask();
+                if (mAm.getLockTaskController().activityBlockedFromFinish(r)) {
+                    return false;
+                }
+                return task.getStack().finishActivityAffinityLocked(r);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
+        final long origId = Binder.clearCallingIdentity();
+        synchronized (mGlobalLock) {
+            ActivityStack stack = ActivityRecord.getStackLocked(token);
+            if (stack != null) {
+                ActivityRecord r =
+                        mStackSupervisor.activityIdleInternalLocked(token, false /* fromTimeout */,
+                                false /* processPausingActivities */, config);
+                if (stopProfiling) {
+                    if ((mAm.mProfileProc == r.app) && mAm.mProfilerInfo != null) {
+                        mAm.clearProfilerLocked();
+                    }
+                }
+            }
+        }
+        Binder.restoreCallingIdentity(origId);
+    }
+
+    @Override
+    public final void activityResumed(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        synchronized (mGlobalLock) {
+            ActivityRecord.activityResumedLocked(token);
+            mAm.mWindowManager.notifyAppResumedFinished(token);
+        }
+        Binder.restoreCallingIdentity(origId);
+    }
+
+    @Override
+    public final void activityPaused(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        synchronized (mGlobalLock) {
+            ActivityStack stack = ActivityRecord.getStackLocked(token);
+            if (stack != null) {
+                stack.activityPausedLocked(token, false);
+            }
+        }
+        Binder.restoreCallingIdentity(origId);
+    }
+
+    @Override
+    public final void activityStopped(IBinder token, Bundle icicle,
+            PersistableBundle persistentState, CharSequence description) {
+        if (DEBUG_ALL) Slog.v(TAG, "Activity stopped: token=" + token);
+
+        // Refuse possible leaked file descriptors
+        if (icicle != null && icicle.hasFileDescriptors()) {
+            throw new IllegalArgumentException("File descriptors passed in Bundle");
+        }
+
+        final long origId = Binder.clearCallingIdentity();
+
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r != null) {
+                r.activityStoppedLocked(icicle, persistentState, description);
+            }
+        }
+
+        mAm.trimApplications();
+
+        Binder.restoreCallingIdentity(origId);
+    }
+
+    @Override
+    public final void activityDestroyed(IBinder token) {
+        if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "ACTIVITY DESTROYED: " + token);
+        synchronized (mGlobalLock) {
+            ActivityStack stack = ActivityRecord.getStackLocked(token);
+            if (stack != null) {
+                stack.activityDestroyedLocked(token, "activityDestroyed");
+            }
+        }
+    }
+
+    @Override
+    public final void activityRelaunched(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        synchronized (mGlobalLock) {
+            mStackSupervisor.activityRelaunchedLocked(token);
+        }
+        Binder.restoreCallingIdentity(origId);
+    }
+
+    public final void activitySlept(IBinder token) {
+        if (DEBUG_ALL) Slog.v(TAG, "Activity slept: token=" + token);
+
+        final long origId = Binder.clearCallingIdentity();
+
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r != null) {
+                mStackSupervisor.activitySleptLocked(r);
+            }
+        }
+
+        Binder.restoreCallingIdentity(origId);
+    }
+
+    @Override
+    public void setRequestedOrientation(IBinder token, int requestedOrientation) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return;
+            }
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                r.setRequestedOrientation(requestedOrientation);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public int getRequestedOrientation(IBinder token) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+            }
+            return r.getRequestedOrientation();
+        }
+    }
+
+    @Override
+    public void setImmersive(IBinder token, boolean immersive) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                throw new IllegalArgumentException();
+            }
+            r.immersive = immersive;
+
+            // update associated state if we're frontmost
+            if (r == mStackSupervisor.getResumedActivityLocked()) {
+                if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE, "Frontmost changed immersion: "+ r);
+                mAm.applyUpdateLockStateLocked(r);
+            }
+        }
+    }
+
+    @Override
+    public boolean isImmersive(IBinder token) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                throw new IllegalArgumentException();
+            }
+            return r.immersive;
+        }
+    }
+
+    @Override
+    public boolean isTopActivityImmersive() {
+        mAm.enforceNotIsolatedCaller("isTopActivityImmersive");
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = mAm.getFocusedStack().topRunningActivityLocked();
+            return (r != null) ? r.immersive : false;
+        }
+    }
+
+    @Override
+    public void overridePendingTransition(IBinder token, String packageName,
+            int enterAnim, int exitAnim) {
+        synchronized (mGlobalLock) {
+            ActivityRecord self = ActivityRecord.isInStackLocked(token);
+            if (self == null) {
+                return;
+            }
+
+            final long origId = Binder.clearCallingIdentity();
+
+            if (self.isState(
+                    ActivityStack.ActivityState.RESUMED, ActivityStack.ActivityState.PAUSING)) {
+                mAm.mWindowManager.overridePendingAppTransition(packageName,
+                        enterAnim, exitAnim, null);
+            }
+
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public int getFrontActivityScreenCompatMode() {
+        mAm.enforceNotIsolatedCaller("getFrontActivityScreenCompatMode");
+        synchronized (mGlobalLock) {
+            return mAm.mCompatModePackages.getFrontActivityScreenCompatModeLocked();
+        }
+    }
+
+    @Override
+    public void setFrontActivityScreenCompatMode(int mode) {
+        mAm.enforceCallingPermission(android.Manifest.permission.SET_SCREEN_COMPATIBILITY,
+                "setFrontActivityScreenCompatMode");
+        synchronized (mGlobalLock) {
+            mAm.mCompatModePackages.setFrontActivityScreenCompatModeLocked(mode);
+        }
+    }
+
+    @Override
+    public int getLaunchedFromUid(IBinder activityToken) {
+        ActivityRecord srec;
+        synchronized (mGlobalLock) {
+            srec = ActivityRecord.forTokenLocked(activityToken);
+        }
+        if (srec == null) {
+            return -1;
+        }
+        return srec.launchedFromUid;
+    }
+
+    @Override
+    public String getLaunchedFromPackage(IBinder activityToken) {
+        ActivityRecord srec;
+        synchronized (mGlobalLock) {
+            srec = ActivityRecord.forTokenLocked(activityToken);
+        }
+        if (srec == null) {
+            return null;
+        }
+        return srec.launchedFromPackage;
+    }
+
+    @Override
+    public boolean convertFromTranslucent(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r == null) {
+                    return false;
+                }
+                final boolean translucentChanged = r.changeWindowTranslucency(true);
+                if (translucentChanged) {
+                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                }
+                mAm.mWindowManager.setAppFullscreen(token, true);
+                return translucentChanged;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public boolean convertToTranslucent(IBinder token, Bundle options) {
+        SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(options);
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r == null) {
+                    return false;
+                }
+                final TaskRecord task = r.getTask();
+                int index = task.mActivities.lastIndexOf(r);
+                if (index > 0) {
+                    ActivityRecord under = task.mActivities.get(index - 1);
+                    under.returningOptions = safeOptions != null ? safeOptions.getOptions(r) : null;
+                }
+                final boolean translucentChanged = r.changeWindowTranslucency(false);
+                if (translucentChanged) {
+                    r.getStack().convertActivityToTranslucent(r);
+                }
+                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+                mAm.mWindowManager.setAppFullscreen(token, false);
+                return translucentChanged;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public void notifyActivityDrawn(IBinder token) {
+        if (DEBUG_VISIBILITY) Slog.d(TAG_VISIBILITY, "notifyActivityDrawn: token=" + token);
+        synchronized (mGlobalLock) {
+            ActivityRecord r = mStackSupervisor.isInAnyStackLocked(token);
+            if (r != null) {
+                r.getStack().notifyActivityDrawnLocked(r);
+            }
+        }
+    }
+
+    @Override
+    public void reportActivityFullyDrawn(IBinder token, boolean restoredFromBundle) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return;
+            }
+            r.reportFullyDrawnLocked(restoredFromBundle);
+        }
+    }
+
+    @Override
+    public int getActivityDisplayId(IBinder activityToken) throws RemoteException {
+        synchronized (mGlobalLock) {
+            final ActivityStack stack = ActivityRecord.getStackLocked(activityToken);
+            if (stack != null && stack.mDisplayId != INVALID_DISPLAY) {
+                return stack.mDisplayId;
+            }
+            return DEFAULT_DISPLAY;
+        }
+    }
+
+    @Override
+    public ActivityManager.StackInfo getFocusedStackInfo() throws RemoteException {
+        mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                ActivityStack focusedStack = mAm.getFocusedStack();
+                if (focusedStack != null) {
+                    return mStackSupervisor.getStackInfo(focusedStack.mStackId);
+                }
+                return null;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public void setFocusedStack(int stackId) {
+        mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedStack()");
+        if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedStack: stackId=" + stackId);
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityStack stack = mStackSupervisor.getStack(stackId);
+                if (stack == null) {
+                    Slog.w(TAG, "setFocusedStack: No stack with id=" + stackId);
+                    return;
+                }
+                final ActivityRecord r = stack.topRunningActivityLocked();
+                if (mStackSupervisor.moveFocusableActivityStackToFrontLocked(
+                        r, "setFocusedStack")) {
+                    mStackSupervisor.resumeFocusedStackTopActivityLocked();
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+    }
+
+    @Override
+    public void setFocusedTask(int taskId) {
+        mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedTask()");
+        if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedTask: taskId=" + taskId);
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+                if (task == null) {
+                    return;
+                }
+                final ActivityRecord r = task.topRunningActivityLocked();
+                if (mStackSupervisor.moveFocusableActivityStackToFrontLocked(r, "setFocusedTask")) {
+                    mStackSupervisor.resumeFocusedStackTopActivityLocked();
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+    }
+
+    @Override
+    public boolean removeTask(int taskId) {
+        mAm.enforceCallerIsRecentsOrHasPermission(REMOVE_TASKS, "removeTask()");
+        synchronized (mGlobalLock) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                return mStackSupervisor.removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS,
+                        "remove-task");
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
+    public boolean shouldUpRecreateTask(IBinder token, String destAffinity) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord srec = ActivityRecord.forTokenLocked(token);
+            if (srec != null) {
+                return srec.getStack().shouldUpRecreateTaskLocked(srec, destAffinity);
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean navigateUpTo(IBinder token, Intent destIntent, int resultCode,
+            Intent resultData) {
+
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+            if (r != null) {
+                return r.getStack().navigateUpToLocked(r, destIntent, resultCode, resultData);
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Attempts to move a task backwards in z-order (the order of activities within the task is
+     * unchanged).
+     *
+     * There are several possible results of this call:
+     * - if the task is locked, then we will show the lock toast
+     * - if there is a task behind the provided task, then that task is made visible and resumed as
+     *   this task is moved to the back
+     * - otherwise, if there are no other tasks in the stack:
+     *     - if this task is in the pinned stack, then we remove the stack completely, which will
+     *       have the effect of moving the task to the top or bottom of the fullscreen stack
+     *       (depending on whether it is visible)
+     *     - otherwise, we simply return home and hide this task
+     *
+     * @param token A reference to the activity we wish to move
+     * @param nonRoot If false then this only works if the activity is the root
+     *                of a task; if true it will work for any activity in a task.
+     * @return Returns true if the move completed, false if not.
+     */
+    @Override
+    public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) {
+        mAm.enforceNotIsolatedCaller("moveActivityTaskToBack");
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                int taskId = ActivityRecord.getTaskForActivityLocked(token, !nonRoot);
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+                if (task != null) {
+                    return ActivityRecord.getStackLocked(token).moveTaskToBackLocked(taskId);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public Rect getTaskBounds(int taskId) {
+        mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "getTaskBounds()");
+        long ident = Binder.clearCallingIdentity();
+        Rect rect = new Rect();
+        try {
+            synchronized (mGlobalLock) {
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                        MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+                if (task == null) {
+                    Slog.w(TAG, "getTaskBounds: taskId=" + taskId + " not found");
+                    return rect;
+                }
+                if (task.getStack() != null) {
+                    // Return the bounds from window manager since it will be adjusted for various
+                    // things like the presense of a docked stack for tasks that aren't resizeable.
+                    task.getWindowContainerBounds(rect);
+                } else {
+                    // Task isn't in window manager yet since it isn't associated with a stack.
+                    // Return the persist value from activity manager
+                    if (!task.matchParentBounds()) {
+                        rect.set(task.getBounds());
+                    } else if (task.mLastNonFullscreenBounds != null) {
+                        rect.set(task.mLastNonFullscreenBounds);
+                    }
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+        return rect;
+    }
+
+    @Override
+    public ActivityManager.TaskDescription getTaskDescription(int id) {
+        synchronized (mGlobalLock) {
+            mAm.enforceCallerIsRecentsOrHasPermission(
+                    MANAGE_ACTIVITY_STACKS, "getTaskDescription()");
+            final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(id,
+                    MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+            if (tr != null) {
+                return tr.lastTaskDescription;
+            }
+        }
+        return null;
+    }
+
+    @Override
     public void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop) {
         if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
             setTaskWindowingModeSplitScreenPrimary(taskId, SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
@@ -126,6 +1314,104 @@
     }
 
     @Override
+    public String getCallingPackage(IBinder token) {
+        synchronized (this) {
+            ActivityRecord r = getCallingRecordLocked(token);
+            return r != null ? r.info.packageName : null;
+        }
+    }
+
+    @Override
+    public ComponentName getCallingActivity(IBinder token) {
+        synchronized (this) {
+            ActivityRecord r = getCallingRecordLocked(token);
+            return r != null ? r.intent.getComponent() : null;
+        }
+    }
+
+    private ActivityRecord getCallingRecordLocked(IBinder token) {
+        ActivityRecord r = ActivityRecord.isInStackLocked(token);
+        if (r == null) {
+            return null;
+        }
+        return r.resultTo;
+    }
+
+    @Override
+    public void unhandledBack() {
+        mAm.enforceCallingPermission(android.Manifest.permission.FORCE_BACK, "unhandledBack()");
+
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                mAm.getFocusedStack().unhandledBackLocked();
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    /**
+     * TODO: Add mController hook
+     */
+    @Override
+    public void moveTaskToFront(int taskId, int flags, Bundle bOptions) {
+        mAm.enforceCallingPermission(android.Manifest.permission.REORDER_TASKS, "moveTaskToFront()");
+
+        if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToFront: moving taskId=" + taskId);
+        synchronized (mGlobalLock) {
+            moveTaskToFrontLocked(taskId, flags, SafeActivityOptions.fromBundle(bOptions),
+                    false /* fromRecents */);
+        }
+    }
+
+    void moveTaskToFrontLocked(int taskId, int flags, SafeActivityOptions options,
+            boolean fromRecents) {
+
+        if (!mAm.checkAppSwitchAllowedLocked(Binder.getCallingPid(),
+                Binder.getCallingUid(), -1, -1, "Task to front")) {
+            SafeActivityOptions.abort(options);
+            return;
+        }
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+            if (task == null) {
+                Slog.d(TAG, "Could not find task for id: "+ taskId);
+                return;
+            }
+            if (mAm.getLockTaskController().isLockTaskModeViolation(task)) {
+                Slog.e(TAG, "moveTaskToFront: Attempt to violate Lock Task Mode");
+                return;
+            }
+            ActivityOptions realOptions = options != null
+                    ? options.getOptions(mStackSupervisor)
+                    : null;
+            mStackSupervisor.findTaskToMoveToFront(task, flags, realOptions, "moveTaskToFront",
+                    false /* forceNonResizable */);
+
+            final ActivityRecord topActivity = task.getTopActivity();
+            if (topActivity != null) {
+
+                // We are reshowing a task, use a starting window to hide the initial draw delay
+                // so the transition can start earlier.
+                topActivity.showStartingWindow(null /* prev */, false /* newTask */,
+                        true /* taskSwitch */, fromRecents);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+        SafeActivityOptions.abort(options);
+    }
+
+    @Override
+    public int getTaskForActivity(IBinder token, boolean onlyRoot) {
+        synchronized (mGlobalLock) {
+            return ActivityRecord.getTaskForActivityLocked(token, onlyRoot);
+        }
+    }
+
+    @Override
     public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) {
         return getFilteredTasks(maxNum, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED);
     }
@@ -150,6 +1436,29 @@
     }
 
     @Override
+    public final void finishSubActivity(IBinder token, String resultWho, int requestCode) {
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r != null) {
+                r.getStack().finishSubActivityLocked(r, resultWho, requestCode);
+            }
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public boolean willActivityBeVisible(IBinder token) {
+        synchronized(this) {
+            ActivityStack stack = ActivityRecord.getStackLocked(token);
+            if (stack != null) {
+                return stack.willActivityBeVisibleLocked(token);
+            }
+            return false;
+        }
+    }
+
+    @Override
     public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
         mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()");
         synchronized (mGlobalLock) {
@@ -306,4 +1615,1716 @@
             }
         }
     }
+
+    @Override
+    public ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
+            int userId) {
+        final int callingUid = Binder.getCallingUid();
+        userId = mAm.mUserController.handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
+                false, ALLOW_FULL_ONLY, "getRecentTasks", null);
+        final boolean allowed = mAm.isGetTasksAllowed("getRecentTasks", Binder.getCallingPid(),
+                callingUid);
+        final boolean detailed = mAm.checkCallingPermission(
+                android.Manifest.permission.GET_DETAILED_TASKS)
+                == PackageManager.PERMISSION_GRANTED;
+
+        synchronized (mGlobalLock) {
+            return mAm.getRecentTasks().getRecentTasks(maxNum, flags, allowed, detailed, userId,
+                    callingUid);
+        }
+    }
+
+    @Override
+    public List<ActivityManager.StackInfo> getAllStackInfos() {
+        mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getAllStackInfos()");
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                return mStackSupervisor.getAllStackInfosLocked();
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public ActivityManager.StackInfo getStackInfo(int windowingMode, int activityType) {
+        mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getStackInfo()");
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                return mStackSupervisor.getStackInfo(windowingMode, activityType);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public void cancelRecentsAnimation(boolean restoreHomeStackPosition) {
+        mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "cancelRecentsAnimation()");
+        final long callingUid = Binder.getCallingUid();
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                // Cancel the recents animation synchronously (do not hold the WM lock)
+                mAm.mWindowManager.cancelRecentsAnimationSynchronously(restoreHomeStackPosition
+                        ? REORDER_MOVE_TO_ORIGINAL_POSITION
+                        : REORDER_KEEP_IN_PLACE, "cancelRecentsAnimation/uid=" + callingUid);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public void startLockTaskModeByToken(IBinder token) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+            if (r == null) {
+                return;
+            }
+            startLockTaskModeLocked(r.getTask(), false /* isSystemCaller */);
+        }
+    }
+
+    @Override
+    public void startSystemLockTaskMode(int taskId) throws RemoteException {
+        mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "startSystemLockTaskMode");
+        // This makes inner call to look as if it was initiated by system.
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+
+                // When starting lock task mode the stack must be in front and focused
+                task.getStack().moveToFront("startSystemLockTaskMode");
+                startLockTaskModeLocked(task, true /* isSystemCaller */);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public void stopLockTaskModeByToken(IBinder token) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+            if (r == null) {
+                return;
+            }
+            stopLockTaskModeInternal(r.getTask(), false /* isSystemCaller */);
+        }
+    }
+
+    /**
+     * This API should be called by SystemUI only when user perform certain action to dismiss
+     * lock task mode. We should only dismiss pinned lock task mode in this case.
+     */
+    @Override
+    public void stopSystemLockTaskMode() throws RemoteException {
+        mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "stopSystemLockTaskMode");
+        stopLockTaskModeInternal(null, true /* isSystemCaller */);
+    }
+
+    private void startLockTaskModeLocked(@Nullable TaskRecord task, boolean isSystemCaller) {
+        if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "startLockTaskModeLocked: " + task);
+        if (task == null || task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
+            return;
+        }
+
+        final ActivityStack stack = mStackSupervisor.getFocusedStack();
+        if (stack == null || task != stack.topTask()) {
+            throw new IllegalArgumentException("Invalid task, not in foreground");
+        }
+
+        // {@code isSystemCaller} is used to distinguish whether this request is initiated by the
+        // system or a specific app.
+        // * System-initiated requests will only start the pinned mode (screen pinning)
+        // * App-initiated requests
+        //   - will put the device in fully locked mode (LockTask), if the app is whitelisted
+        //   - will start the pinned mode, otherwise
+        final int callingUid = Binder.getCallingUid();
+        long ident = Binder.clearCallingIdentity();
+        try {
+            // When a task is locked, dismiss the pinned stack if it exists
+            mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+
+            mAm.getLockTaskController().startLockTaskMode(task, isSystemCaller, callingUid);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    private void stopLockTaskModeInternal(@Nullable TaskRecord task, boolean isSystemCaller) {
+        final int callingUid = Binder.getCallingUid();
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                mAm.getLockTaskController().stopLockTaskMode(task, isSystemCaller, callingUid);
+            }
+            // Launch in-call UI if a call is ongoing. This is necessary to allow stopping the lock
+            // task and jumping straight into a call in the case of emergency call back.
+            TelecomManager tm = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
+            if (tm != null) {
+                tm.showInCallScreen(false);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public boolean isInLockTaskMode() {
+        return getLockTaskModeState() != LOCK_TASK_MODE_NONE;
+    }
+
+    @Override
+    public int getLockTaskModeState() {
+        synchronized (mGlobalLock) {
+            return mAm.getLockTaskController().getLockTaskModeState();
+        }
+    }
+
+    @Override
+    public void setTaskDescription(IBinder token, ActivityManager.TaskDescription td) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r != null) {
+                r.setTaskDescription(td);
+                final TaskRecord task = r.getTask();
+                task.updateTaskDescription();
+                mAm.mTaskChangeNotificationController.notifyTaskDescriptionChanged(task.taskId, td);
+            }
+        }
+    }
+
+    @Override
+    public Bundle getActivityOptions(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r != null) {
+                    final ActivityOptions activityOptions = r.takeOptionsLocked();
+                    return activityOptions == null ? null : activityOptions.toBundle();
+                }
+                return null;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public List<IBinder> getAppTasks(String callingPackage) {
+        int callingUid = Binder.getCallingUid();
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                return mAm.getRecentTasks().getAppTasksList(callingUid, callingPackage);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public void finishVoiceTask(IVoiceInteractionSession session) {
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                // TODO: VI Consider treating local voice interactions and voice tasks
+                // differently here
+                mStackSupervisor.finishVoiceTask(session);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+
+    }
+
+    @Override
+    public boolean isTopOfTask(IBinder token) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                throw new IllegalArgumentException();
+            }
+            return r.getTask().getTopActivity() == r;
+        }
+    }
+
+    @Override
+    public void notifyLaunchTaskBehindComplete(IBinder token) {
+        mStackSupervisor.scheduleLaunchTaskBehindComplete(token);
+    }
+
+    @Override
+    public void notifyEnterAnimationComplete(IBinder token) {
+        mAm.mHandler.sendMessage(mAm.mHandler.obtainMessage(ENTER_ANIMATION_COMPLETE_MSG, token));
+    }
+
+    /** Called from an app when assist data is ready. */
+    @Override
+    public void reportAssistContextExtras(IBinder token, Bundle extras, AssistStructure structure,
+            AssistContent content, Uri referrer) {
+        PendingAssistExtras pae = (PendingAssistExtras)token;
+        synchronized (pae) {
+            pae.result = extras;
+            pae.structure = structure;
+            pae.content = content;
+            if (referrer != null) {
+                pae.extras.putParcelable(Intent.EXTRA_REFERRER, referrer);
+            }
+            if (structure != null) {
+                structure.setHomeActivity(pae.isHome);
+            }
+            pae.haveResult = true;
+            pae.notifyAll();
+            if (pae.intent == null && pae.receiver == null) {
+                // Caller is just waiting for the result.
+                return;
+            }
+        }
+        // We are now ready to launch the assist activity.
+        IAssistDataReceiver sendReceiver = null;
+        Bundle sendBundle = null;
+        synchronized (mGlobalLock) {
+            buildAssistBundleLocked(pae, extras);
+            boolean exists = mPendingAssistExtras.remove(pae);
+            mAm.mUiHandler.removeCallbacks(pae);
+            if (!exists) {
+                // Timed out.
+                return;
+            }
+
+            if ((sendReceiver=pae.receiver) != null) {
+                // Caller wants result sent back to them.
+                sendBundle = new Bundle();
+                sendBundle.putBundle(ASSIST_KEY_DATA, pae.extras);
+                sendBundle.putParcelable(ASSIST_KEY_STRUCTURE, pae.structure);
+                sendBundle.putParcelable(ASSIST_KEY_CONTENT, pae.content);
+                sendBundle.putBundle(ASSIST_KEY_RECEIVER_EXTRAS, pae.receiverExtras);
+            }
+        }
+        if (sendReceiver != null) {
+            try {
+                sendReceiver.onHandleAssistData(sendBundle);
+            } catch (RemoteException e) {
+            }
+            return;
+        }
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            if (TextUtils.equals(pae.intent.getAction(),
+                    android.service.voice.VoiceInteractionService.SERVICE_INTERFACE)) {
+                pae.intent.putExtras(pae.extras);
+                mContext.startServiceAsUser(pae.intent, new UserHandle(pae.userHandle));
+            } else {
+                pae.intent.replaceExtras(pae.extras);
+                pae.intent.setFlags(FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_SINGLE_TOP
+                        | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+                mAm.closeSystemDialogs("assist");
+
+                try {
+                    mContext.startActivityAsUser(pae.intent, new UserHandle(pae.userHandle));
+                } catch (ActivityNotFoundException e) {
+                    Slog.w(TAG, "No activity to handle assist action.", e);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public int addAppTask(IBinder activityToken, Intent intent,
+            ActivityManager.TaskDescription description, Bitmap thumbnail) throws RemoteException {
+        final int callingUid = Binder.getCallingUid();
+        final long callingIdent = Binder.clearCallingIdentity();
+
+        try {
+            synchronized (mGlobalLock) {
+                ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
+                if (r == null) {
+                    throw new IllegalArgumentException("Activity does not exist; token="
+                            + activityToken);
+                }
+                ComponentName comp = intent.getComponent();
+                if (comp == null) {
+                    throw new IllegalArgumentException("Intent " + intent
+                            + " must specify explicit component");
+                }
+                if (thumbnail.getWidth() != mAm.mThumbnailWidth
+                        || thumbnail.getHeight() != mAm.mThumbnailHeight) {
+                    throw new IllegalArgumentException("Bad thumbnail size: got "
+                            + thumbnail.getWidth() + "x" + thumbnail.getHeight() + ", require "
+                            + mAm.mThumbnailWidth + "x" + mAm.mThumbnailHeight);
+                }
+                if (intent.getSelector() != null) {
+                    intent.setSelector(null);
+                }
+                if (intent.getSourceBounds() != null) {
+                    intent.setSourceBounds(null);
+                }
+                if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0) {
+                    if ((intent.getFlags()&Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS) == 0) {
+                        // The caller has added this as an auto-remove task...  that makes no
+                        // sense, so turn off auto-remove.
+                        intent.addFlags(Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS);
+                    }
+                }
+                final ActivityInfo ainfo = AppGlobals.getPackageManager().getActivityInfo(comp,
+                        STOCK_PM_FLAGS, UserHandle.getUserId(callingUid));
+                if (ainfo.applicationInfo.uid != callingUid) {
+                    throw new SecurityException(
+                            "Can't add task for another application: target uid="
+                                    + ainfo.applicationInfo.uid + ", calling uid=" + callingUid);
+                }
+
+                final ActivityStack stack = r.getStack();
+                final TaskRecord task = stack.createTaskRecord(
+                        mStackSupervisor.getNextTaskIdForUserLocked(r.userId), ainfo, intent,
+                        null /* voiceSession */, null /* voiceInteractor */, !ON_TOP);
+                if (!mAm.getRecentTasks().addToBottom(task)) {
+                    // The app has too many tasks already and we can't add any more
+                    stack.removeTask(task, "addAppTask", REMOVE_TASK_MODE_DESTROYING);
+                    return INVALID_TASK_ID;
+                }
+                task.lastTaskDescription.copyFrom(description);
+
+                // TODO: Send the thumbnail to WM to store it.
+
+                return task.taskId;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingIdent);
+        }
+    }
+
+    @Override
+    public Point getAppTaskThumbnailSize() {
+        synchronized (mGlobalLock) {
+            return new Point(mAm.mThumbnailWidth,  mAm.mThumbnailHeight);
+        }
+    }
+
+    @Override
+    public void setTaskResizeable(int taskId, int resizeableMode) {
+        synchronized (mGlobalLock) {
+            final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(
+                    taskId, MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+            if (task == null) {
+                Slog.w(TAG, "setTaskResizeable: taskId=" + taskId + " not found");
+                return;
+            }
+            task.setResizeMode(resizeableMode);
+        }
+    }
+
+    @Override
+    public void resizeTask(int taskId, Rect bounds, int resizeMode) {
+        mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "resizeTask()");
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+                if (task == null) {
+                    Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found");
+                    return;
+                }
+                // Place the task in the right stack if it isn't there already based on
+                // the requested bounds.
+                // The stack transition logic is:
+                // - a null bounds on a freeform task moves that task to fullscreen
+                // - a non-null bounds on a non-freeform (fullscreen OR docked) task moves
+                //   that task to freeform
+                // - otherwise the task is not moved
+                ActivityStack stack = task.getStack();
+                if (!task.getWindowConfiguration().canResizeTask()) {
+                    throw new IllegalArgumentException("resizeTask not allowed on task=" + task);
+                }
+                if (bounds == null && stack.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+                    stack = stack.getDisplay().getOrCreateStack(
+                            WINDOWING_MODE_FULLSCREEN, stack.getActivityType(), ON_TOP);
+                } else if (bounds != null && stack.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
+                    stack = stack.getDisplay().getOrCreateStack(
+                            WINDOWING_MODE_FREEFORM, stack.getActivityType(), ON_TOP);
+                }
+
+                // Reparent the task to the right stack if necessary
+                boolean preserveWindow = (resizeMode & RESIZE_MODE_PRESERVE_WINDOW) != 0;
+                if (stack != task.getStack()) {
+                    // Defer resume until the task is resized below
+                    task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE,
+                            DEFER_RESUME, "resizeTask");
+                    preserveWindow = false;
+                }
+
+                // After reparenting (which only resizes the task to the stack bounds), resize the
+                // task to the actual bounds provided
+                task.resize(bounds, resizeMode, preserveWindow, !DEFER_RESUME);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public boolean releaseActivityInstance(IBinder token) {
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r == null) {
+                    return false;
+                }
+                return r.getStack().safelyDestroyActivityLocked(r, "app-req");
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public void releaseSomeActivities(IApplicationThread appInt) {
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                ProcessRecord app = mAm.getRecordForAppLocked(appInt);
+                mStackSupervisor.releaseSomeActivitiesLocked(app, "low-mem");
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public void setLockScreenShown(boolean keyguardShowing, boolean aodShowing,
+            int secondaryDisplayShowing) {
+        if (mAm.checkCallingPermission(android.Manifest.permission.DEVICE_POWER)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Requires permission "
+                    + android.Manifest.permission.DEVICE_POWER);
+        }
+
+        synchronized (mGlobalLock) {
+            long ident = Binder.clearCallingIdentity();
+            if (mKeyguardShown != keyguardShowing) {
+                mKeyguardShown = keyguardShowing;
+                reportCurKeyguardUsageEventLocked(keyguardShowing);
+            }
+            try {
+                mAm.mKeyguardController.setKeyguardShown(keyguardShowing, aodShowing,
+                        secondaryDisplayShowing);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        mAm.mHandler.obtainMessage(DISPATCH_SCREEN_KEYGUARD_MSG, keyguardShowing ? 1 : 0, 0)
+                .sendToTarget();
+    }
+
+    @Override
+    public Bitmap getTaskDescriptionIcon(String filePath, int userId) {
+        userId = mAm.mUserController.handleIncomingUser(Binder.getCallingPid(),
+                Binder.getCallingUid(), userId, false, ALLOW_FULL_ONLY, "getTaskDescriptionIcon", null);
+
+        final File passedIconFile = new File(filePath);
+        final File legitIconFile = new File(TaskPersister.getUserImagesDir(userId),
+                passedIconFile.getName());
+        if (!legitIconFile.getPath().equals(filePath)
+                || !filePath.contains(ActivityRecord.ACTIVITY_ICON_SUFFIX)) {
+            throw new IllegalArgumentException("Bad file path: " + filePath
+                    + " passed for userId " + userId);
+        }
+        return mAm.getRecentTasks().getTaskDescriptionIcon(filePath);
+    }
+
+    @Override
+    public void startInPlaceAnimationOnFrontMostApplication(Bundle opts)
+            throws RemoteException {
+        final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(opts);
+        final ActivityOptions activityOptions = safeOptions != null
+                ? safeOptions.getOptions(mStackSupervisor)
+                : null;
+        if (activityOptions == null
+                || activityOptions.getAnimationType() != ActivityOptions.ANIM_CUSTOM_IN_PLACE
+                || activityOptions.getCustomInPlaceResId() == 0) {
+            throw new IllegalArgumentException("Expected in-place ActivityOption " +
+                    "with valid animation");
+        }
+        mAm.mWindowManager.prepareAppTransition(TRANSIT_TASK_IN_PLACE, false);
+        mAm.mWindowManager.overridePendingAppTransitionInPlace(activityOptions.getPackageName(),
+                activityOptions.getCustomInPlaceResId());
+        mAm.mWindowManager.executeAppTransition();
+    }
+
+    @Override
+    public void removeStack(int stackId) {
+        mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "removeStack()");
+        synchronized (mGlobalLock) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                final ActivityStack stack = mStackSupervisor.getStack(stackId);
+                if (stack == null) {
+                    Slog.w(TAG, "removeStack: No stack with id=" + stackId);
+                    return;
+                }
+                if (!stack.isActivityTypeStandardOrUndefined()) {
+                    throw new IllegalArgumentException(
+                            "Removing non-standard stack is not allowed.");
+                }
+                mStackSupervisor.removeStack(stack);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
+    public void moveStackToDisplay(int stackId, int displayId) {
+        mAm.enforceCallingPermission(INTERNAL_SYSTEM_WINDOW, "moveStackToDisplay()");
+
+        synchronized (mGlobalLock) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                if (DEBUG_STACK) Slog.d(TAG_STACK, "moveStackToDisplay: moving stackId=" + stackId
+                        + " to displayId=" + displayId);
+                mStackSupervisor.moveStackToDisplayLocked(stackId, displayId, ON_TOP);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
+    public int createStackOnDisplay(int displayId) {
+        mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "createStackOnDisplay()");
+        synchronized (mGlobalLock) {
+            final ActivityDisplay display =
+                    mStackSupervisor.getActivityDisplayOrCreateLocked(displayId);
+            if (display == null) {
+                return INVALID_STACK_ID;
+            }
+            // TODO(multi-display): Have the caller pass in the windowing mode and activity type.
+            final ActivityStack stack = display.createStack(
+                    WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD,
+                    ON_TOP);
+            return (stack != null) ? stack.mStackId : INVALID_STACK_ID;
+        }
+    }
+
+    @Override
+    public void exitFreeformMode(IBinder token) {
+        synchronized (mGlobalLock) {
+            long ident = Binder.clearCallingIdentity();
+            try {
+                final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+                if (r == null) {
+                    throw new IllegalArgumentException(
+                            "exitFreeformMode: No activity record matching token=" + token);
+                }
+
+                final ActivityStack stack = r.getStack();
+                if (stack == null || !stack.inFreeformWindowingMode()) {
+                    throw new IllegalStateException(
+                            "exitFreeformMode: You can only go fullscreen from freeform.");
+                }
+
+                stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    /** Sets the task stack listener that gets callbacks when a task stack changes. */
+    @Override
+    public void registerTaskStackListener(ITaskStackListener listener) {
+        mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "registerTaskStackListener()");
+        mAm.mTaskChangeNotificationController.registerTaskStackListener(listener);
+    }
+
+    /** Unregister a task stack listener so that it stops receiving callbacks. */
+    @Override
+    public void unregisterTaskStackListener(ITaskStackListener listener) {
+        mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "unregisterTaskStackListener()");
+        mAm.mTaskChangeNotificationController.unregisterTaskStackListener(listener);
+    }
+
+    private void reportCurKeyguardUsageEventLocked(boolean keyguardShowing) {
+        mAm.reportGlobalUsageEventLocked(keyguardShowing
+                ? UsageEvents.Event.KEYGUARD_SHOWN
+                : UsageEvents.Event.KEYGUARD_HIDDEN);
+    }
+
+    @Override
+    public boolean requestAssistContextExtras(int requestType, IAssistDataReceiver receiver,
+            Bundle receiverExtras, IBinder activityToken, boolean focused, boolean newSessionId) {
+        return enqueueAssistContext(requestType, null, null, receiver, receiverExtras,
+                activityToken, focused, newSessionId, UserHandle.getCallingUserId(), null,
+                PENDING_ASSIST_EXTRAS_LONG_TIMEOUT, 0) != null;
+    }
+
+    @Override
+    public boolean requestAutofillData(IAssistDataReceiver receiver, Bundle receiverExtras,
+            IBinder activityToken, int flags) {
+        return enqueueAssistContext(ActivityManager.ASSIST_CONTEXT_AUTOFILL, null, null,
+                receiver, receiverExtras, activityToken, true, true, UserHandle.getCallingUserId(),
+                null, PENDING_AUTOFILL_ASSIST_STRUCTURE_TIMEOUT, flags) != null;
+    }
+
+    @Override
+    public boolean launchAssistIntent(Intent intent, int requestType, String hint, int userHandle,
+            Bundle args) {
+        return enqueueAssistContext(requestType, intent, hint, null, null, null,
+                true /* focused */, true /* newSessionId */, userHandle, args,
+                PENDING_ASSIST_EXTRAS_TIMEOUT, 0) != null;
+    }
+
+    @Override
+    public Bundle getAssistContextExtras(int requestType) {
+        PendingAssistExtras pae = enqueueAssistContext(requestType, null, null, null,
+                null, null, true /* focused */, true /* newSessionId */,
+                UserHandle.getCallingUserId(), null, PENDING_ASSIST_EXTRAS_TIMEOUT, 0);
+        if (pae == null) {
+            return null;
+        }
+        synchronized (pae) {
+            while (!pae.haveResult) {
+                try {
+                    pae.wait();
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+        synchronized (mGlobalLock) {
+            buildAssistBundleLocked(pae, pae.result);
+            mPendingAssistExtras.remove(pae);
+            mAm.mUiHandler.removeCallbacks(pae);
+        }
+        return pae.extras;
+    }
+
+    private PendingAssistExtras enqueueAssistContext(int requestType, Intent intent, String hint,
+            IAssistDataReceiver receiver, Bundle receiverExtras, IBinder activityToken,
+            boolean focused, boolean newSessionId, int userHandle, Bundle args, long timeout,
+            int flags) {
+        mAm.enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,
+                "enqueueAssistContext()");
+
+        synchronized (mGlobalLock) {
+            ActivityRecord activity = mAm.getFocusedStack().getTopActivity();
+            if (activity == null) {
+                Slog.w(TAG, "getAssistContextExtras failed: no top activity");
+                return null;
+            }
+            if (activity.app == null || activity.app.thread == null) {
+                Slog.w(TAG, "getAssistContextExtras failed: no process for " + activity);
+                return null;
+            }
+            if (focused) {
+                if (activityToken != null) {
+                    ActivityRecord caller = ActivityRecord.forTokenLocked(activityToken);
+                    if (activity != caller) {
+                        Slog.w(TAG, "enqueueAssistContext failed: caller " + caller
+                                + " is not current top " + activity);
+                        return null;
+                    }
+                }
+            } else {
+                activity = ActivityRecord.forTokenLocked(activityToken);
+                if (activity == null) {
+                    Slog.w(TAG, "enqueueAssistContext failed: activity for token=" + activityToken
+                            + " couldn't be found");
+                    return null;
+                }
+                if (activity.app == null || activity.app.thread == null) {
+                    Slog.w(TAG, "enqueueAssistContext failed: no process for " + activity);
+                    return null;
+                }
+            }
+
+            PendingAssistExtras pae;
+            Bundle extras = new Bundle();
+            if (args != null) {
+                extras.putAll(args);
+            }
+            extras.putString(Intent.EXTRA_ASSIST_PACKAGE, activity.packageName);
+            extras.putInt(Intent.EXTRA_ASSIST_UID, activity.app.uid);
+
+            pae = new PendingAssistExtras(activity, extras, intent, hint, receiver, receiverExtras,
+                    userHandle);
+            pae.isHome = activity.isActivityTypeHome();
+
+            // Increment the sessionId if necessary
+            if (newSessionId) {
+                mViSessionId++;
+            }
+            try {
+                activity.app.thread.requestAssistContextExtras(activity.appToken, pae, requestType,
+                        mViSessionId, flags);
+                mPendingAssistExtras.add(pae);
+                mAm.mUiHandler.postDelayed(pae, timeout);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "getAssistContextExtras failed: crash calling " + activity);
+                return null;
+            }
+            return pae;
+        }
+    }
+
+    private void buildAssistBundleLocked(PendingAssistExtras pae, Bundle result) {
+        if (result != null) {
+            pae.extras.putBundle(Intent.EXTRA_ASSIST_CONTEXT, result);
+        }
+        if (pae.hint != null) {
+            pae.extras.putBoolean(pae.hint, true);
+        }
+    }
+
+    private void pendingAssistExtrasTimedOut(PendingAssistExtras pae) {
+        IAssistDataReceiver receiver;
+        synchronized (mGlobalLock) {
+            mPendingAssistExtras.remove(pae);
+            receiver = pae.receiver;
+        }
+        if (receiver != null) {
+            // Caller wants result sent back to them.
+            Bundle sendBundle = new Bundle();
+            // At least return the receiver extras
+            sendBundle.putBundle(ASSIST_KEY_RECEIVER_EXTRAS, pae.receiverExtras);
+            try {
+                pae.receiver.onHandleAssistData(sendBundle);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
+    public class PendingAssistExtras extends Binder implements Runnable {
+        public final ActivityRecord activity;
+        public boolean isHome;
+        public final Bundle extras;
+        public final Intent intent;
+        public final String hint;
+        public final IAssistDataReceiver receiver;
+        public final int userHandle;
+        public boolean haveResult = false;
+        public Bundle result = null;
+        public AssistStructure structure = null;
+        public AssistContent content = null;
+        public Bundle receiverExtras;
+
+        public PendingAssistExtras(ActivityRecord _activity, Bundle _extras, Intent _intent,
+                String _hint, IAssistDataReceiver _receiver, Bundle _receiverExtras,
+                int _userHandle) {
+            activity = _activity;
+            extras = _extras;
+            intent = _intent;
+            hint = _hint;
+            receiver = _receiver;
+            receiverExtras = _receiverExtras;
+            userHandle = _userHandle;
+        }
+
+        @Override
+        public void run() {
+            Slog.w(TAG, "getAssistContextExtras failed: timeout retrieving from " + activity);
+            synchronized (this) {
+                haveResult = true;
+                notifyAll();
+            }
+            pendingAssistExtrasTimedOut(this);
+        }
+    }
+
+    @Override
+    public boolean isAssistDataAllowedOnCurrentActivity() {
+        int userId;
+        synchronized (mGlobalLock) {
+            final ActivityStack focusedStack = mAm.getFocusedStack();
+            if (focusedStack == null || focusedStack.isActivityTypeAssistant()) {
+                return false;
+            }
+
+            final ActivityRecord activity = focusedStack.getTopActivity();
+            if (activity == null) {
+                return false;
+            }
+            userId = activity.userId;
+        }
+        return !DevicePolicyCache.getInstance().getScreenCaptureDisabled(userId);
+    }
+
+    @Override
+    public boolean showAssistFromActivity(IBinder token, Bundle args) {
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                ActivityRecord caller = ActivityRecord.forTokenLocked(token);
+                ActivityRecord top = mAm.getFocusedStack().getTopActivity();
+                if (top != caller) {
+                    Slog.w(TAG, "showAssistFromActivity failed: caller " + caller
+                            + " is not current top " + top);
+                    return false;
+                }
+                if (!top.nowVisible) {
+                    Slog.w(TAG, "showAssistFromActivity failed: caller " + caller
+                            + " is not visible");
+                    return false;
+                }
+            }
+            return mAssistUtils.showSessionForActiveService(args, SHOW_SOURCE_APPLICATION, null,
+                    token);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public boolean isRootVoiceInteraction(IBinder token) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return false;
+            }
+            return r.rootVoiceInteraction;
+        }
+    }
+
+    @Override
+    public ComponentName getActivityClassForToken(IBinder token) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return null;
+            }
+            return r.intent.getComponent();
+        }
+    }
+
+    @Override
+    public String getPackageForToken(IBinder token) {
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return null;
+            }
+            return r.packageName;
+        }
+    }
+
+    @Override
+    public void showLockTaskEscapeMessage(IBinder token) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+            if (r == null) {
+                return;
+            }
+            mAm.getLockTaskController().showLockTaskToast();
+        }
+    }
+
+    @Override
+    public void keyguardGoingAway(int flags) {
+        mAm.enforceNotIsolatedCaller("keyguardGoingAway");
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                mAm.mKeyguardController.keyguardGoingAway(flags);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    /**
+     * Try to place task to provided position. The final position might be different depending on
+     * current user and stacks state. The task will be moved to target stack if it's currently in
+     * different stack.
+     */
+    @Override
+    public void positionTaskInStack(int taskId, int stackId, int position) {
+        mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "positionTaskInStack()");
+        synchronized (mGlobalLock) {
+            long ident = Binder.clearCallingIdentity();
+            try {
+                if (DEBUG_STACK) Slog.d(TAG_STACK, "positionTaskInStack: positioning task="
+                        + taskId + " in stackId=" + stackId + " at position=" + position);
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+                if (task == null) {
+                    throw new IllegalArgumentException("positionTaskInStack: no task for id="
+                            + taskId);
+                }
+
+                final ActivityStack stack = mStackSupervisor.getStack(stackId);
+
+                if (stack == null) {
+                    throw new IllegalArgumentException("positionTaskInStack: no stack for id="
+                            + stackId);
+                }
+                if (!stack.isActivityTypeStandardOrUndefined()) {
+                    throw new IllegalArgumentException("positionTaskInStack: Attempt to change"
+                            + " the position of task " + taskId + " in/to non-standard stack");
+                }
+
+                // TODO: Have the callers of this API call a separate reparent method if that is
+                // what they intended to do vs. having this method also do reparenting.
+                if (task.getStack() == stack) {
+                    // Change position in current stack.
+                    stack.positionChildAt(task, position);
+                } else {
+                    // Reparent to new stack.
+                    task.reparent(stack, position, REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE,
+                            !DEFER_RESUME, "positionTaskInStack");
+                }
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
+    public void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration,
+            int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) {
+        if (DEBUG_CONFIGURATION) Slog.v(TAG, "Report configuration: " + token + " "
+                + horizontalSizeConfiguration + " " + verticalSizeConfigurations);
+        synchronized (mGlobalLock) {
+            ActivityRecord record = ActivityRecord.isInStackLocked(token);
+            if (record == null) {
+                throw new IllegalArgumentException("reportSizeConfigurations: ActivityRecord not "
+                        + "found for: " + token);
+            }
+            record.setSizeConfigurations(horizontalSizeConfiguration,
+                    verticalSizeConfigurations, smallestSizeConfigurations);
+        }
+    }
+
+    /**
+     * Dismisses split-screen multi-window mode.
+     * @param toTop If true the current primary split-screen stack will be placed or left on top.
+     */
+    @Override
+    public void dismissSplitScreenMode(boolean toTop) {
+        mAm.enforceCallerIsRecentsOrHasPermission(
+                MANAGE_ACTIVITY_STACKS, "dismissSplitScreenMode()");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityStack stack =
+                        mStackSupervisor.getDefaultDisplay().getSplitScreenPrimaryStack();
+                if (stack == null) {
+                    Slog.w(TAG, "dismissSplitScreenMode: primary split-screen stack not found.");
+                    return;
+                }
+
+                if (toTop) {
+                    // Caller wants the current split-screen primary stack to be the top stack after
+                    // it goes fullscreen, so move it to the front.
+                    stack.moveToFront("dismissSplitScreenMode");
+                } else if (mStackSupervisor.isFocusedStack(stack)) {
+                    // In this case the current split-screen primary stack shouldn't be the top
+                    // stack after it goes fullscreen, but it current has focus, so we move the
+                    // focus to the top-most split-screen secondary stack next to it.
+                    final ActivityStack otherStack = stack.getDisplay().getTopStackInWindowingMode(
+                            WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+                    if (otherStack != null) {
+                        otherStack.moveToFront("dismissSplitScreenMode_other");
+                    }
+                }
+
+                stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    /**
+     * Dismisses Pip
+     * @param animate True if the dismissal should be animated.
+     * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
+     *                          default animation duration should be used.
+     */
+    @Override
+    public void dismissPip(boolean animate, int animationDuration) {
+        mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "dismissPip()");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final PinnedActivityStack stack =
+                        mStackSupervisor.getDefaultDisplay().getPinnedStack();
+                if (stack == null) {
+                    Slog.w(TAG, "dismissPip: pinned stack not found.");
+                    return;
+                }
+                if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
+                    throw new IllegalArgumentException("Stack: " + stack
+                            + " doesn't support animated resize.");
+                }
+                if (animate) {
+                    stack.animateResizePinnedStack(null /* sourceHintBounds */,
+                            null /* destBounds */, animationDuration, false /* fromFullscreen */);
+                } else {
+                    mStackSupervisor.moveTasksToFullscreenStackLocked(stack, true /* onTop */);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public void suppressResizeConfigChanges(boolean suppress) throws RemoteException {
+        mAm.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "suppressResizeConfigChanges()");
+        synchronized (mGlobalLock) {
+            mSuppressResizeConfigChanges = suppress;
+        }
+    }
+
+    /**
+     * NOTE: For the pinned stack, this method is usually called after the bounds animation has
+     *       animated the stack to the fullscreen, but can also be called if we are relaunching an
+     *       activity and clearing the task at the same time.
+     */
+    @Override
+    // TODO: API should just be about changing windowing modes...
+    public void moveTasksToFullscreenStack(int fromStackId, boolean onTop) {
+        mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "moveTasksToFullscreenStack()");
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                final ActivityStack stack = mStackSupervisor.getStack(fromStackId);
+                if (stack != null){
+                    if (!stack.isActivityTypeStandardOrUndefined()) {
+                        throw new IllegalArgumentException(
+                                "You can't move tasks from non-standard stacks.");
+                    }
+                    mStackSupervisor.moveTasksToFullscreenStackLocked(stack, onTop);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    /**
+     * Moves the top activity in the input stackId to the pinned stack.
+     *
+     * @param stackId Id of stack to move the top activity to pinned stack.
+     * @param bounds Bounds to use for pinned stack.
+     *
+     * @return True if the top activity of the input stack was successfully moved to the pinned
+     *          stack.
+     */
+    @Override
+    public boolean moveTopActivityToPinnedStack(int stackId, Rect bounds) {
+        mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "moveTopActivityToPinnedStack()");
+        synchronized (mGlobalLock) {
+            if (!mAm.mSupportsPictureInPicture) {
+                throw new IllegalStateException("moveTopActivityToPinnedStack:"
+                        + "Device doesn't support picture-in-picture mode");
+            }
+
+            long ident = Binder.clearCallingIdentity();
+            try {
+                return mStackSupervisor.moveTopStackActivityToPinnedStackLocked(stackId, bounds);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
+    public boolean isInMultiWindowMode(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r == null) {
+                    return false;
+                }
+                // An activity is consider to be in multi-window mode if its task isn't fullscreen.
+                return r.inMultiWindowMode();
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public boolean isInPictureInPictureMode(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                return isInPictureInPictureMode(ActivityRecord.forTokenLocked(token));
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    private boolean isInPictureInPictureMode(ActivityRecord r) {
+        if (r == null || r.getStack() == null || !r.inPinnedWindowingMode()
+                || r.getStack().isInStackLocked(r) == null) {
+            return false;
+        }
+
+        // If we are animating to fullscreen then we have already dispatched the PIP mode
+        // changed, so we should reflect that check here as well.
+        final PinnedActivityStack stack = r.getStack();
+        final PinnedStackWindowController windowController = stack.getWindowContainerController();
+        return !windowController.isAnimatingBoundsToFullscreen();
+    }
+
+    @Override
+    public boolean enterPictureInPictureMode(IBinder token, final PictureInPictureParams params) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ensureValidPictureInPictureActivityParamsLocked(
+                        "enterPictureInPictureMode", token, params);
+
+                // If the activity is already in picture in picture mode, then just return early
+                if (isInPictureInPictureMode(r)) {
+                    return true;
+                }
+
+                // Activity supports picture-in-picture, now check that we can enter PiP at this
+                // point, if it is
+                if (!r.checkEnterPictureInPictureState("enterPictureInPictureMode",
+                        false /* beforeStopping */)) {
+                    return false;
+                }
+
+                final Runnable enterPipRunnable = () -> {
+                    // Only update the saved args from the args that are set
+                    r.pictureInPictureArgs.copyOnlySet(params);
+                    final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
+                    final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
+                    // Adjust the source bounds by the insets for the transition down
+                    final Rect sourceBounds = new Rect(r.pictureInPictureArgs.getSourceRectHint());
+                    mStackSupervisor.moveActivityToPinnedStackLocked(r, sourceBounds, aspectRatio,
+                            "enterPictureInPictureMode");
+                    final PinnedActivityStack stack = r.getStack();
+                    stack.setPictureInPictureAspectRatio(aspectRatio);
+                    stack.setPictureInPictureActions(actions);
+                    MetricsLoggerWrapper.logPictureInPictureEnter(mContext, r.appInfo.uid,
+                            r.shortComponentName, r.supportsEnterPipOnTaskSwitch);
+                    logPictureInPictureArgs(params);
+                };
+
+                if (mAm.isKeyguardLocked()) {
+                    // If the keyguard is showing or occluded, then try and dismiss it before
+                    // entering picture-in-picture (this will prompt the user to authenticate if the
+                    // device is currently locked).
+                    dismissKeyguard(token, new KeyguardDismissCallback() {
+                        @Override
+                        public void onDismissSucceeded() throws RemoteException {
+                            mAm.mHandler.post(enterPipRunnable);
+                        }
+                    }, null /* message */);
+                } else {
+                    // Enter picture in picture immediately otherwise
+                    enterPipRunnable.run();
+                }
+                return true;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public void setPictureInPictureParams(IBinder token, final PictureInPictureParams params) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ensureValidPictureInPictureActivityParamsLocked(
+                        "setPictureInPictureParams", token, params);
+
+                // Only update the saved args from the args that are set
+                r.pictureInPictureArgs.copyOnlySet(params);
+                if (r.inPinnedWindowingMode()) {
+                    // If the activity is already in picture-in-picture, update the pinned stack now
+                    // if it is not already expanding to fullscreen. Otherwise, the arguments will
+                    // be used the next time the activity enters PiP
+                    final PinnedActivityStack stack = r.getStack();
+                    if (!stack.isAnimatingBoundsToFullscreen()) {
+                        stack.setPictureInPictureAspectRatio(
+                                r.pictureInPictureArgs.getAspectRatio());
+                        stack.setPictureInPictureActions(r.pictureInPictureArgs.getActions());
+                    }
+                }
+                logPictureInPictureArgs(params);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public int getMaxNumPictureInPictureActions(IBinder token) {
+        // Currently, this is a static constant, but later, we may change this to be dependent on
+        // the context of the activity
+        return 3;
+    }
+
+    private void logPictureInPictureArgs(PictureInPictureParams params) {
+        if (params.hasSetActions()) {
+            MetricsLogger.histogram(mContext, "tron_varz_picture_in_picture_actions_count",
+                    params.getActions().size());
+        }
+        if (params.hasSetAspectRatio()) {
+            LogMaker lm = new LogMaker(MetricsEvent.ACTION_PICTURE_IN_PICTURE_ASPECT_RATIO_CHANGED);
+            lm.addTaggedData(MetricsEvent.PICTURE_IN_PICTURE_ASPECT_RATIO, params.getAspectRatio());
+            MetricsLogger.action(lm);
+        }
+    }
+
+    /**
+     * Checks the state of the system and the activity associated with the given {@param token} to
+     * verify that picture-in-picture is supported for that activity.
+     *
+     * @return the activity record for the given {@param token} if all the checks pass.
+     */
+    private ActivityRecord ensureValidPictureInPictureActivityParamsLocked(String caller,
+            IBinder token, PictureInPictureParams params) {
+        if (!mAm.mSupportsPictureInPicture) {
+            throw new IllegalStateException(caller
+                    + ": Device doesn't support picture-in-picture mode.");
+        }
+
+        final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+        if (r == null) {
+            throw new IllegalStateException(caller
+                    + ": Can't find activity for token=" + token);
+        }
+
+        if (!r.supportsPictureInPicture()) {
+            throw new IllegalStateException(caller
+                    + ": Current activity does not support picture-in-picture.");
+        }
+
+        if (params.hasSetAspectRatio()
+                && !mAm.mWindowManager.isValidPictureInPictureAspectRatio(r.getStack().mDisplayId,
+                params.getAspectRatio())) {
+            final float minAspectRatio = mContext.getResources().getFloat(
+                    com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
+            final float maxAspectRatio = mContext.getResources().getFloat(
+                    com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
+            throw new IllegalArgumentException(String.format(caller
+                            + ": Aspect ratio is too extreme (must be between %f and %f).",
+                    minAspectRatio, maxAspectRatio));
+        }
+
+        // Truncate the number of actions if necessary
+        params.truncateActions(getMaxNumPictureInPictureActions(token));
+
+        return r;
+    }
+
+    @Override
+    public IBinder getUriPermissionOwnerForActivity(IBinder activityToken) {
+        mAm.enforceNotIsolatedCaller("getUriPermissionOwnerForActivity");
+        synchronized (mGlobalLock) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(activityToken);
+            if (r == null) {
+                throw new IllegalArgumentException("Activity does not exist; token="
+                        + activityToken);
+            }
+            return r.getUriPermissionsLocked().getExternalTokenLocked();
+        }
+    }
+
+    @Override
+    public void resizeDockedStack(Rect dockedBounds, Rect tempDockedTaskBounds,
+            Rect tempDockedTaskInsetBounds,
+            Rect tempOtherTaskBounds, Rect tempOtherTaskInsetBounds) {
+        mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeDockedStack()");
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.resizeDockedStackLocked(dockedBounds, tempDockedTaskBounds,
+                        tempDockedTaskInsetBounds, tempOtherTaskBounds, tempOtherTaskInsetBounds,
+                        PRESERVE_WINDOWS);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public void setSplitScreenResizing(boolean resizing) {
+        mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "setSplitScreenResizing()");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.setSplitScreenResizing(resizing);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public int setVrMode(IBinder token, boolean enabled, ComponentName packageName) {
+        mAm.enforceSystemHasVrFeature();
+
+        final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
+
+        ActivityRecord r;
+        synchronized (mGlobalLock) {
+            r = ActivityRecord.isInStackLocked(token);
+        }
+
+        if (r == null) {
+            throw new IllegalArgumentException();
+        }
+
+        int err;
+        if ((err = vrService.hasVrPackage(packageName, r.userId)) !=
+                VrManagerInternal.NO_ERROR) {
+            return err;
+        }
+
+        // Clear the binder calling uid since this path may call moveToTask().
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                r.requestedVrComponent = (enabled) ? packageName : null;
+
+                // Update associated state if this activity is currently focused
+                if (r == mStackSupervisor.getResumedActivityLocked()) {
+                    mAm.applyUpdateVrModeLocked(r);
+                }
+                return 0;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+    }
+
+    @Override
+    public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options) {
+        Slog.i(TAG, "Activity tried to startLocalVoiceInteraction");
+        synchronized (mGlobalLock) {
+            ActivityRecord activity = mAm.getFocusedStack().getTopActivity();
+            if (ActivityRecord.forTokenLocked(callingActivity) != activity) {
+                throw new SecurityException("Only focused activity can call startVoiceInteraction");
+            }
+            if (mAm.mRunningVoice != null || activity.getTask().voiceSession != null
+                    || activity.voiceSession != null) {
+                Slog.w(TAG, "Already in a voice interaction, cannot start new voice interaction");
+                return;
+            }
+            if (activity.pendingVoiceInteractionStart) {
+                Slog.w(TAG, "Pending start of voice interaction already.");
+                return;
+            }
+            activity.pendingVoiceInteractionStart = true;
+        }
+        LocalServices.getService(VoiceInteractionManagerInternal.class)
+                .startLocalVoiceInteraction(callingActivity, options);
+    }
+
+    @Override
+    public void stopLocalVoiceInteraction(IBinder callingActivity) {
+        LocalServices.getService(VoiceInteractionManagerInternal.class)
+                .stopLocalVoiceInteraction(callingActivity);
+    }
+
+    @Override
+    public boolean supportsLocalVoiceInteraction() {
+        return LocalServices.getService(VoiceInteractionManagerInternal.class)
+                .supportsLocalVoiceInteraction();
+    }
+
+    /** Notifies all listeners when the pinned stack animation starts. */
+    @Override
+    public void notifyPinnedStackAnimationStarted() {
+        mAm.mTaskChangeNotificationController.notifyPinnedStackAnimationStarted();
+    }
+
+    /** Notifies all listeners when the pinned stack animation ends. */
+    @Override
+    public void notifyPinnedStackAnimationEnded() {
+        mAm.mTaskChangeNotificationController.notifyPinnedStackAnimationEnded();
+    }
+
+    @Override
+    public void resizePinnedStack(Rect pinnedBounds, Rect tempPinnedTaskBounds) {
+        mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizePinnedStack()");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                mStackSupervisor.resizePinnedStackLocked(pinnedBounds, tempPinnedTaskBounds);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public boolean updateDisplayOverrideConfiguration(Configuration values, int displayId) {
+        mAm.enforceCallingPermission(CHANGE_CONFIGURATION, "updateDisplayOverrideConfiguration()");
+
+        synchronized (mGlobalLock) {
+            // Check if display is initialized in AM.
+            if (!mStackSupervisor.isDisplayAdded(displayId)) {
+                // Call might come when display is not yet added or has already been removed.
+                if (DEBUG_CONFIGURATION) {
+                    Slog.w(TAG, "Trying to update display configuration for non-existing displayId="
+                            + displayId);
+                }
+                return false;
+            }
+
+            if (values == null && mAm.mWindowManager != null) {
+                // sentinel: fetch the current configuration from the window manager
+                values = mAm.mWindowManager.computeNewConfiguration(displayId);
+            }
+
+            if (mAm.mWindowManager != null) {
+                // Update OOM levels based on display size.
+                mAm.mProcessList.applyDisplaySize(mAm.mWindowManager);
+            }
+
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                if (values != null) {
+                    Settings.System.clearConfiguration(values);
+                }
+                mAm.updateDisplayOverrideConfigurationLocked(values, null /* starting */,
+                        false /* deferResume */, displayId, mTmpUpdateConfigurationResult);
+                return mTmpUpdateConfigurationResult.changes != 0;
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public boolean updateConfiguration(Configuration values) {
+        mAm.enforceCallingPermission(CHANGE_CONFIGURATION, "updateConfiguration()");
+
+        synchronized (mGlobalLock) {
+            if (values == null && mAm.mWindowManager != null) {
+                // sentinel: fetch the current configuration from the window manager
+                values = mAm.mWindowManager.computeNewConfiguration(DEFAULT_DISPLAY);
+            }
+
+            if (mAm.mWindowManager != null) {
+                // Update OOM levels based on display size.
+                mAm.mProcessList.applyDisplaySize(mAm.mWindowManager);
+            }
+
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                if (values != null) {
+                    Settings.System.clearConfiguration(values);
+                }
+                mAm.updateConfigurationLocked(values, null, false, false /* persistent */,
+                        UserHandle.USER_NULL, false /* deferResume */,
+                        mTmpUpdateConfigurationResult);
+                return mTmpUpdateConfigurationResult.changes != 0;
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback,
+            CharSequence message) {
+        if (message != null) {
+            mAm.enforceCallingPermission(
+                    Manifest.permission.SHOW_KEYGUARD_MESSAGE, "dismissKeyguard()");
+        }
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                mAm.mKeyguardController.dismissKeyguard(token, callback, message);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+    }
+
+    @Override
+    public void cancelTaskWindowTransition(int taskId) {
+        mAm.enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS,
+                "cancelTaskWindowTransition()");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                        MATCH_TASK_IN_STACKS_ONLY);
+                if (task == null) {
+                    Slog.w(TAG, "cancelTaskWindowTransition: taskId=" + taskId + " not found");
+                    return;
+                }
+                task.cancelWindowTransition();
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public ActivityManager.TaskSnapshot getTaskSnapshot(int taskId, boolean reducedResolution) {
+        mAm.enforceCallerIsRecentsOrHasPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            final TaskRecord task;
+            synchronized (mGlobalLock) {
+                task = mStackSupervisor.anyTaskForIdLocked(taskId,
+                        MATCH_TASK_IN_STACKS_OR_RECENT_TASKS);
+                if (task == null) {
+                    Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found");
+                    return null;
+                }
+            }
+            // Don't call this while holding the lock as this operation might hit the disk.
+            return task.getSnapshot(reducedResolution);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
+    public void setDisablePreviewScreenshots(IBinder token, boolean disable) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                Slog.w(TAG, "setDisablePreviewScreenshots: Unable to find activity for token="
+                        + token);
+                return;
+            }
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                r.setDisablePreviewScreenshots(disable);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    /** Return the user id of the last resumed activity. */
+    @Override
+    public @UserIdInt
+    int getLastResumedActivityUserId() {
+        mAm.enforceCallingPermission(
+                Manifest.permission.INTERACT_ACROSS_USERS_FULL, "getLastResumedActivityUserId()");
+        synchronized (mGlobalLock) {
+            if (mAm.mLastResumedActivity == null) {
+                return mAm.mUserController.getCurrentUserId();
+            }
+            return mAm.mLastResumedActivity.userId;
+        }
+    }
+
+    @Override
+    public void updateLockTaskFeatures(int userId, int flags) {
+        final int callingUid = Binder.getCallingUid();
+        if (callingUid != 0 && callingUid != SYSTEM_UID) {
+            mAm.enforceCallingPermission(android.Manifest.permission.UPDATE_LOCK_TASK_PACKAGES,
+                    "updateLockTaskFeatures()");
+        }
+        synchronized (mGlobalLock) {
+            if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Allowing features " + userId + ":0x" +
+                    Integer.toHexString(flags));
+            mAm.getLockTaskController().updateLockTaskFeatures(userId, flags);
+        }
+    }
+
+    @Override
+    public void setShowWhenLocked(IBinder token, boolean showWhenLocked) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return;
+            }
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                r.setShowWhenLocked(showWhenLocked);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public void setTurnScreenOn(IBinder token, boolean turnScreenOn) {
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return;
+            }
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                r.setTurnScreenOn(turnScreenOn);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public void registerRemoteAnimations(IBinder token, RemoteAnimationDefinition definition) {
+        mAm.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+                "registerRemoteAnimations");
+        definition.setCallingPid(Binder.getCallingPid());
+        synchronized (mGlobalLock) {
+            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r == null) {
+                return;
+            }
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                r.registerRemoteAnimations(definition);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    @Override
+    public void registerRemoteAnimationForNextActivityStart(String packageName,
+            RemoteAnimationAdapter adapter) {
+        mAm.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+                "registerRemoteAnimationForNextActivityStart");
+        adapter.setCallingPid(Binder.getCallingPid());
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                mAm.getActivityStartController().registerRemoteAnimationForNextActivityStart(
+                        packageName, adapter);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
+
+    /** @see android.app.ActivityManager#alwaysShowUnsupportedCompileSdkWarning */
+    @Override
+    public void alwaysShowUnsupportedCompileSdkWarning(ComponentName activity) {
+        synchronized (mGlobalLock) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                mAm.mAppWarnings.alwaysShowUnsupportedCompileSdkWarning(activity);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
 }