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/api/current.txt b/api/current.txt
index e0d895e..28f7e15 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -36786,6 +36786,7 @@
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 a5f3081..1039ef5 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -39904,6 +39904,7 @@
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 a983cf5..129eefa 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -36921,6 +36921,7 @@
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 5b05d58..efe72c3 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -616,11 +616,14 @@
/** 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 @@
* 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 @@
* 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 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 @@
}
/**
+ * 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 @@
* 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 @@
* @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 @@
* 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 @@
/** 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 c842f78..585bd05 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -389,6 +389,8 @@
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 ca736e3..a2a129d 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -1261,6 +1261,36 @@
}
}
+
+
+ /**
+ * <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 033dd13..ff75a8b 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -35,6 +35,7 @@
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 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);
}
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 b99b8fe..d5e6b6d 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.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 @@
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 8a94b83..e5e3512 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.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 @@
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 @@
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 @@
: 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 @@
}
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 @@
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 e8976a7..03a7db7 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 @@
}
@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 4357dca..0c5e4bd 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 @@
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 @@
}
}
+ 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) {