Merge "Allow TaskFragmentOrganizer to set remote animation" into sc-v2-dev

This commit is contained in:
Chris Li
2021-08-31 17:49:33 +00:00
committed by Android (Google) Code Review
10 changed files with 266 additions and 5 deletions

View File

@@ -16,6 +16,7 @@
package android.window;
import android.view.RemoteAnimationDefinition;
import android.window.ITaskFragmentOrganizer;
/** @hide */
@@ -30,4 +31,17 @@ interface ITaskFragmentOrganizerController {
* Unregisters a previously registered TaskFragmentOrganizer.
*/
void unregisterOrganizer(in ITaskFragmentOrganizer organizer);
/**
* Registers remote animations per transition type for the organizer. It will override the
* animations if the transition only contains windows that belong to the organized
* TaskFragments.
*/
void registerRemoteAnimations(in ITaskFragmentOrganizer organizer,
in RemoteAnimationDefinition definition);
/**
* Unregisters remote animations per transition type for the organizer.
*/
void unregisterRemoteAnimations(in ITaskFragmentOrganizer organizer);
}

View File

@@ -23,6 +23,7 @@ import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.RemoteAnimationDefinition;
import java.util.concurrent.Executor;
@@ -90,6 +91,34 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
}
}
/**
* Registers remote animations per transition type for the organizer. It will override the
* animations if the transition only contains windows that belong to the organized
* TaskFragments.
* @hide
*/
@CallSuper
public void registerRemoteAnimations(@NonNull RemoteAnimationDefinition definition) {
try {
getController().registerRemoteAnimations(mInterface, definition);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Unregisters remote animations per transition type for the organizer.
* @hide
*/
@CallSuper
public void unregisterRemoteAnimations() {
try {
getController().unregisterRemoteAnimations(mInterface);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/** Called when a TaskFragment is created and organized by this organizer. */
public void onTaskFragmentAppeared(
@NonNull TaskFragmentAppearedInfo taskFragmentAppearedInfo) {}

View File

@@ -1261,6 +1261,12 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java"
},
"-702650156": {
"message": "Override with TaskFragment remote animation for transit=%s",
"level": "VERBOSE",
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/AppTransitionController.java"
},
"-701167286": {
"message": "applyAnimation: transit=%s, enter=%b, wc=%s",
"level": "VERBOSE",
@@ -1789,6 +1795,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/TaskFragment.java"
},
"-70719599": {
"message": "Unregister remote animations for organizer=%s uid=%d pid=%d",
"level": "VERBOSE",
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
},
"-55185509": {
"message": "setFocusedTask: taskId=%d touchedActivity=%s",
"level": "DEBUG",
@@ -2941,6 +2953,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
"1210037962": {
"message": "Register remote animations for organizer=%s uid=%d pid=%d",
"level": "VERBOSE",
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
},
"1219600119": {
"message": "addWindow: win=%s Callers=%s",
"level": "DEBUG",

View File

@@ -2595,6 +2595,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return task != null ? task.getOrganizedTask() : null;
}
/** Returns the organized parent {@link TaskFragment}. */
@Nullable
TaskFragment getOrganizedTaskFragment() {
final TaskFragment parent = getTaskFragment();
return parent != null ? parent.getOrganizedTaskFragment() : null;
}
@Override
@Nullable
TaskDisplayArea getDisplayArea() {

View File

@@ -86,6 +86,7 @@ import android.view.WindowManager.TransitionFlags;
import android.view.WindowManager.TransitionOldType;
import android.view.WindowManager.TransitionType;
import android.view.animation.Animation;
import android.window.ITaskFragmentOrganizer;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
@@ -233,7 +234,11 @@ public class AppTransitionController {
final ActivityRecord topChangingApp =
getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */);
final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity);
overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
// Check if there is any override
if (!overrideWithTaskFragmentRemoteAnimation(transit, activityTypes)) {
overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
}
final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mOpeningApps)
|| containsVoiceInteraction(mDisplayContent.mOpeningApps);
@@ -483,6 +488,7 @@ public class AppTransitionController {
return TYPE_NONE;
}
@Nullable
private static WindowManager.LayoutParams getAnimLp(ActivityRecord activity) {
final WindowState mainWindow = activity != null ? activity.findMainWindow() : null;
return mainWindow != null ? mainWindow.mAttrs : null;
@@ -505,6 +511,61 @@ public class AppTransitionController {
: null;
}
/**
* Overrides the pending transition with the remote animation defined by the
* {@link ITaskFragmentOrganizer} if all windows in the transition are children of
* {@link TaskFragment} that are organized by the same organizer.
*
* @return {@code true} if the transition is overridden.
*/
@VisibleForTesting
boolean overrideWithTaskFragmentRemoteAnimation(@TransitionOldType int transit,
ArraySet<Integer> activityTypes) {
final ArrayList<WindowContainer> allWindows = new ArrayList<>();
allWindows.addAll(mDisplayContent.mClosingApps);
allWindows.addAll(mDisplayContent.mOpeningApps);
allWindows.addAll(mDisplayContent.mChangingContainers);
// Find the common TaskFragmentOrganizer of all windows.
ITaskFragmentOrganizer organizer = null;
for (int i = allWindows.size() - 1; i >= 0; i--) {
final ActivityRecord r = getAppFromContainer(allWindows.get(i));
if (r == null) {
return false;
}
final TaskFragment organizedTaskFragment = r.getOrganizedTaskFragment();
final ITaskFragmentOrganizer curOrganizer = organizedTaskFragment != null
? organizedTaskFragment.getTaskFragmentOrganizer()
: null;
if (curOrganizer == null) {
// All windows must below an organized TaskFragment.
return false;
}
if (organizer == null) {
organizer = curOrganizer;
} else if (!organizer.asBinder().equals(curOrganizer.asBinder())) {
// They must be controlled by the same organizer.
return false;
}
}
final RemoteAnimationDefinition definition = organizer != null
? mDisplayContent.mAtmService.mTaskFragmentOrganizerController
.getRemoteAnimationDefinition(organizer)
: null;
final RemoteAnimationAdapter adapter = definition != null
? definition.getAdapter(transit, activityTypes)
: null;
if (adapter == null) {
return false;
}
mDisplayContent.mAppTransition.overridePendingAppTransitionRemote(adapter);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"Override with TaskFragment remote animation for transit=%s",
AppTransition.appTransitionOldToString(transit));
return true;
}
/**
* Overrides the pending transition with the remote animation defined for the transition in the
* set of defined remote animations in the app window token.
@@ -524,13 +585,14 @@ public class AppTransitionController {
}
static ActivityRecord getAppFromContainer(WindowContainer wc) {
return wc.asTask() != null ? wc.asTask().getTopNonFinishingActivity()
return wc.asTaskFragment() != null ? wc.asTaskFragment().getTopNonFinishingActivity()
: wc.asActivityRecord();
}
/**
* @return The window token that determines the animation theme.
*/
@Nullable
private ActivityRecord findAnimLayoutParamsToken(@TransitionOldType int transit,
ArraySet<Integer> activityTypes) {
ActivityRecord result;
@@ -543,7 +605,7 @@ public class AppTransitionController {
w -> w.getRemoteAnimationDefinition() != null
&& w.getRemoteAnimationDefinition().hasTransition(transit, activityTypes));
if (result != null) {
return getAppFromContainer(result);
return result;
}
result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps,
w -> w.fillsParent() && w.findMainWindow() != null);

View File

@@ -2095,7 +2095,6 @@ class TaskFragment extends WindowContainer<WindowContainer> {
}
@Nullable
@VisibleForTesting
ITaskFragmentOrganizer getTaskFragmentOrganizer() {
return mTaskFragmentOrganizer;
}

View File

@@ -30,6 +30,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Slog;
import android.view.RemoteAnimationDefinition;
import android.view.SurfaceControl;
import android.window.ITaskFragmentOrganizer;
import android.window.ITaskFragmentOrganizerController;
@@ -81,6 +82,13 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
private final Map<TaskFragment, Configuration> mLastSentTaskFragmentParentConfigs =
new WeakHashMap<>();
/**
* @see android.window.TaskFragmentOrganizer#registerRemoteAnimations(
* RemoteAnimationDefinition)
*/
@Nullable
private RemoteAnimationDefinition mRemoteAnimationDefinition;
TaskFragmentOrganizerState(ITaskFragmentOrganizer organizer) {
mOrganizer = organizer;
try {
@@ -245,6 +253,61 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
}
@Override
public void registerRemoteAnimations(ITaskFragmentOrganizer organizer,
RemoteAnimationDefinition definition) {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
synchronized (mGlobalLock) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
"Register remote animations for organizer=%s uid=%d pid=%d",
organizer.asBinder(), uid, pid);
final TaskFragmentOrganizerState organizerState =
mTaskFragmentOrganizerState.get(organizer.asBinder());
if (organizerState == null) {
throw new IllegalStateException("The organizer hasn't been registered.");
}
if (organizerState.mRemoteAnimationDefinition != null) {
throw new IllegalStateException(
"The organizer has already registered remote animations="
+ organizerState.mRemoteAnimationDefinition);
}
definition.setCallingPidUid(pid, uid);
organizerState.mRemoteAnimationDefinition = definition;
}
}
@Override
public void unregisterRemoteAnimations(ITaskFragmentOrganizer organizer) {
final int pid = Binder.getCallingPid();
final long uid = Binder.getCallingUid();
synchronized (mGlobalLock) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
"Unregister remote animations for organizer=%s uid=%d pid=%d",
organizer.asBinder(), uid, pid);
final TaskFragmentOrganizerState organizerState =
mTaskFragmentOrganizerState.get(organizer.asBinder());
if (organizerState == null) {
Slog.e(TAG, "The organizer hasn't been registered.");
return;
}
organizerState.mRemoteAnimationDefinition = null;
}
}
/** Gets the {@link RemoteAnimationDefinition} set on the given organizer if exists. */
@Nullable
public RemoteAnimationDefinition getRemoteAnimationDefinition(
ITaskFragmentOrganizer organizer) {
synchronized (mGlobalLock) {
final TaskFragmentOrganizerState organizerState =
mTaskFragmentOrganizerState.get(organizer.asBinder());
return organizerState != null ? organizerState.mRemoteAnimationDefinition : null;
}
}
void onTaskFragmentAppeared(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
final TaskFragmentOrganizerState state = validateAndGetState(organizer);
if (!state.addTaskFragment(taskFragment)) {

View File

@@ -26,15 +26,19 @@ import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
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.spyOn;
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.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.verify;
import android.os.Binder;
import android.os.IBinder;
@@ -47,6 +51,8 @@ import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationTarget;
import android.view.WindowManager;
import android.window.ITaskFragmentOrganizer;
import android.window.TaskFragmentOrganizer;
import androidx.test.filters.SmallTest;
@@ -737,4 +743,38 @@ public class AppTransitionControllerTest extends WindowTestsBase {
mAppTransitionController.getRemoteAnimationOverride(
activity, TRANSIT_OLD_ACTIVITY_OPEN, new ArraySet<Integer>()));
}
@Test
public void testGetRemoteAnimationOverrideTaskFragmentOrganizer() {
// TaskFragmentOrganizer registers remote animation.
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
final ITaskFragmentOrganizer iOrganizer =
ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder());
final RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
new TestRemoteAnimationRunner(), 10, 1);
definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, adapter);
mAtm.mTaskFragmentOrganizerController.registerOrganizer(iOrganizer);
mAtm.mTaskFragmentOrganizerController.registerRemoteAnimations(iOrganizer, definition);
// Create a TaskFragment with embedded activity.
final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
.setParentTask(createTask(mDisplayContent))
.createActivityCount(1)
.setOrganizer(organizer)
.build();
final ActivityRecord activity = taskFragment.getTopMostActivity();
activity.allDrawn = true;
spyOn(mDisplayContent.mAppTransition);
// Prepare a transition for TaskFragment.
mDisplayContent.mAppTransition.prepareAppTransition(TRANSIT_CHANGE, 0);
mDisplayContent.mOpeningApps.add(activity);
mDisplayContent.mChangingContainers.add(taskFragment);
mDisplayContent.mAppTransitionController.handleAppTransitionReady();
// Check if the transition has been overridden.
verify(mDisplayContent.mAppTransition)
.overridePendingAppTransitionRemote(adapter, false /* sync */);
}
}

View File

@@ -21,6 +21,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.testing.Assert.assertThrows;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@@ -36,6 +37,7 @@ import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.view.RemoteAnimationDefinition;
import android.view.SurfaceControl;
import android.window.ITaskFragmentOrganizer;
import android.window.TaskFragmentCreationParams;
@@ -70,6 +72,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
private IBinder mFragmentToken;
private WindowContainerTransaction mTransaction;
private WindowContainerToken mFragmentWindowToken;
private RemoteAnimationDefinition mDefinition;
@Before
public void setup() {
@@ -83,6 +86,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
new TaskFragment(mAtm, mFragmentToken, true /* createdByOrganizer */);
mTransaction = new WindowContainerTransaction();
mFragmentWindowToken = mTaskFragment.mRemoteToken.toWindowContainerToken();
mDefinition = new RemoteAnimationDefinition();
spyOn(mController);
spyOn(mOrganizer);
@@ -207,6 +211,18 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
verify(mOrganizer).onTaskFragmentError(eq(errorCallbackToken), eq(exception));
}
@Test
public void testRegisterRemoteAnimations() {
mController.registerOrganizer(mIOrganizer);
mController.registerRemoteAnimations(mIOrganizer, mDefinition);
assertEquals(mDefinition, mController.getRemoteAnimationDefinition(mIOrganizer));
mController.unregisterRemoteAnimations(mIOrganizer);
assertNull(mController.getRemoteAnimationDefinition(mIOrganizer));
}
@Test
public void testWindowContainerTransaction_setTaskFragmentOrganizer() {
mOrganizer.applyTransaction(mTransaction);

View File

@@ -67,6 +67,7 @@ import static org.mockito.Mockito.mock;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.IApplicationThread;
@@ -100,6 +101,7 @@ import android.view.WindowManager;
import android.view.WindowManager.DisplayImePolicy;
import android.window.ITransitionPlayer;
import android.window.StartingWindowInfo;
import android.window.TaskFragmentOrganizer;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
@@ -1135,6 +1137,8 @@ class WindowTestsBase extends SystemServiceTestsBase {
private boolean mCreateParentTask;
private boolean mCreateEmbeddedTask;
private int mCreateActivityCount = 0;
@Nullable
private TaskFragmentOrganizer mOrganizer;
TaskFragmentBuilder(ActivityTaskManagerService service) {
mAtm = service;
@@ -1161,11 +1165,16 @@ class WindowTestsBase extends SystemServiceTestsBase {
return this;
}
TaskFragmentBuilder setOrganizer(@Nullable TaskFragmentOrganizer organizer) {
mOrganizer = organizer;
return this;
}
TaskFragment build() {
SystemServicesTestRule.checkHoldsLock(mAtm.mGlobalLock);
final TaskFragment taskFragment = new TaskFragment(mAtm, null /* fragmentToken */,
false /* createdByOrganizer */);
mOrganizer != null);
if (mParentTask == null && mCreateParentTask) {
mParentTask = new TaskBuilder(mAtm.mTaskSupervisor).build();
}
@@ -1183,6 +1192,10 @@ class WindowTestsBase extends SystemServiceTestsBase {
taskFragment.addChild(activity);
mCreateActivityCount--;
}
if (mOrganizer != null) {
taskFragment.setTaskFragmentOrganizer(
mOrganizer.getOrganizerToken(), 10000 /* pid */);
}
return taskFragment;
}
}