Merge "Wait for TaskFragmentOrganizer to finish handling transaction" into tm-qpr-dev

This commit is contained in:
Chris Li
2022-08-23 23:56:37 +00:00
committed by Android (Google) Code Review
8 changed files with 193 additions and 43 deletions

View File

@@ -16,8 +16,10 @@
package android.window;
import android.os.IBinder;
import android.view.RemoteAnimationDefinition;
import android.window.ITaskFragmentOrganizer;
import android.window.WindowContainerTransaction;
/** @hide */
interface ITaskFragmentOrganizerController {
@@ -46,8 +48,15 @@ interface ITaskFragmentOrganizerController {
void unregisterRemoteAnimations(in ITaskFragmentOrganizer organizer, int taskId);
/**
* Checks if an activity organized by a {@link android.window.TaskFragmentOrganizer} and
* only occupies a portion of Task bounds.
*/
* Checks if an activity organized by a {@link android.window.TaskFragmentOrganizer} and
* only occupies a portion of Task bounds.
*/
boolean isActivityEmbedded(in IBinder activityToken);
/**
* Notifies the server that the organizer has finished handling the given transaction. The
* server should apply the given {@link WindowContainerTransaction} for the necessary changes.
*/
void onTransactionHandled(in ITaskFragmentOrganizer organizer, in IBinder transactionToken,
in WindowContainerTransaction wct);
}

View File

@@ -26,7 +26,6 @@ import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.content.Intent;
import android.content.res.Configuration;
@@ -148,6 +147,28 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
}
}
/**
* Notifies the server that the organizer has finished handling the given transaction. The
* server should apply the given {@link WindowContainerTransaction} for the necessary changes.
*
* @param transactionToken {@link TaskFragmentTransaction#getTransactionToken()} from
* {@link #onTransactionReady(TaskFragmentTransaction)}
* @param wct {@link WindowContainerTransaction} that the server should apply for
* update of the transaction.
* @see com.android.server.wm.WindowOrganizerController#enforceTaskPermission for permission
* requirement.
* @hide
*/
public void onTransactionHandled(@NonNull IBinder transactionToken,
@NonNull WindowContainerTransaction wct) {
wct.setTaskFragmentOrganizer(mInterface);
try {
getController().onTransactionHandled(mInterface, transactionToken, wct);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Called when a TaskFragment is created and organized by this organizer.
*
@@ -318,12 +339,8 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
/**
* Called when the transaction is ready so that the organizer can update the TaskFragments based
* on the changes in transaction.
* Note: {@link WindowOrganizer#applyTransaction} permission requirement is conditional for
* {@link TaskFragmentOrganizer}.
* @see com.android.server.wm.WindowOrganizerController#enforceTaskPermission
* @hide
*/
@SuppressLint("AndroidFrameworkRequiresPermission")
public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
final List<TaskFragmentTransaction.Change> changes = transaction.getChanges();
@@ -389,8 +406,9 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
"Unknown TaskFragmentEvent=" + change.getType());
}
}
// TODO(b/240519866): notify TaskFragmentOrganizerController that the transition is done.
applyTransaction(wct);
// Notify the server, and the server should apply the WindowContainerTransaction.
onTransactionHandled(transaction.getTransactionToken(), wct);
}
@Override

View File

@@ -23,6 +23,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
@@ -41,19 +42,31 @@ import java.util.List;
*/
public final class TaskFragmentTransaction implements Parcelable {
/** Unique token to represent this transaction. */
private final IBinder mTransactionToken;
/** Changes in this transaction. */
private final ArrayList<Change> mChanges = new ArrayList<>();
public TaskFragmentTransaction() {}
public TaskFragmentTransaction() {
mTransactionToken = new Binder();
}
private TaskFragmentTransaction(Parcel in) {
mTransactionToken = in.readStrongBinder();
in.readTypedList(mChanges, Change.CREATOR);
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeStrongBinder(mTransactionToken);
dest.writeTypedList(mChanges);
}
public IBinder getTransactionToken() {
return mTransactionToken;
}
/** Adds a {@link Change} to this transaction. */
public void addChange(@Nullable Change change) {
if (change != null) {
@@ -74,7 +87,9 @@ public final class TaskFragmentTransaction implements Parcelable {
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("TaskFragmentTransaction{changes=[");
sb.append("TaskFragmentTransaction{token=");
sb.append(mTransactionToken);
sb.append(" changes=[");
for (int i = 0; i < mChanges.size(); ++i) {
if (i > 0) {
sb.append(',');

View File

@@ -2041,6 +2041,12 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
"-108248992": {
"message": "Defer transition ready for TaskFragmentTransaction=%s",
"level": "VERBOSE",
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
},
"-106400104": {
"message": "Preload recents with %s",
"level": "DEBUG",
@@ -2089,6 +2095,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/TaskFragment.java"
},
"-79016993": {
"message": "Continue transition ready for TaskFragmentTransaction=%s",
"level": "VERBOSE",
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/TaskFragmentOrganizerController.java"
},
"-70719599": {
"message": "Unregister remote animations for organizer=%s uid=%d pid=%d",
"level": "VERBOSE",

View File

@@ -49,7 +49,9 @@ import android.window.ITaskFragmentOrganizer;
import android.window.ITaskFragmentOrganizerController;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentTransaction;
import android.window.WindowContainerTransaction;
import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.common.ProtoLog;
import java.lang.annotation.Retention;
@@ -68,6 +70,8 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
private final ActivityTaskManagerService mAtmService;
private final WindowManagerGlobalLock mGlobalLock;
private final WindowOrganizerController mWindowOrganizerController;
/**
* A Map which manages the relationship between
* {@link ITaskFragmentOrganizer} and {@link TaskFragmentOrganizerState}
@@ -82,9 +86,11 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
private final ArraySet<Task> mTmpTaskSet = new ArraySet<>();
TaskFragmentOrganizerController(ActivityTaskManagerService atm) {
mAtmService = atm;
TaskFragmentOrganizerController(@NonNull ActivityTaskManagerService atm,
@NonNull WindowOrganizerController windowOrganizerController) {
mAtmService = requireNonNull(atm);
mGlobalLock = atm.mGlobalLock;
mWindowOrganizerController = requireNonNull(windowOrganizerController);
}
/**
@@ -131,6 +137,14 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
private final SparseArray<RemoteAnimationDefinition> mRemoteAnimationDefinitions =
new SparseArray<>();
/**
* List of {@link TaskFragmentTransaction#getTransactionToken()} that have been sent to the
* organizer. If the transaction is sent during a transition, the
* {@link TransitionController} will wait until the transaction is finished.
* @see #onTransactionFinished(IBinder)
*/
private final List<IBinder> mRunningTransactions = new ArrayList<>();
TaskFragmentOrganizerState(ITaskFragmentOrganizer organizer, int pid, int uid) {
mOrganizer = organizer;
mOrganizerPid = pid;
@@ -176,6 +190,10 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
taskFragment.removeImmediately();
mOrganizedTaskFragments.remove(taskFragment);
}
for (int i = mRunningTransactions.size() - 1; i >= 0; i--) {
// Cleanup any running transaction to unblock the current transition.
onTransactionFinished(mRunningTransactions.get(i));
}
mOrganizer.asBinder().unlinkToDeath(this, 0 /*flags*/);
}
@@ -320,6 +338,40 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
.setActivityIntent(activity.intent)
.setActivityToken(activityToken);
}
void dispatchTransaction(@NonNull TaskFragmentTransaction transaction) {
if (transaction.isEmpty()) {
return;
}
try {
mOrganizer.onTransactionReady(transaction);
} catch (RemoteException e) {
Slog.d(TAG, "Exception sending TaskFragmentTransaction", e);
return;
}
onTransactionStarted(transaction.getTransactionToken());
}
/** Called when the transaction is sent to the organizer. */
void onTransactionStarted(@NonNull IBinder transactionToken) {
if (!mWindowOrganizerController.getTransitionController().isCollecting()) {
return;
}
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Defer transition ready for TaskFragmentTransaction=%s", transactionToken);
mRunningTransactions.add(transactionToken);
mWindowOrganizerController.getTransitionController().deferTransitionReady();
}
/** Called when the transaction is finished. */
void onTransactionFinished(@NonNull IBinder transactionToken) {
if (!mRunningTransactions.remove(transactionToken)) {
return;
}
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Continue transition ready for TaskFragmentTransaction=%s", transactionToken);
mWindowOrganizerController.getTransitionController().continueTransitionReady();
}
}
@Nullable
@@ -336,7 +388,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
@Override
public void registerOrganizer(ITaskFragmentOrganizer organizer) {
public void registerOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
synchronized (mGlobalLock) {
@@ -354,7 +406,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
@Override
public void unregisterOrganizer(ITaskFragmentOrganizer organizer) {
public void unregisterOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
validateAndGetState(organizer);
final int pid = Binder.getCallingPid();
final long uid = Binder.getCallingUid();
@@ -372,8 +424,8 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
@Override
public void registerRemoteAnimations(ITaskFragmentOrganizer organizer, int taskId,
RemoteAnimationDefinition definition) {
public void registerRemoteAnimations(@NonNull ITaskFragmentOrganizer organizer, int taskId,
@NonNull RemoteAnimationDefinition definition) {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
synchronized (mGlobalLock) {
@@ -398,7 +450,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
@Override
public void unregisterRemoteAnimations(ITaskFragmentOrganizer organizer, int taskId) {
public void unregisterRemoteAnimations(@NonNull ITaskFragmentOrganizer organizer, int taskId) {
final int pid = Binder.getCallingPid();
final long uid = Binder.getCallingUid();
synchronized (mGlobalLock) {
@@ -416,6 +468,17 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
}
@Override
public void onTransactionHandled(@NonNull ITaskFragmentOrganizer organizer,
@NonNull IBinder transactionToken, @NonNull WindowContainerTransaction wct) {
synchronized (mGlobalLock) {
// Keep the calling identity to avoid unsecure change.
mWindowOrganizerController.applyTransaction(wct);
final TaskFragmentOrganizerState state = validateAndGetState(organizer);
state.onTransactionFinished(transactionToken);
}
}
/**
* Gets the {@link RemoteAnimationDefinition} set on the given organizer if exists. Returns
* {@code null} if it doesn't, or if the organizer has activity(ies) embedded in untrusted mode.
@@ -775,13 +838,13 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
final int organizerNum = mPendingTaskFragmentEvents.size();
for (int i = 0; i < organizerNum; i++) {
final ITaskFragmentOrganizer organizer = mTaskFragmentOrganizerState.get(
mPendingTaskFragmentEvents.keyAt(i)).mOrganizer;
dispatchPendingEvents(organizer, mPendingTaskFragmentEvents.valueAt(i));
final TaskFragmentOrganizerState state =
mTaskFragmentOrganizerState.get(mPendingTaskFragmentEvents.keyAt(i));
dispatchPendingEvents(state, mPendingTaskFragmentEvents.valueAt(i));
}
}
void dispatchPendingEvents(@NonNull ITaskFragmentOrganizer organizer,
void dispatchPendingEvents(@NonNull TaskFragmentOrganizerState state,
@NonNull List<PendingTaskFragmentEvent> pendingEvents) {
if (pendingEvents.isEmpty()) {
return;
@@ -817,7 +880,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
if (mTmpTaskSet.add(task)) {
// Make sure the organizer know about the Task config.
transaction.addChange(prepareChange(new PendingTaskFragmentEvent.Builder(
PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED, organizer)
PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED, state.mOrganizer)
.setTask(task)
.build()));
}
@@ -825,7 +888,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
transaction.addChange(prepareChange(event));
}
mTmpTaskSet.clear();
dispatchTransactionInfo(organizer, transaction);
state.dispatchTransaction(transaction);
pendingEvents.removeAll(candidateEvents);
}
@@ -855,6 +918,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
final ITaskFragmentOrganizer organizer = taskFragment.getTaskFragmentOrganizer();
final TaskFragmentOrganizerState state = validateAndGetState(organizer);
final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
// Make sure the organizer know about the Task config.
transaction.addChange(prepareChange(new PendingTaskFragmentEvent.Builder(
@@ -862,22 +926,10 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
.setTask(taskFragment.getTask())
.build()));
transaction.addChange(prepareChange(event));
dispatchTransactionInfo(event.mTaskFragmentOrg, transaction);
state.dispatchTransaction(transaction);
mPendingTaskFragmentEvents.get(organizer.asBinder()).remove(event);
}
private void dispatchTransactionInfo(@NonNull ITaskFragmentOrganizer organizer,
@NonNull TaskFragmentTransaction transaction) {
if (transaction.isEmpty()) {
return;
}
try {
organizer.onTransactionReady(transaction);
} catch (RemoteException e) {
Slog.d(TAG, "Exception sending TaskFragmentTransaction", e);
}
}
@Nullable
private TaskFragmentTransaction.Change prepareChange(
@NonNull PendingTaskFragmentEvent event) {

View File

@@ -1815,6 +1815,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
/** This undoes one call to {@link #deferTransitionReady}. */
void continueTransitionReady() {
--mReadyTracker.mDeferReadyDepth;
// Apply ready in case it is waiting for the previous defer call.
applyReady();
}
/**

View File

@@ -147,7 +147,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
mGlobalLock = atm.mGlobalLock;
mTaskOrganizerController = new TaskOrganizerController(mService);
mDisplayAreaOrganizerController = new DisplayAreaOrganizerController(mService);
mTaskFragmentOrganizerController = new TaskFragmentOrganizerController(atm);
mTaskFragmentOrganizerController = new TaskFragmentOrganizerController(atm, this);
}
void setWindowManager(WindowManagerService wms) {

View File

@@ -25,6 +25,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
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.TaskFragment.EMBEDDING_ALLOWED;
@@ -46,7 +47,6 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -65,6 +65,7 @@ import android.window.TaskFragmentCreationParams;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentOrganizer;
import android.window.TaskFragmentOrganizerToken;
import android.window.TaskFragmentTransaction;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import android.window.WindowContainerTransactionCallback;
@@ -90,6 +91,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
private TaskFragmentOrganizerController mController;
private WindowOrganizerController mWindowOrganizerController;
private TransitionController mTransitionController;
private TaskFragmentOrganizer mOrganizer;
private TaskFragmentOrganizerToken mOrganizerToken;
private ITaskFragmentOrganizer mIOrganizer;
@@ -107,9 +109,10 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
private Task mTask;
@Before
public void setup() {
public void setup() throws RemoteException {
MockitoAnnotations.initMocks(this);
mWindowOrganizerController = mAtm.mWindowOrganizerController;
mTransitionController = mWindowOrganizerController.mTransitionController;
mController = mWindowOrganizerController.mTaskFragmentOrganizerController;
mOrganizer = new TaskFragmentOrganizer(Runnable::run);
mOrganizerToken = mOrganizer.getOrganizerToken();
@@ -128,11 +131,16 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
spyOn(mController);
spyOn(mOrganizer);
spyOn(mTaskFragment);
spyOn(mWindowOrganizerController);
spyOn(mTransitionController);
doReturn(mIOrganizer).when(mTaskFragment).getTaskFragmentOrganizer();
doReturn(mTaskFragmentInfo).when(mTaskFragment).getTaskFragmentInfo();
doReturn(new SurfaceControl()).when(mTaskFragment).getSurfaceControl();
doReturn(mFragmentToken).when(mTaskFragment).getFragmentToken();
doReturn(new Configuration()).when(mTaskFragmentInfo).getConfiguration();
// To prevent it from calling the real server.
doNothing().when(mOrganizer).onTransactionHandled(any(), any());
}
@Test
@@ -866,7 +874,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
assertFalse(parentTask.shouldBeVisible(null));
// Verify the info changed callback still occurred despite the task being invisible
reset(mOrganizer);
clearInvocations(mOrganizer);
mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment);
mController.dispatchPendingEvents();
verify(mOrganizer).onTaskFragmentInfoChanged(any(), any());
@@ -899,7 +907,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
verify(mOrganizer).onTaskFragmentInfoChanged(any(), any());
// Verify the info changed callback is not called when the task is invisible
reset(mOrganizer);
clearInvocations(mOrganizer);
doReturn(false).when(task).shouldBeVisible(any());
mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment);
mController.dispatchPendingEvents();
@@ -1092,6 +1100,40 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
.that(mTaskFragment.getBounds()).isEqualTo(task.getBounds());
}
@Test
public void testOnTransactionReady_invokeOnTransactionHandled() {
mController.registerOrganizer(mIOrganizer);
final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
mOrganizer.onTransactionReady(transaction);
// Organizer should always trigger #onTransactionHandled when receives #onTransactionReady
verify(mOrganizer).onTransactionHandled(eq(transaction.getTransactionToken()), any());
verify(mOrganizer, never()).applyTransaction(any());
}
@Test
public void testDispatchTransaction_deferTransitionReady() {
mController.registerOrganizer(mIOrganizer);
setupMockParent(mTaskFragment, mTask);
final ArgumentCaptor<IBinder> tokenCaptor = ArgumentCaptor.forClass(IBinder.class);
final ArgumentCaptor<WindowContainerTransaction> wctCaptor =
ArgumentCaptor.forClass(WindowContainerTransaction.class);
doReturn(true).when(mTransitionController).isCollecting();
mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents();
// Defer transition when send TaskFragment transaction during transition collection.
verify(mTransitionController).deferTransitionReady();
verify(mOrganizer).onTransactionHandled(tokenCaptor.capture(), wctCaptor.capture());
mController.onTransactionHandled(mIOrganizer, tokenCaptor.getValue(), wctCaptor.getValue());
// Apply the organizer change and continue transition.
verify(mWindowOrganizerController).applyTransaction(wctCaptor.getValue());
verify(mTransitionController).continueTransitionReady();
}
/**
* Creates a {@link TaskFragment} with the {@link WindowContainerTransaction}. Calls
* {@link WindowOrganizerController#applyTransaction} to apply the transaction,