Hook up split-launch to legacy transition system
Part 1 of hooking up stage-split to legacy stuff. This
actually routes the legacy remoteAnimation through shell
in order to gather the dividerbar and interract with the
WindowProcessController
Bug: 192279476
Test: atest WindowOrganizerTests.
use SPLIT_SELECT to launch 2 apps in split and observe.
Change-Id: I4f8802e6eb33f068fae0e4b3c12942bea76e332b
This commit is contained in:
@@ -336,4 +336,12 @@ interface IActivityTaskManager {
|
||||
* TODO(188595497): Remove this once navbar attachment is in shell.
|
||||
*/
|
||||
void detachNavigationBarFromApp(in IBinder transition);
|
||||
|
||||
/**
|
||||
* Marks a process as a delegate for the currently playing remote transition animation. This
|
||||
* must be called from a process that is already a remote transition player or delegate. Any
|
||||
* marked delegates are cleaned-up automatically at the end of the transition.
|
||||
* @param caller is the IApplicationThread representing the calling process.
|
||||
*/
|
||||
void setRunningRemoteTransitionDelegate(in IApplicationThread caller);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.view;
|
||||
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.IApplicationThread;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.os.Build;
|
||||
import android.os.Parcel;
|
||||
@@ -58,6 +59,9 @@ public class RemoteAnimationAdapter implements Parcelable {
|
||||
private int mCallingPid;
|
||||
private int mCallingUid;
|
||||
|
||||
/** @see #getCallingApplication */
|
||||
private IApplicationThread mCallingApplication;
|
||||
|
||||
/**
|
||||
* @param runner The interface that gets notified when we actually need to start the animation.
|
||||
* @param duration The duration of the animation.
|
||||
@@ -81,11 +85,19 @@ public class RemoteAnimationAdapter implements Parcelable {
|
||||
this(runner, duration, statusBarTransitionDelay, false /* changeNeedsSnapshot */);
|
||||
}
|
||||
|
||||
@UnsupportedAppUsage
|
||||
public RemoteAnimationAdapter(IRemoteAnimationRunner runner, long duration,
|
||||
long statusBarTransitionDelay, IApplicationThread callingApplication) {
|
||||
this(runner, duration, statusBarTransitionDelay, false /* changeNeedsSnapshot */);
|
||||
mCallingApplication = callingApplication;
|
||||
}
|
||||
|
||||
public RemoteAnimationAdapter(Parcel in) {
|
||||
mRunner = IRemoteAnimationRunner.Stub.asInterface(in.readStrongBinder());
|
||||
mDuration = in.readLong();
|
||||
mStatusBarTransitionDelay = in.readLong();
|
||||
mChangeNeedsSnapshot = in.readBoolean();
|
||||
mCallingApplication = IApplicationThread.Stub.asInterface(in.readStrongBinder());
|
||||
}
|
||||
|
||||
public IRemoteAnimationRunner getRunner() {
|
||||
@@ -126,6 +138,15 @@ public class RemoteAnimationAdapter implements Parcelable {
|
||||
return mCallingUid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ApplicationThread that will run the animation. Instead it is intended to pass the
|
||||
* calling information among client processes (eg. shell + launcher) through one-way binder
|
||||
* calls (where binder itself doesn't track calling information).
|
||||
*/
|
||||
public IApplicationThread getCallingApplication() {
|
||||
return mCallingApplication;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
@@ -137,6 +158,7 @@ public class RemoteAnimationAdapter implements Parcelable {
|
||||
dest.writeLong(mDuration);
|
||||
dest.writeLong(mStatusBarTransitionDelay);
|
||||
dest.writeBoolean(mChangeNeedsSnapshot);
|
||||
dest.writeStrongInterface(mCallingApplication);
|
||||
}
|
||||
|
||||
public static final @android.annotation.NonNull Creator<RemoteAnimationAdapter> CREATOR
|
||||
|
||||
@@ -20,6 +20,8 @@ import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.view.RemoteAnimationAdapter;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
import android.window.IRemoteTransition;
|
||||
|
||||
import com.android.wm.shell.splitscreen.ISplitScreenListener;
|
||||
@@ -77,9 +79,15 @@ interface ISplitScreen {
|
||||
int position, in Bundle options) = 9;
|
||||
|
||||
/**
|
||||
* Starts tasks simultaneously in one transition. The first task in the list will be in the
|
||||
* main-stage and on the left/top.
|
||||
* Starts tasks simultaneously in one transition.
|
||||
*/
|
||||
oneway void startTasks(int mainTaskId, in Bundle mainOptions, int sideTaskId,
|
||||
in Bundle sideOptions, int sidePosition, in IRemoteTransition remoteTransition) = 10;
|
||||
|
||||
/**
|
||||
* Version of startTasks using legacy transition system.
|
||||
*/
|
||||
oneway void startTasksWithLegacyTransition(int mainTaskId, in Bundle mainOptions,
|
||||
int sideTaskId, in Bundle sideOptions, int sidePosition,
|
||||
in RemoteAnimationAdapter adapter) = 11;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Slog;
|
||||
import android.view.RemoteAnimationAdapter;
|
||||
import android.window.IRemoteTransition;
|
||||
|
||||
import androidx.annotation.BinderThread;
|
||||
@@ -427,6 +428,16 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
|
||||
int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
|
||||
RemoteAnimationAdapter adapter) {
|
||||
executeRemoteCallWithTaskPermission(mController, "startTasks",
|
||||
(controller) -> controller.mStageCoordinator.startTasksWithLegacyTransition(
|
||||
mainTaskId, mainOptions, sideTaskId, sideOptions, sidePosition,
|
||||
adapter));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startTasks(int mainTaskId, @Nullable Bundle mainOptions,
|
||||
int sideTaskId, @Nullable Bundle sideOptions,
|
||||
|
||||
@@ -20,6 +20,7 @@ import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
|
||||
import static android.view.WindowManager.TRANSIT_OPEN;
|
||||
import static android.view.WindowManager.TRANSIT_TO_BACK;
|
||||
import static android.view.WindowManager.TRANSIT_TO_FRONT;
|
||||
@@ -42,12 +43,21 @@ import static com.android.wm.shell.transition.Transitions.isOpeningType;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.app.WindowConfiguration;
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.devicestate.DeviceStateManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
import android.view.IRemoteAnimationFinishedCallback;
|
||||
import android.view.IRemoteAnimationRunner;
|
||||
import android.view.RemoteAnimationAdapter;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceSession;
|
||||
import android.view.WindowManager;
|
||||
@@ -248,6 +258,75 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this);
|
||||
}
|
||||
|
||||
/** Starts 2 tasks in one legacy transition. */
|
||||
void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
|
||||
int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
|
||||
RemoteAnimationAdapter adapter) {
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
// Need to add another wrapper here in shell so that we can inject the divider bar
|
||||
// and also manage the process elevation via setRunningRemote
|
||||
IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
|
||||
@Override
|
||||
public void onAnimationStart(@WindowManager.TransitionOldType int transit,
|
||||
RemoteAnimationTarget[] apps,
|
||||
RemoteAnimationTarget[] wallpapers,
|
||||
RemoteAnimationTarget[] nonApps,
|
||||
final IRemoteAnimationFinishedCallback finishedCallback) {
|
||||
RemoteAnimationTarget[] augmentedNonApps =
|
||||
new RemoteAnimationTarget[nonApps.length + 1];
|
||||
for (int i = 0; i < nonApps.length; ++i) {
|
||||
augmentedNonApps[i] = nonApps[i];
|
||||
}
|
||||
augmentedNonApps[augmentedNonApps.length - 1] = getDividerBarLegacyTarget();
|
||||
try {
|
||||
ActivityTaskManager.getService().setRunningRemoteTransitionDelegate(
|
||||
adapter.getCallingApplication());
|
||||
adapter.getRunner().onAnimationStart(transit, apps, wallpapers, nonApps,
|
||||
finishedCallback);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Error starting remote animation", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancelled() {
|
||||
try {
|
||||
adapter.getRunner().onAnimationCancelled();
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Error starting remote animation", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(
|
||||
wrapper, adapter.getDuration(), adapter.getStatusBarTransitionDelay());
|
||||
|
||||
if (mainOptions == null) {
|
||||
mainOptions = ActivityOptions.makeRemoteAnimation(wrappedAdapter).toBundle();
|
||||
} else {
|
||||
ActivityOptions mainActivityOptions = ActivityOptions.fromBundle(mainOptions);
|
||||
mainActivityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
|
||||
}
|
||||
|
||||
sideOptions = sideOptions != null ? sideOptions : new Bundle();
|
||||
setSideStagePosition(sidePosition);
|
||||
|
||||
// Build a request WCT that will launch both apps such that task 0 is on the main stage
|
||||
// while task 1 is on the side stage.
|
||||
mMainStage.activate(getMainStageBounds(), wct);
|
||||
mSideStage.setBounds(getSideStageBounds(), wct);
|
||||
|
||||
// Make sure the launch options will put tasks in the corresponding split roots
|
||||
addActivityOptions(mainOptions, mMainStage);
|
||||
addActivityOptions(sideOptions, mSideStage);
|
||||
|
||||
// Add task launch requests
|
||||
wct.startTask(mainTaskId, mainOptions);
|
||||
wct.startTask(sideTaskId, sideOptions);
|
||||
|
||||
// Using legacy transitions, so we can't use blast sync since it conflicts.
|
||||
mTaskOrganizer.applyTransaction(wct);
|
||||
}
|
||||
|
||||
@SplitLayout.SplitPosition
|
||||
int getSideStagePosition() {
|
||||
return mSideStagePosition;
|
||||
@@ -891,6 +970,16 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
}
|
||||
}
|
||||
|
||||
private RemoteAnimationTarget getDividerBarLegacyTarget() {
|
||||
final Rect bounds = mSplitLayout.getDividerBounds();
|
||||
return new RemoteAnimationTarget(-1 /* taskId */, -1 /* mode */,
|
||||
mSplitLayout.getDividerLeash(), false /* isTranslucent */, null /* clipRect */,
|
||||
null /* contentInsets */, Integer.MAX_VALUE /* prefixOrderIndex */,
|
||||
new android.graphics.Point(0, 0) /* position */, bounds, bounds,
|
||||
new WindowConfiguration(), true, null /* startLeash */, null /* startBounds */,
|
||||
null /* taskInfo */, TYPE_DOCK_DIVIDER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(@NonNull PrintWriter pw, String prefix) {
|
||||
final String innerPrefix = prefix + " ";
|
||||
|
||||
@@ -69,7 +69,8 @@ public class RemoteAnimationAdapterCompat {
|
||||
return mRemoteTransition;
|
||||
}
|
||||
|
||||
private static IRemoteAnimationRunner.Stub wrapRemoteAnimationRunner(
|
||||
/** Wraps a RemoteAnimationRunnerCompat in an IRemoteAnimationRunner. */
|
||||
public static IRemoteAnimationRunner.Stub wrapRemoteAnimationRunner(
|
||||
final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
|
||||
return new IRemoteAnimationRunner.Stub() {
|
||||
@Override
|
||||
|
||||
@@ -5069,6 +5069,34 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
|
||||
process.registerDisplayAreaConfigurationListener(imeContainer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRunningRemoteTransitionDelegate(IApplicationThread caller) {
|
||||
mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
|
||||
"setRunningRemoteTransition");
|
||||
final int callingPid = Binder.getCallingPid();
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
synchronized (mGlobalLock) {
|
||||
// Also only allow a process which is already runningRemoteAnimation to mark another
|
||||
// process.
|
||||
final WindowProcessController callingProc = getProcessController(callingPid,
|
||||
callingUid);
|
||||
if (callingProc == null || !callingProc.isRunningRemoteTransition()) {
|
||||
final String msg = "Can't call setRunningRemoteTransition from a process (pid="
|
||||
+ callingPid + " uid=" + callingUid + ") which isn't itself running a "
|
||||
+ "remote transition.";
|
||||
Slog.e(TAG, msg);
|
||||
throw new SecurityException(msg);
|
||||
}
|
||||
final WindowProcessController wpc = getProcessController(caller);
|
||||
if (wpc == null) {
|
||||
Slog.w(TAG, "Unable to find process for application " + caller);
|
||||
return;
|
||||
}
|
||||
wpc.setRunningRemoteAnimation(true /* running */);
|
||||
callingProc.addRemoteAnimationDelegate(wpc);
|
||||
}
|
||||
}
|
||||
|
||||
final class H extends Handler {
|
||||
static final int REPORT_TIME_TRACKER_MSG = 1;
|
||||
static final int UPDATE_PROCESS_ANIMATING_STATE = 2;
|
||||
|
||||
@@ -77,6 +77,18 @@ public class SafeActivityOptions {
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance from a bundle and provided pid/uid.
|
||||
*
|
||||
* @param bOptions The {@link ActivityOptions} as {@link Bundle}.
|
||||
*/
|
||||
static SafeActivityOptions fromBundle(Bundle bOptions, int callingPid, int callingUid) {
|
||||
return bOptions != null
|
||||
? new SafeActivityOptions(ActivityOptions.fromBundle(bOptions),
|
||||
callingPid, callingUid)
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance and records {@link Binder#getCallingPid}/
|
||||
* {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when constructing
|
||||
@@ -90,6 +102,17 @@ public class SafeActivityOptions {
|
||||
mOriginalOptions = options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance.
|
||||
*
|
||||
* @param options The options to wrap.
|
||||
*/
|
||||
private SafeActivityOptions(@Nullable ActivityOptions options, int callingPid, int callingUid) {
|
||||
mOriginalCallingPid = callingPid;
|
||||
mOriginalCallingUid = callingUid;
|
||||
mOriginalOptions = options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides options with options from a caller and records {@link Binder#getCallingPid}/
|
||||
* {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when calling this
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
|
||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
|
||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT;
|
||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT;
|
||||
@@ -142,10 +143,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
if (t == null) {
|
||||
throw new IllegalArgumentException("Null transaction passed to applySyncTransaction");
|
||||
}
|
||||
final CallerInfo caller = new CallerInfo();
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mGlobalLock) {
|
||||
applyTransaction(t, -1 /*syncId*/, null /*transition*/);
|
||||
applyTransaction(t, -1 /*syncId*/, null /*transition*/, caller);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
@@ -159,6 +161,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
if (t == null) {
|
||||
throw new IllegalArgumentException("Null transaction passed to applySyncTransaction");
|
||||
}
|
||||
final CallerInfo caller = new CallerInfo();
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mGlobalLock) {
|
||||
@@ -178,7 +181,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
if (callback != null) {
|
||||
syncId = startSyncWithOrganizer(callback);
|
||||
}
|
||||
applyTransaction(t, syncId, null /*transition*/);
|
||||
applyTransaction(t, syncId, null /*transition*/, caller);
|
||||
if (syncId >= 0) {
|
||||
setSyncReady(syncId);
|
||||
}
|
||||
@@ -193,6 +196,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
public IBinder startTransition(int type, @Nullable IBinder transitionToken,
|
||||
@Nullable WindowContainerTransaction t) {
|
||||
enforceTaskPermission("startTransition()");
|
||||
final CallerInfo caller = new CallerInfo();
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mGlobalLock) {
|
||||
@@ -212,7 +216,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
throw new IllegalArgumentException("Can't use legacy transitions in"
|
||||
+ " compatibility mode with no WCT.");
|
||||
}
|
||||
applyTransaction(t, -1 /* syncId */, null);
|
||||
applyTransaction(t, -1 /* syncId */, null, caller);
|
||||
return null;
|
||||
}
|
||||
transition = mTransitionController.createTransition(type);
|
||||
@@ -221,7 +225,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
if (t == null) {
|
||||
t = new WindowContainerTransaction();
|
||||
}
|
||||
applyTransaction(t, -1 /*syncId*/, transition);
|
||||
applyTransaction(t, -1 /*syncId*/, transition, caller);
|
||||
if (needsSetReady) {
|
||||
transition.setReady();
|
||||
}
|
||||
@@ -237,6 +241,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
@Nullable WindowContainerTransaction t,
|
||||
@Nullable IWindowContainerTransactionCallback callback) {
|
||||
enforceTaskPermission("finishTransition()");
|
||||
final CallerInfo caller = new CallerInfo();
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mGlobalLock) {
|
||||
@@ -247,7 +252,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
// apply the incoming transaction before finish in case it alters the visibility
|
||||
// of the participants.
|
||||
if (t != null) {
|
||||
applyTransaction(t, syncId, null /*transition*/);
|
||||
applyTransaction(t, syncId, null /*transition*/, caller);
|
||||
}
|
||||
getTransitionController().finishTransition(transitionToken);
|
||||
if (syncId >= 0) {
|
||||
@@ -263,9 +268,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
/**
|
||||
* @param syncId If non-null, this will be a sync-transaction.
|
||||
* @param transition A transition to collect changes into.
|
||||
* @param caller Info about the calling process.
|
||||
*/
|
||||
private void applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
|
||||
@Nullable Transition transition) {
|
||||
@Nullable Transition transition, @Nullable CallerInfo caller) {
|
||||
int effects = 0;
|
||||
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId);
|
||||
mService.deferWindowLayout();
|
||||
@@ -319,7 +325,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
final boolean isInLockTaskMode = mService.isInLockTaskMode();
|
||||
for (int i = 0; i < hopSize; ++i) {
|
||||
effects |= applyHierarchyOp(hops.get(i), effects, syncId, transition,
|
||||
isInLockTaskMode);
|
||||
isInLockTaskMode, caller);
|
||||
}
|
||||
}
|
||||
// Queue-up bounds-change transactions for tasks which are now organized. Do
|
||||
@@ -474,7 +480,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
}
|
||||
|
||||
private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects,
|
||||
int syncId, @Nullable Transition transition, boolean isInLockTaskMode) {
|
||||
int syncId, @Nullable Transition transition, boolean isInLockTaskMode,
|
||||
@Nullable CallerInfo caller) {
|
||||
final int type = hop.getType();
|
||||
switch (type) {
|
||||
case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: {
|
||||
@@ -555,11 +562,17 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
effects |= sanitizeAndApplyHierarchyOp(wc, hop);
|
||||
break;
|
||||
case HIERARCHY_OP_TYPE_LAUNCH_TASK:
|
||||
mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
|
||||
"launchTask HierarchyOp");
|
||||
final Bundle launchOpts = hop.getLaunchOptions();
|
||||
final int taskId = launchOpts.getInt(
|
||||
WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
|
||||
launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
|
||||
mService.startActivityFromRecents(taskId, launchOpts);
|
||||
final SafeActivityOptions safeOptions = caller != null
|
||||
? SafeActivityOptions.fromBundle(launchOpts, caller.mPid, caller.mUid)
|
||||
: SafeActivityOptions.fromBundle(launchOpts);
|
||||
mService.mTaskSupervisor.startActivityFromRecents(caller.mPid, caller.mUid,
|
||||
taskId, safeOptions);
|
||||
break;
|
||||
case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT:
|
||||
final TaskFragmentCreationParams taskFragmentCreationOptions =
|
||||
@@ -936,4 +949,14 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
mLaunchTaskFragments.removeAt(index);
|
||||
taskFragment.removeImmediately();
|
||||
}
|
||||
|
||||
static class CallerInfo {
|
||||
final int mPid;
|
||||
final int mUid;
|
||||
|
||||
CallerInfo() {
|
||||
mPid = Binder.getCallingPid();
|
||||
mUid = Binder.getCallingUid();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ import com.android.server.wm.ActivityTaskManagerService.HotPath;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -219,6 +220,10 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
|
||||
/** Whether our process is currently running a {@link IRemoteAnimationRunner} */
|
||||
private boolean mRunningRemoteAnimation;
|
||||
|
||||
/** List of "chained" processes that are running remote animations for this process */
|
||||
private final ArrayList<WeakReference<WindowProcessController>> mRemoteAnimationDelegates =
|
||||
new ArrayList<>();
|
||||
|
||||
// The bits used for mActivityStateFlags.
|
||||
private static final int ACTIVITY_STATE_FLAG_IS_VISIBLE = 1 << 16;
|
||||
private static final int ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED = 1 << 17;
|
||||
@@ -1596,11 +1601,38 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
|
||||
updateRunningRemoteOrRecentsAnimation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks another process as a "delegate" animator. This means that process is doing some part
|
||||
* of a remote animation on behalf of this process.
|
||||
*/
|
||||
void addRemoteAnimationDelegate(WindowProcessController delegate) {
|
||||
if (!isRunningRemoteTransition()) {
|
||||
throw new IllegalStateException("Can't add a delegate to a process which isn't itself"
|
||||
+ " running a remote animation");
|
||||
}
|
||||
mRemoteAnimationDelegates.add(new WeakReference<>(delegate));
|
||||
}
|
||||
|
||||
void updateRunningRemoteOrRecentsAnimation() {
|
||||
if (!isRunningRemoteTransition()) {
|
||||
// Clean-up any delegates
|
||||
for (int i = 0; i < mRemoteAnimationDelegates.size(); ++i) {
|
||||
final WindowProcessController delegate = mRemoteAnimationDelegates.get(i).get();
|
||||
if (delegate == null) continue;
|
||||
delegate.setRunningRemoteAnimation(false);
|
||||
delegate.setRunningRecentsAnimation(false);
|
||||
}
|
||||
mRemoteAnimationDelegates.clear();
|
||||
}
|
||||
|
||||
// Posting on handler so WM lock isn't held when we call into AM.
|
||||
mAtm.mH.sendMessage(PooledLambda.obtainMessage(
|
||||
WindowProcessListener::setRunningRemoteAnimation, mListener,
|
||||
mRunningRecentsAnimation || mRunningRemoteAnimation));
|
||||
isRunningRemoteTransition()));
|
||||
}
|
||||
|
||||
boolean isRunningRemoteTransition() {
|
||||
return mRunningRecentsAnimation || mRunningRemoteAnimation;
|
||||
}
|
||||
|
||||
/** Adjusts scheduling group for animation. This method MUST NOT be called inside WM lock. */
|
||||
|
||||
@@ -61,6 +61,7 @@ import static org.mockito.Mockito.clearInvocations;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.ActivityTaskManager.RootTaskInfo;
|
||||
import android.app.IRequestFinishCallback;
|
||||
import android.app.PictureInPictureParams;
|
||||
@@ -69,7 +70,6 @@ import android.content.pm.ParceledListSlice;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
@@ -1255,21 +1255,24 @@ public class WindowOrganizerTests extends WindowTestsBase {
|
||||
@Test
|
||||
public void testStartTasksInTransaction() {
|
||||
WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
Bundle testOptions = new Bundle();
|
||||
testOptions.putInt("test", 20);
|
||||
ActivityOptions testOptions = ActivityOptions.makeBasic();
|
||||
testOptions.setTransientLaunch();
|
||||
wct.startTask(1, null /* options */);
|
||||
wct.startTask(2, testOptions);
|
||||
spyOn(mWm.mAtmService);
|
||||
doReturn(START_CANCELED).when(mWm.mAtmService).startActivityFromRecents(anyInt(), any());
|
||||
wct.startTask(2, testOptions.toBundle());
|
||||
spyOn(mWm.mAtmService.mTaskSupervisor);
|
||||
doReturn(START_CANCELED).when(mWm.mAtmService.mTaskSupervisor).startActivityFromRecents(
|
||||
anyInt(), anyInt(), anyInt(), any());
|
||||
clearInvocations(mWm.mAtmService);
|
||||
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
|
||||
|
||||
final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
verify(mWm.mAtmService, times(1)).startActivityFromRecents(eq(1), bundleCaptor.capture());
|
||||
assertTrue(bundleCaptor.getValue().isEmpty());
|
||||
verify(mWm.mAtmService.mTaskSupervisor, times(1)).startActivityFromRecents(
|
||||
anyInt(), anyInt(), eq(1), any());
|
||||
|
||||
verify(mWm.mAtmService, times(1)).startActivityFromRecents(eq(2), bundleCaptor.capture());
|
||||
assertEquals(20, bundleCaptor.getValue().getInt("test"));
|
||||
final ArgumentCaptor<SafeActivityOptions> optionsCaptor =
|
||||
ArgumentCaptor.forClass(SafeActivityOptions.class);
|
||||
verify(mWm.mAtmService.mTaskSupervisor, times(1)).startActivityFromRecents(
|
||||
anyInt(), anyInt(), eq(2), optionsCaptor.capture());
|
||||
assertTrue(optionsCaptor.getValue().getOriginalOptions().getTransientLaunch());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user