Disable all input on ActivityRecord during untrusted animation
For cross-process embedding, there can be activity of other app embedded in untrusted mode. When it happens, we need to disable all input on the Task if we are going to play client-driven animation to make sure the host client can't abuse the animation leash. Bug: 197364677 Test: atest WmTests:AppTransitionControllerTest Change-Id: I3e299c0a43ac823b7df6af9d02c7168bd65d3271
This commit is contained in:
parent
6cc0fa4d00
commit
a53b05ca8c
@ -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 */
|
||||
|
@ -577,6 +577,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",
|
||||
|
@ -155,6 +155,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)
|
||||
@ -9301,6 +9315,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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
@ -302,6 +320,7 @@ class RemoteAnimationController implements DeathRecipient {
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to notify cancel", e);
|
||||
}
|
||||
mOnRemoteAnimationReady = null;
|
||||
}
|
||||
|
||||
private void releaseFinishedCallback() {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user