Merge "Disable all input on ActivityRecord during untrusted animation" into tm-dev
This commit is contained in:
@@ -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 */
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
@@ -303,6 +321,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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user