diff options
18 files changed, 377 insertions, 86 deletions
diff --git a/api/current.txt b/api/current.txt index e0d895ed85cc..28f7e15fc6ce 100644 --- a/api/current.txt +++ b/api/current.txt @@ -36786,6 +36786,7 @@ package android.service.voice { method public void setTheme(int); method public void setUiEnabled(boolean); method public void show(android.os.Bundle, int); + method public void startAssistantActivity(android.content.Intent); method public void startVoiceActivity(android.content.Intent); field public static final int SHOW_SOURCE_ACTIVITY = 16; // 0x10 field public static final int SHOW_SOURCE_APPLICATION = 8; // 0x8 diff --git a/api/system-current.txt b/api/system-current.txt index a5f30819e353..1039ef57659a 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -39904,6 +39904,7 @@ package android.service.voice { method public void setTheme(int); method public void setUiEnabled(boolean); method public void show(android.os.Bundle, int); + method public void startAssistantActivity(android.content.Intent); method public void startVoiceActivity(android.content.Intent); field public static final int SHOW_SOURCE_ACTIVITY = 16; // 0x10 field public static final int SHOW_SOURCE_APPLICATION = 8; // 0x8 diff --git a/api/test-current.txt b/api/test-current.txt index a983cf540b64..129eefa56d7d 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -36921,6 +36921,7 @@ package android.service.voice { method public void setTheme(int); method public void setUiEnabled(boolean); method public void show(android.os.Bundle, int); + method public void startAssistantActivity(android.content.Intent); method public void startVoiceActivity(android.content.Intent); field public static final int SHOW_SOURCE_ACTIVITY = 16; // 0x10 field public static final int SHOW_SOURCE_APPLICATION = 8; // 0x8 diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 5b05d581c4e0..efe72c306883 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -616,11 +616,14 @@ public class ActivityManager { /** ID of stack that always on top (always visible) when it exist. */ public static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1; - /** Recents activity stack ID. */ + /** ID of stack that contains the Recents activity. */ public static final int RECENTS_STACK_ID = PINNED_STACK_ID + 1; + /** ID of stack that contains activities launched by the assistant. */ + public static final int ASSISTANT_STACK_ID = RECENTS_STACK_ID + 1; + /** Last static stack stack ID. */ - public static final int LAST_STATIC_STACK_ID = RECENTS_STACK_ID; + public static final int LAST_STATIC_STACK_ID = ASSISTANT_STACK_ID; /** Start of ID range used by stacks that are created dynamically. */ public static final int FIRST_DYNAMIC_STACK_ID = LAST_STATIC_STACK_ID + 1; @@ -665,7 +668,7 @@ public class ActivityManager { * Returns true if dynamic stacks are allowed to be visible behind the input stack. */ public static boolean isDynamicStacksVisibleBehindAllowed(int stackId) { - return stackId == PINNED_STACK_ID; + return stackId == PINNED_STACK_ID || stackId == ASSISTANT_STACK_ID; } /** @@ -681,8 +684,8 @@ public class ActivityManager { * Returns true if Stack size is affected by the docked stack changing size. */ public static boolean isResizeableByDockedStack(int stackId) { - return isStaticStack(stackId) && - stackId != DOCKED_STACK_ID && stackId != PINNED_STACK_ID; + return isStaticStack(stackId) && stackId != DOCKED_STACK_ID + && stackId != PINNED_STACK_ID && stackId != ASSISTANT_STACK_ID; } /** @@ -691,14 +694,16 @@ public class ActivityManager { */ public static boolean isTaskResizeableByDockedStack(int stackId) { return isStaticStack(stackId) && stackId != FREEFORM_WORKSPACE_STACK_ID - && stackId != DOCKED_STACK_ID && stackId != PINNED_STACK_ID; + && stackId != DOCKED_STACK_ID && stackId != PINNED_STACK_ID + && stackId != ASSISTANT_STACK_ID; } /** * Returns true if the input stack is affected by drag resizing. */ public static boolean isStackAffectedByDragResizing(int stackId) { - return isStaticStack(stackId) && stackId != PINNED_STACK_ID; + return isStaticStack(stackId) && stackId != PINNED_STACK_ID + && stackId != ASSISTANT_STACK_ID; } /** @@ -722,19 +727,31 @@ public class ActivityManager { } /** + * Return whether a stackId is a stack that be a backdrop to a translucent activity. These + * are generally fullscreen stacks. + */ + public static boolean isBackdropToTranslucentActivity(int stackId) { + return stackId == FULLSCREEN_WORKSPACE_STACK_ID + || stackId == ASSISTANT_STACK_ID; + } + + /** * Returns true if animation specs should be constructed for app transition that moves * the task to the specified stack. */ public static boolean useAnimationSpecForAppTransition(int stackId) { - // TODO: INVALID_STACK_ID is also animated because we don't persist stack id's across // reboots. return stackId == FREEFORM_WORKSPACE_STACK_ID - || stackId == FULLSCREEN_WORKSPACE_STACK_ID || stackId == DOCKED_STACK_ID + || stackId == FULLSCREEN_WORKSPACE_STACK_ID + || stackId == ASSISTANT_STACK_ID + || stackId == DOCKED_STACK_ID || stackId == INVALID_STACK_ID; } - /** Returns true if the windows in the stack can receive input keys. */ + /** + * Returns true if the windows in the stack can receive input keys. + */ public static boolean canReceiveKeys(int stackId) { return stackId != PINNED_STACK_ID; } @@ -743,7 +760,17 @@ public class ActivityManager { * Returns true if the stack can be visible above lockscreen. */ public static boolean isAllowedOverLockscreen(int stackId) { - return stackId == HOME_STACK_ID || stackId == FULLSCREEN_WORKSPACE_STACK_ID; + return stackId == HOME_STACK_ID || stackId == FULLSCREEN_WORKSPACE_STACK_ID || + stackId == ASSISTANT_STACK_ID; + } + + /** + * Returns true if activities from stasks in the given {@param stackId} are allowed to + * enter picture-in-picture. + */ + public static boolean isAllowedToEnterPictureInPicture(int stackId) { + return stackId != HOME_STACK_ID && stackId != ASSISTANT_STACK_ID && + stackId != RECENTS_STACK_ID; } public static boolean isAlwaysOnTop(int stackId) { @@ -799,8 +826,8 @@ public class ActivityManager { * @see android.app.ActivityManager#supportsMultiWindow */ public static boolean isMultiWindowStack(int stackId) { - return isStaticStack(stackId) || stackId == PINNED_STACK_ID - || stackId == FREEFORM_WORKSPACE_STACK_ID || stackId == DOCKED_STACK_ID; + return stackId == PINNED_STACK_ID || stackId == FREEFORM_WORKSPACE_STACK_ID + || stackId == DOCKED_STACK_ID; } /** @@ -815,20 +842,20 @@ public class ActivityManager { * calling {@link Activity#requestVisibleBehind}. */ public static boolean activitiesCanRequestVisibleBehind(int stackId) { - return stackId == FULLSCREEN_WORKSPACE_STACK_ID; + return stackId == FULLSCREEN_WORKSPACE_STACK_ID || + stackId == ASSISTANT_STACK_ID; } /** - * Returns true if this stack may be scaled without resizing, - * and windows within may need to be configured as such. + * Returns true if this stack may be scaled without resizing, and windows within may need + * to be configured as such. */ public static boolean windowsAreScaleable(int stackId) { return stackId == PINNED_STACK_ID; } /** - * Returns true if windows in this stack should be given move animations - * by default. + * Returns true if windows in this stack should be given move animations by default. */ public static boolean hasMovementAnimations(int stackId) { return stackId != PINNED_STACK_ID; @@ -836,8 +863,11 @@ public class ActivityManager { /** Returns true if the input stack and its content can affect the device orientation. */ public static boolean canSpecifyOrientation(int stackId) { - return stackId == HOME_STACK_ID || stackId == RECENTS_STACK_ID - || stackId == FULLSCREEN_WORKSPACE_STACK_ID || isDynamicStack(stackId); + return stackId == HOME_STACK_ID + || stackId == RECENTS_STACK_ID + || stackId == FULLSCREEN_WORKSPACE_STACK_ID + || stackId == ASSISTANT_STACK_ID + || isDynamicStack(stackId); } } diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index c842f788feda..585bd053ad81 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -389,6 +389,8 @@ interface IActivityManager { in Intent intent, in String resolvedType, in IVoiceInteractionSession session, in IVoiceInteractor interactor, int flags, in ProfilerInfo profilerInfo, in Bundle options, int userId); + int startAssistantActivity(in String callingPackage, int callingPid, int callingUid, + in Intent intent, in String resolvedType, in Bundle options, int userId); Bundle getActivityOptions(in IBinder token); List<IBinder> getAppTasks(in String callingPackage); void startSystemLockTaskMode(int taskId); diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java index ca736e346761..a2a129d2bbe0 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -1261,6 +1261,36 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall } } + + + /** + * <p>Ask that a new assistant activity be started. This will create a new task in the + * in activity manager: this means that + * {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK} + * will be set for you to make it a new task.</p> + * + * <p>The newly started activity will be displayed on top of other activities in the system + * in a new layer that is not affected by multi-window mode. Tasks started from this activity + * will go into the normal activity layer and not this new layer.</p> + * + * <p>By default, the system will create a window for the UI for this session. If you are using + * an assistant activity instead, then you can disable the window creation by calling + * {@link #setUiEnabled} in {@link #onPrepareShow(Bundle, int)}.</p> + */ + public void startAssistantActivity(Intent intent) { + if (mToken == null) { + throw new IllegalStateException("Can't call before onCreate()"); + } + try { + intent.migrateExtraStreamToClipData(); + intent.prepareToLeaveProcess(mContext); + int res = mSystemService.startAssistantActivity(mToken, intent, + intent.resolveType(mContext.getContentResolver())); + Instrumentation.checkStartActivityResult(res, intent); + } catch (RemoteException e) { + } + } + /** * Set whether this session will keep the device awake while it is running a voice * activity. By default, the system holds a wake lock for it while in this state, diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl index 033dd13046ea..ff75a8b5fea4 100644 --- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl +++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl @@ -35,6 +35,7 @@ interface IVoiceInteractionManagerService { boolean showSessionFromSession(IBinder token, in Bundle sessionArgs, int flags); boolean hideSessionFromSession(IBinder token); int startVoiceActivity(IBinder token, in Intent intent, String resolvedType); + int startAssistantActivity(IBinder token, in Intent intent, String resolvedType); void setKeepAwake(IBinder token, boolean keepAwake); void closeSystemDialogs(IBinder token); void finish(IBinder token); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 3b9426d22007..a59ee74d8447 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_RESIZABLE_ACTIV 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.StorageManager; 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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 @@ public class ActivityManagerService extends IActivityManager.Stub } @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 @@ public class ActivityManagerService extends IActivityManager.Stub + ": 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 65b8554ac0c1..ff796a549f37 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 @@ class ActivityMetricsLogger { 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 @@ class ActivityMetricsLogger { // 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 @@ class ActivityMetricsLogger { 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 58c17ebbeafe..1b193825c41d 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 @@ package com.android.server.am; 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 @@ final class ActivityRecord implements AppWindowContainerListener { 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 @@ final class ActivityRecord implements AppWindowContainerListener { 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 @@ final class ActivityRecord implements AppWindowContainerListener { 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 @@ final class ActivityRecord implements AppWindowContainerListener { } } 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 @@ final class ActivityRecord implements AppWindowContainerListener { return mActivityType == RECENTS_ACTIVITY_TYPE; } + boolean isAssistantActivity() { + return mActivityType == ASSISTANT_ACTIVITY_TYPE; + } + boolean isApplicationActivity() { return mActivityType == APPLICATION_ACTIVITY_TYPE; } @@ -2317,6 +2342,7 @@ final class ActivityRecord implements AppWindowContainerListener { 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 cf0ebaf6964e..7c2460421e35 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.POSTFIX_VISIBILIT 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 @@ final class ActivityStack extends ConfigurationContainer implements StackWindowL 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 class ActivityStack extends ConfigurationContainer implements StackWindowL 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 class ActivityStack extends ConfigurationContainer implements StackWindowL 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 @@ final class ActivityStack extends ConfigurationContainer implements StackWindowL 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 @@ final class ActivityStack extends ConfigurationContainer implements StackWindowL 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 @@ final class ActivityStack extends ConfigurationContainer implements StackWindowL } } + // 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 @@ final class ActivityStack extends ConfigurationContainer implements StackWindowL 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 @@ final class ActivityStack extends ConfigurationContainer implements StackWindowL 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 class ActivityStack extends ConfigurationContainer implements StackWindowL 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 d1606b4dc13b..4b07af0d00dc 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_RETURN_LOCK_TASK_MODE_VIOLATION; 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_AM; 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 @@ class ActivityStarter { 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 @@ class ActivityStarter { // 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 @@ class ActivityStarter { 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 @@ class ActivityStarter { 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 @@ class ActivityStarter { 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 @@ class ActivityStarter { 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 @@ class ActivityStarter { 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 80ed8333fdc4..520d4ee87592 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.ArrayList; 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.POSTFIX_TASKS; 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 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta ? 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 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta return taskType == RECENTS_ACTIVITY_TYPE; } + boolean isAssistantTask() { + return taskType == ASSISTANT_ACTIVITY_TYPE; + } + boolean isApplicationTask() { return taskType == APPLICATION_ACTIVITY_TYPE; } @@ -1296,6 +1310,10 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta 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 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta 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 class TaskRecord extends ConfigurationContainer implements TaskWindowConta 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 d56110c67c4b..1cd2b53de9ba 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 android.view.Display; 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 @@ class WindowLayersController { 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 @@ class WindowLayersController { mPinnedWindows.clear(); mInputMethodWindows.clear(); mDockedWindows.clear(); + mAssistantWindows.clear(); mReplacingWindows.clear(); mDockDivider = null; @@ -188,6 +191,8 @@ class WindowLayersController { 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 @@ class WindowLayersController { 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); } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java index b99b8fe6f77b..d5e6b6dcfbc6 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowLayersControllerTests.java @@ -23,6 +23,9 @@ import android.platform.test.annotations.Presubmit; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; +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.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; @@ -155,6 +158,22 @@ public class WindowLayersControllerTests extends WindowTestsBase { assertWindowLayerGreaterThan(sImeDialogWindow, sImeWindow); } + @Test + public void testStackLayers() throws Exception { + WindowState pinnedStackWindow = createWindowOnStack(null, PINNED_STACK_ID, + TYPE_BASE_APPLICATION, sDisplayContent, "pinnedStackWindow"); + WindowState dockedStackWindow = createWindowOnStack(null, DOCKED_STACK_ID, + TYPE_BASE_APPLICATION, sDisplayContent, "dockedStackWindow"); + WindowState assistantStackWindow = createWindowOnStack(null, ASSISTANT_STACK_ID, + TYPE_BASE_APPLICATION, sDisplayContent, "assistantStackWindow"); + + sLayersController.assignWindowLayers(sDisplayContent); + + assertWindowLayerGreaterThan(dockedStackWindow, sAppWindow); + assertWindowLayerGreaterThan(assistantStackWindow, dockedStackWindow); + assertWindowLayerGreaterThan(pinnedStackWindow, assistantStackWindow); + } + private void assertWindowLayerGreaterThan(WindowState first, WindowState second) throws Exception { assertGreaterThan(first.mWinAnimator.mAnimLayer, second.mWinAnimator.mAnimLayer); diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java index 8a94b83eb78d..e5e351226306 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java @@ -37,6 +37,7 @@ import android.view.IWindow; import android.view.WindowManager; import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID; +import static android.app.ActivityManager.StackId.INVALID_STACK_ID; import static android.app.AppOpsManager.OP_NONE; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -145,6 +146,21 @@ class WindowTestsBase { return win; } + /** + * Creates a window for a task on a the given {@param stackId}. + */ + private WindowState createStackWindow(int stackId, String name) { + final StackWindowController stackController = createStackControllerOnStackOnDisplay(stackId, + sDisplayContent); + final TestTaskWindowContainerController taskController = + new TestTaskWindowContainerController(stackController); + TestAppWindowToken appWinToken = new TestAppWindowToken(sDisplayContent); + appWinToken.mTask = taskController.mContainer; + final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, name); + win.mAppToken = appWinToken; + return win; + } + /** Asserts that the first entry is greater than the second entry. */ void assertGreaterThan(int first, int second) throws Exception { Assert.assertTrue("Excepted " + first + " to be greater than " + second, first > second); @@ -157,12 +173,14 @@ class WindowTestsBase { sWm.mH.runWithScissors(() -> { }, 0); } - private static WindowToken createWindowToken(DisplayContent dc, int type) { + private static WindowToken createWindowToken(DisplayContent dc, int stackId, int type) { if (type < FIRST_APPLICATION_WINDOW || type > LAST_APPLICATION_WINDOW) { return new TestWindowToken(type, dc); } - final TaskStack stack = createTaskStackOnDisplay(dc); + final TaskStack stack = stackId == INVALID_STACK_ID + ? createTaskStackOnDisplay(dc) + : createStackControllerOnStackOnDisplay(stackId, dc).mContainer; final Task task = createTaskInStack(stack, 0 /* userId */); final TestAppWindowToken token = new TestAppWindowToken(dc); task.addChild(token, 0); @@ -175,6 +193,12 @@ class WindowTestsBase { : createWindow(parent, type, parent.mToken, name); } + static WindowState createWindowOnStack(WindowState parent, int stackId, int type, + DisplayContent dc, String name) { + final WindowToken token = createWindowToken(dc, stackId, type); + return createWindow(parent, type, token, name); + } + WindowState createAppWindow(Task task, int type, String name) { final AppWindowToken token = new TestAppWindowToken(sDisplayContent); task.addChild(token, 0); @@ -182,13 +206,13 @@ class WindowTestsBase { } static WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name) { - final WindowToken token = createWindowToken(dc, type); + final WindowToken token = createWindowToken(dc, INVALID_STACK_ID, type); return createWindow(parent, type, token, name); } static WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name, boolean ownerCanAddInternalSystemWindow) { - final WindowToken token = createWindowToken(dc, type); + final WindowToken token = createWindowToken(dc, INVALID_STACK_ID, type); return createWindow(parent, type, token, name, ownerCanAddInternalSystemWindow); } @@ -216,6 +240,11 @@ class WindowTestsBase { static StackWindowController createStackControllerOnDisplay(DisplayContent dc) { final int stackId = ++sNextStackId; + return createStackControllerOnStackOnDisplay(stackId, dc); + } + + static StackWindowController createStackControllerOnStackOnDisplay(int stackId, + DisplayContent dc) { return new StackWindowController(stackId, null, dc.getDisplayId(), true /* onTop */, new Rect(), sWm); } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index e8976a7cad6d..03a7db7286b6 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -638,6 +638,25 @@ public class VoiceInteractionManagerService extends SystemService { } @Override + public int startAssistantActivity(IBinder token, Intent intent, String resolvedType) { + synchronized (this) { + if (mImpl == null) { + Slog.w(TAG, "startAssistantActivity without running voice interaction service"); + return ActivityManager.START_CANCELED; + } + final int callingPid = Binder.getCallingPid(); + final int callingUid = Binder.getCallingUid(); + final long caller = Binder.clearCallingIdentity(); + try { + return mImpl.startAssistantActivityLocked(callingPid, callingUid, token, + intent, resolvedType); + } finally { + Binder.restoreCallingIdentity(caller); + } + } + } + + @Override public void setKeepAwake(IBinder token, boolean keepAwake) { synchronized (this) { if (mImpl == null) { diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java index 4357dca73fbf..0c5e4bdca08d 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java @@ -16,8 +16,13 @@ package com.android.server.voiceinteraction; +import static android.app.ActivityManager.START_VOICE_HIDDEN_SESSION; +import static android.app.ActivityManager.START_VOICE_NOT_ACTIVE_SESSION; + import android.app.ActivityManager; +import android.app.ActivityManager.StackId; import android.app.ActivityManagerInternal; +import android.app.ActivityOptions; import android.app.IActivityManager; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -185,11 +190,11 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne try { if (mActiveSession == null || token != mActiveSession.mToken) { Slog.w(TAG, "startVoiceActivity does not match active session"); - return ActivityManager.START_VOICE_NOT_ACTIVE_SESSION; + return START_VOICE_NOT_ACTIVE_SESSION; } if (!mActiveSession.mShown) { Slog.w(TAG, "startVoiceActivity not allowed on hidden session"); - return ActivityManager.START_VOICE_HIDDEN_SESSION; + return START_VOICE_HIDDEN_SESSION; } intent = new Intent(intent); intent.addCategory(Intent.CATEGORY_VOICE); @@ -202,6 +207,28 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne } } + public int startAssistantActivityLocked(int callingPid, int callingUid, IBinder token, + Intent intent, String resolvedType) { + try { + if (mActiveSession == null || token != mActiveSession.mToken) { + Slog.w(TAG, "startAssistantActivity does not match active session"); + return START_VOICE_NOT_ACTIVE_SESSION; + } + if (!mActiveSession.mShown) { + Slog.w(TAG, "startAssistantActivity not allowed on hidden session"); + return START_VOICE_HIDDEN_SESSION; + } + intent = new Intent(intent); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchStackId(StackId.ASSISTANT_STACK_ID); + return mAm.startAssistantActivity(mComponent.getPackageName(), callingPid, callingUid, + intent, resolvedType, options.toBundle(), mUser); + } catch (RemoteException e) { + throw new IllegalStateException("Unexpected remote error", e); + } + } + public void setKeepAwakeLocked(IBinder token, boolean keepAwake) { try { if (mActiveSession == null || token != mActiveSession.mToken) { |