Merge "Disable all input on ActivityRecord during untrusted animation" into tm-dev

This commit is contained in:
Chris Li
2022-03-23 13:47:59 +00:00
committed by Android (Google) Code Review
7 changed files with 221 additions and 79 deletions

View File

@@ -384,6 +384,7 @@ message ActivityRecordProto {
optional float min_aspect_ratio = 33;
optional bool provides_max_bounds = 34;
optional bool enable_recents_screenshot = 35;
optional int32 last_drop_input_mode = 36;
}
/* represents WindowToken */

View File

@@ -613,6 +613,12 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityStarter.java"
},
"-1488852351": {
"message": "Task=%d contains embedded TaskFragment in untrusted mode. Disabled all input during TaskFragment remote animation.",
"level": "DEBUG",
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/AppTransitionController.java"
},
"-1483435730": {
"message": "InsetsSource setWin %s for type %s",
"level": "DEBUG",

View File

@@ -156,6 +156,7 @@ import static com.android.server.wm.ActivityRecordProto.IN_SIZE_COMPAT_MODE;
import static com.android.server.wm.ActivityRecordProto.IS_ANIMATING;
import static com.android.server.wm.ActivityRecordProto.IS_WAITING_FOR_TRANSITION_START;
import static com.android.server.wm.ActivityRecordProto.LAST_ALL_DRAWN;
import static com.android.server.wm.ActivityRecordProto.LAST_DROP_INPUT_MODE;
import static com.android.server.wm.ActivityRecordProto.LAST_SURFACE_SHOWING;
import static com.android.server.wm.ActivityRecordProto.MIN_ASPECT_RATIO;
import static com.android.server.wm.ActivityRecordProto.NAME;
@@ -786,6 +787,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
/** The last set {@link DropInputMode} for this activity surface. */
@DropInputMode
private int mLastDropInputMode = DropInputMode.NONE;
/** Whether the input to this activity will be dropped during the current playing animation. */
private boolean mIsInputDroppedForAnimation;
/**
* If it is non-null, it requires all activities who have the same starting data to be drawn
@@ -1567,6 +1570,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
/** Sets if all input will be dropped as a protection during the client-driven animation. */
void setDropInputForAnimation(boolean isInputDroppedForAnimation) {
if (mIsInputDroppedForAnimation == isInputDroppedForAnimation) {
return;
}
mIsInputDroppedForAnimation = isInputDroppedForAnimation;
updateUntrustedEmbeddingInputProtection();
}
/**
* Sets to drop input when obscured to activity if it is embedded in untrusted mode.
*
@@ -1576,11 +1588,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* all untrusted activities.
*/
private void updateUntrustedEmbeddingInputProtection() {
final SurfaceControl sc = getSurfaceControl();
if (sc == null) {
if (getSurfaceControl() == null) {
return;
}
if (isEmbeddedInUntrustedMode()) {
if (mIsInputDroppedForAnimation) {
// Disable all input during the animation.
setDropInputMode(DropInputMode.ALL);
} else if (isEmbeddedInUntrustedMode()) {
// Set drop input to OBSCURED when untrusted embedded.
setDropInputMode(DropInputMode.OBSCURED);
} else {
@@ -1591,7 +1605,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@VisibleForTesting
void setDropInputMode(@DropInputMode int mode) {
if (mLastDropInputMode != mode && getSurfaceControl() != null) {
if (mLastDropInputMode != mode) {
mLastDropInputMode = mode;
mWmService.mTransactionFactory.get()
.setDropInputMode(getSurfaceControl(), mode)
@@ -9299,6 +9313,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// permission to access the device configs.
proto.write(PROVIDES_MAX_BOUNDS, providesMaxBounds());
proto.write(ENABLE_RECENTS_SCREENSHOT, mEnableRecentsScreenshot);
proto.write(LAST_DROP_INPUT_MODE, mLastDropInputMode);
}
@Override

View File

@@ -98,6 +98,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.function.Consumer;
import java.util.function.Predicate;
/**
@@ -541,12 +542,13 @@ public class AppTransitionController {
}
/**
* Finds the common {@link android.window.TaskFragmentOrganizer} that organizes all app windows
* in the current transition.
* @return {@code null} if there is no such organizer, or if there are more than one.
* Finds the common parent {@link Task} that is parent of all embedded app windows in the
* current transition.
* @return {@code null} if app windows in the transition are not children of the same Task, or
* if none of the app windows is embedded.
*/
@Nullable
private ITaskFragmentOrganizer findTaskFragmentOrganizerForAllWindows() {
private Task findParentTaskForAllEmbeddedWindows() {
mTempTransitionWindows.clear();
mTempTransitionWindows.addAll(mDisplayContent.mClosingApps);
mTempTransitionWindows.addAll(mDisplayContent.mOpeningApps);
@@ -600,13 +602,22 @@ public class AppTransitionController {
leafTask = task;
}
mTempTransitionWindows.clear();
if (leafTask == null) {
return leafTask;
}
/**
* Finds the common {@link android.window.TaskFragmentOrganizer} that organizes all embedded
* {@link TaskFragment} belong to the given {@link Task}.
* @return {@code null} if there is no such organizer, or if there are more than one.
*/
@Nullable
private ITaskFragmentOrganizer findTaskFragmentOrganizer(@Nullable Task task) {
if (task == null) {
return null;
}
// We don't support remote animation for Task with multiple TaskFragmentOrganizers.
final ITaskFragmentOrganizer[] organizer = new ITaskFragmentOrganizer[1];
final boolean hasMultipleOrganizers = leafTask.forAllLeafTaskFragments(taskFragment -> {
final boolean hasMultipleOrganizers = task.forAllLeafTaskFragments(taskFragment -> {
final ITaskFragmentOrganizer tfOrganizer = taskFragment.getTaskFragmentOrganizer();
if (tfOrganizer == null) {
return false;
@@ -638,7 +649,8 @@ public class AppTransitionController {
return false;
}
final ITaskFragmentOrganizer organizer = findTaskFragmentOrganizerForAllWindows();
final Task task = findParentTaskForAllEmbeddedWindows();
final ITaskFragmentOrganizer organizer = findTaskFragmentOrganizer(task);
final RemoteAnimationDefinition definition = organizer != null
? mDisplayContent.mAtmService.mTaskFragmentOrganizerController
.getRemoteAnimationDefinition(organizer)
@@ -653,6 +665,24 @@ public class AppTransitionController {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"Override with TaskFragment remote animation for transit=%s",
AppTransition.appTransitionOldToString(transit));
final boolean hasUntrustedEmbedding = task.forAllLeafTasks(
taskFragment -> !taskFragment.isAllowedToBeEmbeddedInTrustedMode());
final RemoteAnimationController remoteAnimationController =
mDisplayContent.mAppTransition.getRemoteAnimationController();
if (hasUntrustedEmbedding && remoteAnimationController != null) {
// We are going to use client-driven animation, but the Task is in untrusted embedded
// mode. We need to disable all input on activity windows during the animation to
// ensure it is safe. This is needed for all activity windows in the animation Task.
remoteAnimationController.setOnRemoteAnimationReady(() -> {
final Consumer<ActivityRecord> updateActivities =
activity -> activity.setDropInputForAnimation(true);
task.forAllActivities(updateActivities);
});
ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, "Task=%d contains embedded TaskFragment in"
+ " untrusted mode. Disabled all input during TaskFragment remote animation.",
task.mTaskId);
}
return true;
}

View File

@@ -23,6 +23,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Binder;
@@ -49,6 +50,7 @@ import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.function.Consumer;
/**
* Helper class to run app animations in a remote process.
@@ -72,6 +74,8 @@ class RemoteAnimationController implements DeathRecipient {
private FinishedCallback mFinishedCallback;
private boolean mCanceled;
private boolean mLinkedToDeathOfRunner;
@Nullable
private Runnable mOnRemoteAnimationReady;
RemoteAnimationController(WindowManagerService service, DisplayContent displayContent,
RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) {
@@ -101,6 +105,11 @@ class RemoteAnimationController implements DeathRecipient {
return adapters;
}
/** Sets callback to run before starting remote animation. */
void setOnRemoteAnimationReady(@Nullable Runnable onRemoteAnimationReady) {
mOnRemoteAnimationReady = onRemoteAnimationReady;
}
/**
* Called when the transition is ready to be started, and all leashes have been set up.
*/
@@ -133,6 +142,11 @@ class RemoteAnimationController implements DeathRecipient {
return;
}
if (mOnRemoteAnimationReady != null) {
mOnRemoteAnimationReady.run();
mOnRemoteAnimationReady = null;
}
// Create the remote wallpaper animation targets (if any)
final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations();
@@ -292,6 +306,10 @@ class RemoteAnimationController implements DeathRecipient {
mService.closeSurfaceTransaction("RemoteAnimationController#finished");
}
}
// Reset input for all activities when the remote animation is finished.
final Consumer<ActivityRecord> updateActivities =
activity -> activity.setDropInputForAnimation(false);
mDisplayContent.forAllActivities(updateActivities);
setRunningRemoteAnimation(false);
ProtoLog.i(WM_DEBUG_REMOTE_ANIMATIONS, "Finishing remote animation");
}
@@ -303,6 +321,7 @@ class RemoteAnimationController implements DeathRecipient {
} catch (RemoteException e) {
Slog.e(TAG, "Failed to notify cancel", e);
}
mOnRemoteAnimationReady = null;
}
private void releaseFinishedCallback() {

View File

@@ -304,18 +304,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
synchronized (mGlobalLock) {
final TaskFragmentOrganizerState organizerState =
mTaskFragmentOrganizerState.get(organizer.asBinder());
if (organizerState == null) {
return null;
}
for (TaskFragment tf : organizerState.mOrganizedTaskFragments) {
if (!tf.isAllowedToBeEmbeddedInTrustedMode()) {
// Disable client-driven animations for organizer if at least one of the
// embedded task fragments is not embedding in trusted mode.
// TODO(b/197364677): replace with a stub or Shell-driven one instead of skip?
return null;
}
}
return organizerState.mRemoteAnimationDefinition;
return organizerState != null ? organizerState.mRemoteAnimationDefinition : null;
}
}

View File

@@ -33,21 +33,26 @@ import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.annotation.Nullable;
import android.gui.DropInputMode;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -739,20 +744,36 @@ public class AppTransitionControllerTest extends WindowTestsBase {
}
static class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
private IRemoteAnimationFinishedCallback mFinishedCallback;
@Override
public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
mFinishedCallback = finishedCallback;
}
@Override
public void onAnimationCancelled() throws RemoteException {
mFinishedCallback = null;
}
@Override
public IBinder asBinder() {
return new Binder();
}
boolean isAnimationStarted() {
return mFinishedCallback != null;
}
void finishAnimation() {
try {
mFinishedCallback.onAnimationFinished();
} catch (RemoteException e) {
fail();
}
}
}
@Test
@@ -841,144 +862,139 @@ public class AppTransitionControllerTest extends WindowTestsBase {
@Test
public void testOverrideTaskFragmentAdapter_overrideWithEmbeddedActivity() {
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
new TestRemoteAnimationRunner(), 10, 1);
setupTaskFragmentRemoteAnimation(organizer, adapter);
final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
// Create a TaskFragment with embedded activity.
final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(
createTask(mDisplayContent), organizer);
final ActivityRecord activity = taskFragment.getTopMostActivity();
activity.allDrawn = true;
prepareActivityForAppTransition(activity);
spyOn(mDisplayContent.mAppTransition);
// Prepare a transition.
// Prepare and start transition.
prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
// Should be overridden.
verify(mDisplayContent.mAppTransition)
.overridePendingAppTransitionRemote(adapter, false /* sync */);
// Animation run by the remote handler.
assertTrue(remoteAnimationRunner.isAnimationStarted());
}
@Test
public void testOverrideTaskFragmentAdapter_overrideWithNonEmbeddedActivity() {
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
new TestRemoteAnimationRunner(), 10, 1);
setupTaskFragmentRemoteAnimation(organizer, adapter);
final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
final Task task = createTask(mDisplayContent);
// Closing non-embedded activity.
final ActivityRecord closingActivity = createActivityRecord(task);
closingActivity.allDrawn = true;
prepareActivityForAppTransition(closingActivity);
// Opening TaskFragment with embedded activity.
final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
final ActivityRecord openingActivity = taskFragment.getTopMostActivity();
openingActivity.allDrawn = true;
prepareActivityForAppTransition(openingActivity);
task.effectiveUid = openingActivity.getUid();
spyOn(mDisplayContent.mAppTransition);
// Prepare a transition.
// Prepare and start transition.
prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
// Should be overridden.
verify(mDisplayContent.mAppTransition)
.overridePendingAppTransitionRemote(adapter, false /* sync */);
// Animation run by the remote handler.
assertTrue(remoteAnimationRunner.isAnimationStarted());
}
@Test
public void testOverrideTaskFragmentAdapter_overrideEmbeddedActivityWithDiffUid() {
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
new TestRemoteAnimationRunner(), 10, 1);
setupTaskFragmentRemoteAnimation(organizer, adapter);
final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
final Task task = createTask(mDisplayContent);
// Closing TaskFragment with embedded activity.
final TaskFragment taskFragment1 = createTaskFragmentWithEmbeddedActivity(task, organizer);
final ActivityRecord closingActivity = taskFragment1.getTopMostActivity();
closingActivity.allDrawn = true;
prepareActivityForAppTransition(closingActivity);
closingActivity.info.applicationInfo.uid = 12345;
// Opening TaskFragment with embedded activity with different UID.
final TaskFragment taskFragment2 = createTaskFragmentWithEmbeddedActivity(task, organizer);
final ActivityRecord openingActivity = taskFragment2.getTopMostActivity();
prepareActivityForAppTransition(openingActivity);
openingActivity.info.applicationInfo.uid = 54321;
openingActivity.allDrawn = true;
spyOn(mDisplayContent.mAppTransition);
// Prepare a transition.
// Prepare and start transition.
prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment1);
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
// Should be overridden.
verify(mDisplayContent.mAppTransition)
.overridePendingAppTransitionRemote(adapter, false /* sync */);
// Animation run by the remote handler.
assertTrue(remoteAnimationRunner.isAnimationStarted());
}
@Test
public void testOverrideTaskFragmentAdapter_noOverrideWithTwoApps() {
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
new TestRemoteAnimationRunner(), 10, 1);
setupTaskFragmentRemoteAnimation(organizer, adapter);
final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
// Closing activity in Task1.
final ActivityRecord closingActivity = createActivityRecord(mDisplayContent);
closingActivity.allDrawn = true;
prepareActivityForAppTransition(closingActivity);
// Opening TaskFragment with embedded activity in Task2.
final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(
createTask(mDisplayContent), organizer);
final ActivityRecord openingActivity = taskFragment.getTopMostActivity();
openingActivity.allDrawn = true;
prepareActivityForAppTransition(openingActivity);
spyOn(mDisplayContent.mAppTransition);
// Prepare a transition for TaskFragment.
// Prepare and start transition.
prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
// Should not be overridden.
verify(mDisplayContent.mAppTransition, never())
.overridePendingAppTransitionRemote(adapter, false /* sync */);
// Animation not run by the remote handler.
assertFalse(remoteAnimationRunner.isAnimationStarted());
}
@Test
public void testOverrideTaskFragmentAdapter_noOverrideNonEmbeddedActivityWithDiffUid() {
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
new TestRemoteAnimationRunner(), 10, 1);
setupTaskFragmentRemoteAnimation(organizer, adapter);
final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
final Task task = createTask(mDisplayContent);
// Closing TaskFragment with embedded activity.
final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
final ActivityRecord closingActivity = taskFragment.getTopMostActivity();
closingActivity.allDrawn = true;
prepareActivityForAppTransition(closingActivity);
closingActivity.info.applicationInfo.uid = 12345;
task.effectiveUid = closingActivity.getUid();
// Opening non-embedded activity with different UID.
final ActivityRecord openingActivity = createActivityRecord(task);
prepareActivityForAppTransition(openingActivity);
openingActivity.info.applicationInfo.uid = 54321;
openingActivity.allDrawn = true;
spyOn(mDisplayContent.mAppTransition);
// Prepare a transition.
// Prepare and start transition.
prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
// Should not be overridden
verify(mDisplayContent.mAppTransition, never())
.overridePendingAppTransitionRemote(adapter, false /* sync */);
// Animation should not run by the remote handler when there are non-embedded activities of
// different UID.
assertFalse(remoteAnimationRunner.isAnimationStarted());
}
@Test
public void testOverrideTaskFragmentAdapter_noOverrideWithWallpaper() {
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
new TestRemoteAnimationRunner(), 10, 1);
setupTaskFragmentRemoteAnimation(organizer, adapter);
final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
// Create a TaskFragment with embedded activity.
final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(
createTask(mDisplayContent), organizer);
final ActivityRecord activity = taskFragment.getTopMostActivity();
activity.allDrawn = true;
prepareActivityForAppTransition(activity);
// Set wallpaper as visible.
final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm,
mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */);
@@ -986,12 +1002,66 @@ public class AppTransitionControllerTest extends WindowTestsBase {
doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible();
spyOn(mDisplayContent.mAppTransition);
// Prepare a transition.
// Prepare and start transition.
prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
// Should not be overridden when there is wallpaper in the transition.
verify(mDisplayContent.mAppTransition, never())
.overridePendingAppTransitionRemote(adapter, false /* sync */);
// Animation should not run by the remote handler when there is wallpaper in the transition.
assertFalse(remoteAnimationRunner.isAnimationStarted());
}
@Test
public void testOverrideTaskFragmentAdapter_inputProtectedForUntrustedAnimation() {
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
// Create a TaskFragment with embedded activities, one is trusted embedded, and the other
// one is untrusted embedded.
final Task task = createTask(mDisplayContent);
final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
.setParentTask(task)
.createActivityCount(2)
.setOrganizer(organizer)
.build();
final ActivityRecord activity0 = taskFragment.getChildAt(0).asActivityRecord();
final ActivityRecord activity1 = taskFragment.getChildAt(1).asActivityRecord();
// Also create a non-embedded activity in the Task.
final ActivityRecord activity2 = new ActivityBuilder(mAtm).build();
task.addChild(activity2, POSITION_BOTTOM);
prepareActivityForAppTransition(activity0);
prepareActivityForAppTransition(activity1);
prepareActivityForAppTransition(activity2);
doReturn(false).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity0);
doReturn(true).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity1);
spyOn(mDisplayContent.mAppTransition);
// Prepare and start transition.
prepareAndTriggerAppTransition(activity1, null /* closingActivity */, taskFragment);
mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
// The animation will be animated remotely by client and all activities are input disabled
// for untrusted animation.
assertTrue(remoteAnimationRunner.isAnimationStarted());
verify(activity0).setDropInputForAnimation(true);
verify(activity1).setDropInputForAnimation(true);
verify(activity2).setDropInputForAnimation(true);
verify(activity0).setDropInputMode(DropInputMode.ALL);
verify(activity1).setDropInputMode(DropInputMode.ALL);
verify(activity2).setDropInputMode(DropInputMode.ALL);
// Reset input after animation is finished.
clearInvocations(activity0);
clearInvocations(activity1);
clearInvocations(activity2);
remoteAnimationRunner.finishAnimation();
verify(activity0).setDropInputForAnimation(false);
verify(activity1).setDropInputForAnimation(false);
verify(activity2).setDropInputForAnimation(false);
verify(activity0).setDropInputMode(DropInputMode.OBSCURED);
verify(activity1).setDropInputMode(DropInputMode.NONE);
verify(activity2).setDropInputMode(DropInputMode.NONE);
}
@Test
@@ -1004,7 +1074,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
.setParentTask(task)
.setOrganizer(organizer)
.build();
changeTaskFragment.getTopMostActivity().allDrawn = true;
prepareActivityForAppTransition(changeTaskFragment.getTopMostActivity());
spyOn(mDisplayContent.mAppTransition);
spyOn(emptyTaskFragment);
@@ -1034,7 +1104,7 @@ public class AppTransitionControllerTest extends WindowTestsBase {
.setParentTask(task)
.setOrganizer(organizer)
.build();
changeTaskFragment.getTopMostActivity().allDrawn = true;
prepareActivityForAppTransition(changeTaskFragment.getTopMostActivity());
// To make sure that having a detached activity won't cause any issue.
final ActivityRecord detachedActivity = createActivityRecord(task);
detachedActivity.removeImmediately();
@@ -1060,7 +1130,9 @@ public class AppTransitionControllerTest extends WindowTestsBase {
/** Registers remote animation for the organizer. */
private void setupTaskFragmentRemoteAnimation(TaskFragmentOrganizer organizer,
RemoteAnimationAdapter adapter) {
TestRemoteAnimationRunner remoteAnimationRunner) {
final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
remoteAnimationRunner, 10, 1);
final ITaskFragmentOrganizer iOrganizer =
ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder());
final RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
@@ -1087,4 +1159,14 @@ public class AppTransitionControllerTest extends WindowTestsBase {
}
mDisplayContent.mAppTransitionController.handleAppTransitionReady();
}
private static void prepareActivityForAppTransition(ActivityRecord activity) {
// Transition will wait until all participated activities to be drawn.
activity.allDrawn = true;
// Skip manipulate the SurfaceControl.
doNothing().when(activity).setDropInputMode(anyInt());
// Make sure activity can create remote animation target.
doReturn(mock(RemoteAnimationTarget.class)).when(activity).createRemoteAnimationTarget(
any());
}
}