Create a new stack for the assistant activity.

- Add a new stack that is not resized with multiwindow, and
  appears above the fullscreen and docked stacks, but below
  the pinned stack
- Add a method on VoiceInteractionSession to allow the assistant
  to launch activities into this new fullscreen stack.
- Also prevent any activities in the assist stack from the
  fetching of the on screen assist data.

Bug: 30999386
Test: android.server.cts.ActivityManagerAssistantStackTests

Change-Id: I22ab7629b5f758cf1e66d7d1c26648af6bc887c9
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3b9426d..a59ee74 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -54,6 +54,7 @@
 import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL;
 import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
 import static android.provider.Settings.System.FONT_SCALE;
+import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_APPLICATION;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static com.android.internal.util.XmlUtils.readBooleanAttribute;
 import static com.android.internal.util.XmlUtils.readIntAttribute;
@@ -283,7 +284,6 @@
 import android.os.storage.StorageManagerInternal;
 import android.provider.Downloads;
 import android.provider.Settings;
-import android.service.autofill.AutoFillService;
 import android.service.voice.IVoiceInteractionSession;
 import android.service.voice.VoiceInteractionManagerInternal;
 import android.service.voice.VoiceInteractionSession;
@@ -556,10 +556,10 @@
 
     // Determines whether to take full screen screenshots
     static final boolean TAKE_FULLSCREEN_SCREENSHOTS = true;
-    public static final float FULLSCREEN_SCREENSHOT_SCALE = 0.6f;
 
     /** All system services */
     SystemServiceManager mSystemServiceManager;
+    AssistUtils mAssistUtils;
 
     private Installer mInstaller;
 
@@ -4518,6 +4518,25 @@
     }
 
     @Override
+    public int startAssistantActivity(String callingPackage, int callingPid, int callingUid,
+            Intent intent, String resolvedType, Bundle bOptions, int userId) {
+        if (checkCallingPermission(Manifest.permission.BIND_VOICE_INTERACTION)
+                != PackageManager.PERMISSION_GRANTED) {
+            final String msg = "Permission Denial: startAssistantActivity() from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid()
+                    + " requires " + Manifest.permission.BIND_VOICE_INTERACTION;
+            Slog.w(TAG, msg);
+            throw new SecurityException(msg);
+        }
+        userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, false,
+                ALLOW_FULL_ONLY, "startAssistantActivity", null);
+        return mActivityStarter.startActivityMayWait(null, callingUid, callingPackage, intent,
+                resolvedType, null, null, null, null, 0, 0, null, null, null, bOptions, false,
+                userId, null, null);
+    }
+
+    @Override
     public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options)
             throws RemoteException {
         Slog.i(TAG, "Activity tried to startVoiceInteraction");
@@ -7767,9 +7786,9 @@
                     + ": Current activity does not support picture-in-picture.");
         }
 
-        if (r.getStack().isHomeStack()) {
+        if (!StackId.isAllowedToEnterPictureInPicture(r.getStack().getStackId())) {
             throw new IllegalStateException(caller
-                    + ": Activities on the home stack not supported");
+                    + ": Activities on the home, assistant, or recents stack not supported");
         }
 
         if (args.hasSetAspectRatio()
@@ -9971,8 +9990,8 @@
                 return;
             }
             final ActivityRecord prev = mStackSupervisor.topRunningActivityLocked();
-            if (prev != null && prev.isRecentsActivity()) {
-                task.setTaskToReturnTo(ActivityRecord.RECENTS_ACTIVITY_TYPE);
+            if (prev != null) {
+                task.setTaskToReturnTo(prev);
             }
             mStackSupervisor.findTaskToMoveToFrontLocked(task, flags, options, "moveTaskToFront",
                     false /* forceNonResizable */);
@@ -12479,8 +12498,12 @@
     public boolean isAssistDataAllowedOnCurrentActivity() {
         int userId;
         synchronized (this) {
-            userId = mUserController.getCurrentUserIdLocked();
-            ActivityRecord activity = getFocusedStack().topActivity();
+            final ActivityStack focusedStack = getFocusedStack();
+            if (focusedStack == null || focusedStack.isAssistantStack()) {
+                return false;
+            }
+
+            final ActivityRecord activity = focusedStack.topActivity();
             if (activity == null) {
                 return false;
             }
@@ -12509,9 +12532,8 @@
                     return false;
                 }
             }
-            AssistUtils utils = new AssistUtils(mContext);
-            return utils.showSessionForActiveService(args,
-                    VoiceInteractionSession.SHOW_SOURCE_APPLICATION, null, token);
+            return mAssistUtils.showSessionForActiveService(args, SHOW_SOURCE_APPLICATION, null,
+                    token);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -13493,6 +13515,7 @@
 
             mLocalDeviceIdleController
                     = LocalServices.getService(DeviceIdleController.LocalService.class);
+            mAssistUtils = new AssistUtils(mContext);
 
             // Make sure we have the current profile info, since it is needed for security checks.
             mUserController.onSystemReady();
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index 65b8554..ff796a54 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -1,5 +1,6 @@
 package com.android.server.am;
 
+import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
@@ -33,6 +34,7 @@
     private static final int WINDOW_STATE_STANDARD = 0;
     private static final int WINDOW_STATE_SIDE_BY_SIDE = 1;
     private static final int WINDOW_STATE_FREEFORM = 2;
+    private static final int WINDOW_STATE_ASSISTANT = 3;
     private static final int WINDOW_STATE_INVALID = -1;
 
     private static final long INVALID_START_TIME = -1;
@@ -40,7 +42,7 @@
     // Preallocated strings we are sending to tron, so we don't have to allocate a new one every
     // time we log.
     private static final String[] TRON_WINDOW_STATE_VARZ_STRINGS = {
-            "window_time_0", "window_time_1", "window_time_2"};
+            "window_time_0", "window_time_1", "window_time_2", "window_time_3"};
 
     private int mWindowState = WINDOW_STATE_STANDARD;
     private long mLastLogTimeSecs;
@@ -88,6 +90,8 @@
             mWindowState = WINDOW_STATE_INVALID;
         } else if (stack.mStackId == FREEFORM_WORKSPACE_STACK_ID) {
             mWindowState = WINDOW_STATE_FREEFORM;
+        } else if (stack.mStackId == ASSISTANT_STACK_ID) {
+            mWindowState = WINDOW_STATE_ASSISTANT;
         } else if (StackId.isStaticStack(stack.mStackId)) {
             throw new IllegalStateException("Unknown stack=" + stack);
         }
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 58c17eb..1b19382 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -19,8 +19,10 @@
 import static android.app.ActivityManager.ENABLE_TASK_SNAPSHOTS;
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.app.ActivityManager.StackId;
+import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.app.AppOpsManager.MODE_ALLOWED;
@@ -178,6 +180,7 @@
     static final int APPLICATION_ACTIVITY_TYPE = 0;
     static final int HOME_ACTIVITY_TYPE = 1;
     static final int RECENTS_ACTIVITY_TYPE = 2;
+    static final int ASSISTANT_ACTIVITY_TYPE = 3;
     int mActivityType;
 
     private CharSequence nonLocalizedLabel;  // the label information from the package mgr.
@@ -721,7 +724,7 @@
         noDisplay = ent != null && ent.array.getBoolean(
                 com.android.internal.R.styleable.Window_windowNoDisplay, false);
 
-        setActivityType(_componentSpecified, _launchedFromUid, _intent, sourceRecord);
+        setActivityType(_componentSpecified, _launchedFromUid, _intent, options, sourceRecord);
 
         immersive = (aInfo.flags & FLAG_IMMERSIVE) != 0;
 
@@ -812,8 +815,23 @@
         return sourceRecord != null && sourceRecord.isResolverActivity();
     }
 
-    private void setActivityType(boolean componentSpecified,
-            int launchedFromUid, Intent intent, ActivityRecord sourceRecord) {
+    /**
+     * @return whether the given package name can launch an assist activity.
+     */
+    private boolean canLaunchAssistActivity(String packageName) {
+        if (service.mAssistUtils == null) {
+            return false;
+        }
+
+        final ComponentName assistComponent = service.mAssistUtils.getActiveServiceComponentName();
+        if (assistComponent != null) {
+            return assistComponent.getPackageName().equals(packageName);
+        }
+        return false;
+    }
+
+    private void setActivityType(boolean componentSpecified, int launchedFromUid, Intent intent,
+            ActivityOptions options, ActivityRecord sourceRecord) {
         if ((!componentSpecified || canLaunchHomeActivity(launchedFromUid, sourceRecord))
                 && isHomeIntent(intent) && !isResolverActivity()) {
             // This sure looks like a home activity!
@@ -826,6 +844,9 @@
             }
         } else if (realActivity.getClassName().contains(RECENTS_PACKAGE_NAME)) {
             mActivityType = RECENTS_ACTIVITY_TYPE;
+        } else if (options != null && options.getLaunchStackId() == ASSISTANT_STACK_ID
+                && canLaunchAssistActivity(launchedFromPackage)) {
+            mActivityType = ASSISTANT_ACTIVITY_TYPE;
         } else {
             mActivityType = APPLICATION_ACTIVITY_TYPE;
         }
@@ -883,6 +904,10 @@
         return mActivityType == RECENTS_ACTIVITY_TYPE;
     }
 
+    boolean isAssistantActivity() {
+        return mActivityType == ASSISTANT_ACTIVITY_TYPE;
+    }
+
     boolean isApplicationActivity() {
         return mActivityType == APPLICATION_ACTIVITY_TYPE;
     }
@@ -2317,6 +2342,7 @@
             case APPLICATION_ACTIVITY_TYPE: return "APPLICATION_ACTIVITY_TYPE";
             case HOME_ACTIVITY_TYPE: return "HOME_ACTIVITY_TYPE";
             case RECENTS_ACTIVITY_TYPE: return "RECENTS_ACTIVITY_TYPE";
+            case ASSISTANT_ACTIVITY_TYPE: return "ASSISTANT_ACTIVITY_TYPE";
             default: return Integer.toString(type);
         }
     }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index cf0ebaf..7c24604 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
@@ -62,6 +63,7 @@
 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.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.ASSISTANT_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityStackSupervisor.FindTaskResult;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
@@ -778,6 +780,10 @@
         return mStackId == PINNED_STACK_ID;
     }
 
+    final boolean isAssistantStack() {
+        return mStackId == ASSISTANT_STACK_ID;
+    }
+
     final boolean isOnHomeDisplay() {
         return isAttached() &&
                 mActivityContainer.mActivityDisplay.mDisplayId == DEFAULT_DISPLAY;
@@ -1556,11 +1562,11 @@
         final ActivityStack focusedStack = mStackSupervisor.getFocusedStack();
         final int focusedStackId = focusedStack.mStackId;
 
-        if (mStackId == FULLSCREEN_WORKSPACE_STACK_ID
+        if (StackId.isBackdropToTranslucentActivity(mStackId)
                 && hasVisibleBehindActivity() && StackId.isHomeOrRecentsStack(focusedStackId)
                 && !focusedStack.topActivity().fullscreen) {
-            // The fullscreen stack should be visible if it has a visible behind activity behind
-            // the home or recents stack that is translucent.
+            // The fullscreen or assistant stack should be visible if it has a visible behind
+            // activity behind the home or recents stack that is translucent.
             return STACK_VISIBLE_ACTIVITY_BEHIND;
         }
 
@@ -1589,10 +1595,10 @@
         final int stackBehindFocusedId = (stackBehindFocusedIndex >= 0)
                 ? mStacks.get(stackBehindFocusedIndex).mStackId : INVALID_STACK_ID;
 
-        if (focusedStackId == FULLSCREEN_WORKSPACE_STACK_ID
+        if (StackId.isBackdropToTranslucentActivity(focusedStackId)
                 && focusedStack.isStackTranslucent(starting, stackBehindFocusedId)) {
-            // Stacks behind the fullscreen stack with a translucent activity are always
-            // visible so they can act as a backdrop to the translucent activity.
+            // Stacks behind the fullscreen or assistant stack with a translucent activity are
+            // always visible so they can act as a backdrop to the translucent activity.
             // For example, dialog activities
             if (stackIndex == stackBehindFocusedIndex) {
                 return STACK_VISIBLE;
@@ -2544,8 +2550,7 @@
 
     private boolean resumeTopActivityInNextFocusableStack(ActivityRecord prev,
             ActivityOptions options, String reason) {
-        if ((!mFullscreen || !isOnHomeDisplay())
-                && adjustFocusToNextFocusableStackLocked(reason)) {
+        if ((!mFullscreen || !isOnHomeDisplay()) && adjustFocusToNextFocusableStackLocked(reason)) {
             // Try to move focus to the next visible stack with a running activity if this
             // stack is not covering the entire screen or is on a secondary display (with no home
             // stack).
@@ -2630,10 +2635,14 @@
                 true /* includingParents */);
     }
 
+    /**
+     * Updates the {@param task}'s return type before it is moved to the top.
+     */
     private void updateTaskReturnToForTopInsertion(TaskRecord task) {
         boolean isLastTaskOverHome = false;
-        // If the moving task is over home stack, transfer its return type to next task
-        if (task.isOverHomeStack()) {
+        // If the moving task is over the home or assistant stack, transfer its return type to next
+        // task so that they return to the same stack
+        if (task.isOverHomeStack() || task.isOverAssistantStack()) {
             final TaskRecord nextTask = getNextTask(task);
             if (nextTask != null) {
                 nextTask.setTaskToReturnTo(task.getTaskToReturnTo());
@@ -2642,24 +2651,32 @@
             }
         }
 
+        // If this is not on the default display, then just set the return type to application
+        if (!isOnHomeDisplay()) {
+            task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
+            return;
+        }
+
+        // If the task was launched from the assistant stack, set the return type to assistant
+        final ActivityStack lastStack = mStackSupervisor.getLastStack();
+        if (lastStack != null && lastStack.isAssistantStack()) {
+            task.setTaskToReturnTo(ASSISTANT_ACTIVITY_TYPE);
+            return;
+        }
+
         // If this is being moved to the top by another activity or being launched from the home
         // activity, set mTaskToReturnTo accordingly.
-        if (isOnHomeDisplay()) {
-            ActivityStack lastStack = mStackSupervisor.getLastStack();
-            final boolean fromHomeOrRecents = lastStack.isHomeOrRecentsStack();
-            final TaskRecord topTask = lastStack.topTask();
-            if (!isHomeOrRecentsStack() && (fromHomeOrRecents || topTask() != task)) {
-                // If it's a last task over home - we default to keep its return to type not to
-                // make underlying task focused when this one will be finished.
-                int returnToType = isLastTaskOverHome
-                        ? task.getTaskToReturnTo() : APPLICATION_ACTIVITY_TYPE;
-                if (fromHomeOrRecents && StackId.allowTopTaskToReturnHome(mStackId)) {
-                    returnToType = topTask == null ? HOME_ACTIVITY_TYPE : topTask.taskType;
-                }
-                task.setTaskToReturnTo(returnToType);
+        final boolean fromHomeOrRecents = lastStack.isHomeOrRecentsStack();
+        final TaskRecord topTask = lastStack.topTask();
+        if (!isHomeOrRecentsStack() && (fromHomeOrRecents || topTask() != task)) {
+            // If it's a last task over home - we default to keep its return to type not to
+            // make underlying task focused when this one will be finished.
+            int returnToType = isLastTaskOverHome
+                    ? task.getTaskToReturnTo() : APPLICATION_ACTIVITY_TYPE;
+            if (fromHomeOrRecents && StackId.allowTopTaskToReturnHome(mStackId)) {
+                returnToType = topTask == null ? HOME_ACTIVITY_TYPE : topTask.taskType;
             }
-        } else {
-            task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
+            task.setTaskToReturnTo(returnToType);
         }
     }
 
@@ -3159,11 +3176,14 @@
                 return;
             } else {
                 final TaskRecord task = r.task;
-                if (r.frontOfTask && task == topTask() && task.isOverHomeStack()) {
-                    // For non-fullscreen stack, we want to move the focus to the next visible
-                    // stack to prevent the home screen from moving to the top and obscuring
+                final boolean isAssistantOrOverAssistant = task.getStack().isAssistantStack() ||
+                        task.isOverAssistantStack();
+                if (r.frontOfTask && task == topTask() &&
+                        (task.isOverHomeStack() || isAssistantOrOverAssistant)) {
+                    // For non-fullscreen or assistant stack, we want to move the focus to the next
+                    // visible stack to prevent the home screen from moving to the top and obscuring
                     // other visible stacks.
-                    if (!mFullscreen
+                    if ((!mFullscreen || isAssistantOrOverAssistant)
                             && adjustFocusToNextFocusableStackLocked(myReason)) {
                         return;
                     }
@@ -4358,13 +4378,21 @@
 
         if (mStackId == HOME_STACK_ID && topTask().isHomeTask()) {
             // For the case where we are moving the home task back and there is an activity visible
-            // behind it on the fullscreen stack, we want to move the focus to the visible behind
-            // activity to maintain order with what the user is seeing.
+            // behind it on the fullscreen or assistant stack, we want to move the focus to the
+            // visible behind activity to maintain order with what the user is seeing.
+            ActivityRecord visibleBehind = null;
             final ActivityStack fullscreenStack =
                     mStackSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID);
+            final ActivityStack assistantStack =
+                    mStackSupervisor.getStack(ASSISTANT_STACK_ID);
             if (fullscreenStack != null && fullscreenStack.hasVisibleBehindActivity()) {
-                final ActivityRecord visibleBehind = fullscreenStack.getVisibleBehindActivity();
-                mStackSupervisor.moveFocusableActivityStackToFrontLocked(visibleBehind, "moveTaskToBack");
+                visibleBehind = fullscreenStack.getVisibleBehindActivity();
+            } else if (assistantStack != null && assistantStack.hasVisibleBehindActivity()) {
+                visibleBehind = assistantStack.getVisibleBehindActivity();
+            }
+            if (visibleBehind != null) {
+                mStackSupervisor.moveFocusableActivityStackToFrontLocked(visibleBehind,
+                        "moveTaskToBack");
                 mStackSupervisor.resumeFocusedStackTopActivityLocked();
                 return true;
             }
@@ -4858,7 +4886,7 @@
         final int topTaskNdx = mTaskHistory.size() - 1;
         if (task.isOverHomeStack() && taskNdx < topTaskNdx) {
             final TaskRecord nextTask = mTaskHistory.get(taskNdx + 1);
-            if (!nextTask.isOverHomeStack()) {
+            if (!nextTask.isOverHomeStack() && !nextTask.isOverAssistantStack()) {
                 nextTask.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
             }
         }
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index d1606b4..4b07af0 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -25,6 +25,7 @@
 import static android.app.ActivityManager.START_SUCCESS;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
 import static android.app.ActivityManager.StackId;
+import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
@@ -69,6 +70,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityManagerService.ANIMATE;
 import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.ASSISTANT_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
@@ -1090,8 +1092,8 @@
                 mIntent, mStartActivity.getUriPermissionsLocked(), mStartActivity.userId);
         mService.grantEphemeralAccessLocked(mStartActivity.userId, mIntent,
                 mStartActivity.appInfo.uid, UserHandle.getAppId(mCallingUid));
-        if (mSourceRecord != null && mSourceRecord.isRecentsActivity()) {
-            mStartActivity.task.setTaskToReturnTo(RECENTS_ACTIVITY_TYPE);
+        if (mSourceRecord != null) {
+            mStartActivity.task.setTaskToReturnTo(mSourceRecord);
         }
         if (newTask) {
             EventLog.writeEvent(
@@ -1503,10 +1505,15 @@
             // Caller wants to appear on home activity.
             task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
             return;
-        } else if (focusedStack == null || focusedStack.mStackId == HOME_STACK_ID) {
+        } else if (focusedStack == null || focusedStack.isHomeStack()) {
             // Task will be launched over the home stack, so return home.
             task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
             return;
+        } else if (focusedStack != null && focusedStack != task.getStack() &&
+                focusedStack.isAssistantStack()) {
+            // Task was launched over the assistant stack, so return there
+            task.setTaskToReturnTo(ASSISTANT_ACTIVITY_TYPE);
+            return;
         }
 
         // Else we are coming from an application stack so return to an application.
@@ -1848,13 +1855,6 @@
     private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, Rect bounds,
             int launchFlags, ActivityOptions aOptions) {
         final TaskRecord task = r.task;
-        if (r.isRecentsActivity()) {
-            return mSupervisor.getStack(RECENTS_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
-        }
-        if (r.isHomeActivity()) {
-            return mSupervisor.mHomeStack;
-        }
-
         ActivityStack stack = getLaunchStack(r, launchFlags, task, aOptions);
         if (stack != null) {
             return stack;
@@ -1927,6 +1927,9 @@
             case FULLSCREEN_WORKSPACE_STACK_ID:
                 canUseFocusedStack = true;
                 break;
+            case ASSISTANT_STACK_ID:
+                canUseFocusedStack = r.isAssistantActivity();
+                break;
             case DOCKED_STACK_ID:
                 canUseFocusedStack = r.supportsSplitScreen();
                 break;
@@ -1946,6 +1949,18 @@
     private ActivityStack getLaunchStack(ActivityRecord r, int launchFlags, TaskRecord task,
             ActivityOptions aOptions) {
 
+        // If the activity is of a specific type, return the associated stack, creating it if
+        // necessary
+        if (r.isHomeActivity()) {
+            return mSupervisor.mHomeStack;
+        }
+        if (r.isRecentsActivity()) {
+            return mSupervisor.getStack(RECENTS_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
+        }
+        if (r.isAssistantActivity()) {
+            return mSupervisor.getStack(ASSISTANT_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
+        }
+
         // We are reusing a task, keep the stack!
         if (mReuseTask != null) {
             return mReuseTask.getStack();
@@ -1996,7 +2011,7 @@
                 return mSupervisor.mFocusedStack;
             }
 
-            if (parentStack != null && parentStack.mStackId == DOCKED_STACK_ID) {
+            if (parentStack != null && parentStack.isDockedStack()) {
                 // If parent was in docked stack, the natural place to launch another activity
                 // will be fullscreen, so it can appear alongside the docked window.
                 return mSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID, CREATE_IF_NEEDED,
@@ -2032,6 +2047,8 @@
                 return r.supportsPictureInPicture();
             case RECENTS_STACK_ID:
                 return r.isRecentsActivity();
+            case ASSISTANT_STACK_ID:
+                return r.isAssistantActivity();
             default:
                 if (StackId.isDynamicStack(stackId)) {
                     return true;
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 80ed833..520d4ee 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -66,6 +66,7 @@
 import java.util.Objects;
 
 import static android.app.ActivityManager.RESIZE_MODE_FORCED;
+import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
@@ -101,6 +102,7 @@
 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.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.ASSISTANT_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.STARTING_WINDOW_SHOWN;
@@ -719,6 +721,14 @@
                 ? HOME_ACTIVITY_TYPE : taskToReturnTo;
     }
 
+    void setTaskToReturnTo(ActivityRecord source) {
+        if (source.isRecentsActivity()) {
+            setTaskToReturnTo(RECENTS_ACTIVITY_TYPE);
+        } else if (source.isAssistantActivity()) {
+            setTaskToReturnTo(ASSISTANT_ACTIVITY_TYPE);
+        }
+    }
+
     int getTaskToReturnTo() {
         return mTaskToReturnTo;
     }
@@ -1288,6 +1298,10 @@
         return taskType == RECENTS_ACTIVITY_TYPE;
     }
 
+    boolean isAssistantTask() {
+        return taskType == ASSISTANT_ACTIVITY_TYPE;
+    }
+
     boolean isApplicationTask() {
         return taskType == APPLICATION_ACTIVITY_TYPE;
     }
@@ -1296,6 +1310,10 @@
         return mTaskToReturnTo == HOME_ACTIVITY_TYPE;
     }
 
+    boolean isOverAssistantStack() {
+        return mTaskToReturnTo == ASSISTANT_ACTIVITY_TYPE;
+    }
+
     private boolean isResizeable(boolean checkSupportsPip) {
         return (mService.mForceResizableActivities || ActivityInfo.isResizeableMode(mResizeMode)
                 || (checkSupportsPip && mSupportsPictureInPicture)) && !mTemporarilyUnresizable;
@@ -1962,6 +1980,9 @@
         if (isHomeTask()) {
             return HOME_STACK_ID;
         }
+        if (isAssistantTask()) {
+            return ASSISTANT_STACK_ID;
+        }
         if (mBounds != null) {
             return FREEFORM_WORKSPACE_STACK_ID;
         }
@@ -1982,6 +2003,7 @@
         final int stackId = mStack.mStackId;
         if (stackId == HOME_STACK_ID
                 || stackId == RECENTS_STACK_ID
+                || stackId == ASSISTANT_STACK_ID
                 || stackId == FULLSCREEN_WORKSPACE_STACK_ID
                 || (stackId == DOCKED_STACK_ID && !isResizeable())) {
             return isResizeable() ? mStack.mBounds : null;
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
index d56110c..1cd2b53d 100644
--- a/services/core/java/com/android/server/wm/WindowLayersController.java
+++ b/services/core/java/com/android/server/wm/WindowLayersController.java
@@ -22,6 +22,7 @@
 import java.util.ArrayDeque;
 import java.util.function.Consumer;
 
+import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.view.Display.DEFAULT_DISPLAY;
@@ -58,6 +59,7 @@
     private int mHighestApplicationLayer = 0;
     private ArrayDeque<WindowState> mPinnedWindows = new ArrayDeque<>();
     private ArrayDeque<WindowState> mDockedWindows = new ArrayDeque<>();
+    private ArrayDeque<WindowState> mAssistantWindows = new ArrayDeque<>();
     private ArrayDeque<WindowState> mInputMethodWindows = new ArrayDeque<>();
     private WindowState mDockDivider = null;
     private ArrayDeque<WindowState> mReplacingWindows = new ArrayDeque<>();
@@ -137,6 +139,7 @@
         mPinnedWindows.clear();
         mInputMethodWindows.clear();
         mDockedWindows.clear();
+        mAssistantWindows.clear();
         mReplacingWindows.clear();
         mDockDivider = null;
 
@@ -188,6 +191,8 @@
             mPinnedWindows.add(w);
         } else if (stack.mStackId == DOCKED_STACK_ID) {
             mDockedWindows.add(w);
+        } else if (stack.mStackId == ASSISTANT_STACK_ID) {
+            mAssistantWindows.add(w);
         }
     }
 
@@ -208,6 +213,12 @@
             layer = assignAndIncreaseLayerIfNeeded(mReplacingWindows.remove(), layer);
         }
 
+        // Adjust the assistant stack windows to be above the docked and fullscreen stack windows,
+        // but under the pinned stack windows
+        while (!mAssistantWindows.isEmpty()) {
+            layer = assignAndIncreaseLayerIfNeeded(mAssistantWindows.remove(), layer);
+        }
+
         while (!mPinnedWindows.isEmpty()) {
             layer = assignAndIncreaseLayerIfNeeded(mPinnedWindows.remove(), layer);
         }