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; package android.window;
import android.os.IBinder;
import android.view.RemoteAnimationDefinition; import android.view.RemoteAnimationDefinition;
import android.window.ITaskFragmentOrganizer; import android.window.ITaskFragmentOrganizer;
import android.window.WindowContainerTransaction;
/** @hide */ /** @hide */
interface ITaskFragmentOrganizerController { interface ITaskFragmentOrganizerController {
@@ -50,4 +52,11 @@ interface ITaskFragmentOrganizerController {
* only occupies a portion of Task bounds. * only occupies a portion of Task bounds.
*/ */
boolean isActivityEmbedded(in IBinder activityToken); 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.CallSuper;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.TestApi; import android.annotation.TestApi;
import android.content.Intent; import android.content.Intent;
import android.content.res.Configuration; 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. * 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 * Called when the transaction is ready so that the organizer can update the TaskFragments based
* on the changes in transaction. * on the changes in transaction.
* Note: {@link WindowOrganizer#applyTransaction} permission requirement is conditional for
* {@link TaskFragmentOrganizer}.
* @see com.android.server.wm.WindowOrganizerController#enforceTaskPermission
* @hide * @hide
*/ */
@SuppressLint("AndroidFrameworkRequiresPermission")
public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) { public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) {
final WindowContainerTransaction wct = new WindowContainerTransaction(); final WindowContainerTransaction wct = new WindowContainerTransaction();
final List<TaskFragmentTransaction.Change> changes = transaction.getChanges(); final List<TaskFragmentTransaction.Change> changes = transaction.getChanges();
@@ -389,8 +406,9 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
"Unknown TaskFragmentEvent=" + change.getType()); "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 @Override

View File

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

View File

@@ -2041,6 +2041,12 @@
"group": "WM_DEBUG_CONFIGURATION", "group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityRecord.java" "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": { "-106400104": {
"message": "Preload recents with %s", "message": "Preload recents with %s",
"level": "DEBUG", "level": "DEBUG",
@@ -2089,6 +2095,12 @@
"group": "WM_DEBUG_STATES", "group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/TaskFragment.java" "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": { "-70719599": {
"message": "Unregister remote animations for organizer=%s uid=%d pid=%d", "message": "Unregister remote animations for organizer=%s uid=%d pid=%d",
"level": "VERBOSE", "level": "VERBOSE",

View File

@@ -49,7 +49,9 @@ import android.window.ITaskFragmentOrganizer;
import android.window.ITaskFragmentOrganizerController; import android.window.ITaskFragmentOrganizerController;
import android.window.TaskFragmentInfo; import android.window.TaskFragmentInfo;
import android.window.TaskFragmentTransaction; import android.window.TaskFragmentTransaction;
import android.window.WindowContainerTransaction;
import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.common.ProtoLog; import com.android.internal.protolog.common.ProtoLog;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
@@ -68,6 +70,8 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
private final ActivityTaskManagerService mAtmService; private final ActivityTaskManagerService mAtmService;
private final WindowManagerGlobalLock mGlobalLock; private final WindowManagerGlobalLock mGlobalLock;
private final WindowOrganizerController mWindowOrganizerController;
/** /**
* A Map which manages the relationship between * A Map which manages the relationship between
* {@link ITaskFragmentOrganizer} and {@link TaskFragmentOrganizerState} * {@link ITaskFragmentOrganizer} and {@link TaskFragmentOrganizerState}
@@ -82,9 +86,11 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
private final ArraySet<Task> mTmpTaskSet = new ArraySet<>(); private final ArraySet<Task> mTmpTaskSet = new ArraySet<>();
TaskFragmentOrganizerController(ActivityTaskManagerService atm) { TaskFragmentOrganizerController(@NonNull ActivityTaskManagerService atm,
mAtmService = atm; @NonNull WindowOrganizerController windowOrganizerController) {
mAtmService = requireNonNull(atm);
mGlobalLock = atm.mGlobalLock; mGlobalLock = atm.mGlobalLock;
mWindowOrganizerController = requireNonNull(windowOrganizerController);
} }
/** /**
@@ -131,6 +137,14 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
private final SparseArray<RemoteAnimationDefinition> mRemoteAnimationDefinitions = private final SparseArray<RemoteAnimationDefinition> mRemoteAnimationDefinitions =
new SparseArray<>(); 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) { TaskFragmentOrganizerState(ITaskFragmentOrganizer organizer, int pid, int uid) {
mOrganizer = organizer; mOrganizer = organizer;
mOrganizerPid = pid; mOrganizerPid = pid;
@@ -176,6 +190,10 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
taskFragment.removeImmediately(); taskFragment.removeImmediately();
mOrganizedTaskFragments.remove(taskFragment); 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*/); mOrganizer.asBinder().unlinkToDeath(this, 0 /*flags*/);
} }
@@ -320,6 +338,40 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
.setActivityIntent(activity.intent) .setActivityIntent(activity.intent)
.setActivityToken(activityToken); .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 @Nullable
@@ -336,7 +388,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
} }
@Override @Override
public void registerOrganizer(ITaskFragmentOrganizer organizer) { public void registerOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
final int pid = Binder.getCallingPid(); final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid(); final int uid = Binder.getCallingUid();
synchronized (mGlobalLock) { synchronized (mGlobalLock) {
@@ -354,7 +406,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
} }
@Override @Override
public void unregisterOrganizer(ITaskFragmentOrganizer organizer) { public void unregisterOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
validateAndGetState(organizer); validateAndGetState(organizer);
final int pid = Binder.getCallingPid(); final int pid = Binder.getCallingPid();
final long uid = Binder.getCallingUid(); final long uid = Binder.getCallingUid();
@@ -372,8 +424,8 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
} }
@Override @Override
public void registerRemoteAnimations(ITaskFragmentOrganizer organizer, int taskId, public void registerRemoteAnimations(@NonNull ITaskFragmentOrganizer organizer, int taskId,
RemoteAnimationDefinition definition) { @NonNull RemoteAnimationDefinition definition) {
final int pid = Binder.getCallingPid(); final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid(); final int uid = Binder.getCallingUid();
synchronized (mGlobalLock) { synchronized (mGlobalLock) {
@@ -398,7 +450,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
} }
@Override @Override
public void unregisterRemoteAnimations(ITaskFragmentOrganizer organizer, int taskId) { public void unregisterRemoteAnimations(@NonNull ITaskFragmentOrganizer organizer, int taskId) {
final int pid = Binder.getCallingPid(); final int pid = Binder.getCallingPid();
final long uid = Binder.getCallingUid(); final long uid = Binder.getCallingUid();
synchronized (mGlobalLock) { 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 * 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. * {@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(); final int organizerNum = mPendingTaskFragmentEvents.size();
for (int i = 0; i < organizerNum; i++) { for (int i = 0; i < organizerNum; i++) {
final ITaskFragmentOrganizer organizer = mTaskFragmentOrganizerState.get( final TaskFragmentOrganizerState state =
mPendingTaskFragmentEvents.keyAt(i)).mOrganizer; mTaskFragmentOrganizerState.get(mPendingTaskFragmentEvents.keyAt(i));
dispatchPendingEvents(organizer, mPendingTaskFragmentEvents.valueAt(i)); dispatchPendingEvents(state, mPendingTaskFragmentEvents.valueAt(i));
} }
} }
void dispatchPendingEvents(@NonNull ITaskFragmentOrganizer organizer, void dispatchPendingEvents(@NonNull TaskFragmentOrganizerState state,
@NonNull List<PendingTaskFragmentEvent> pendingEvents) { @NonNull List<PendingTaskFragmentEvent> pendingEvents) {
if (pendingEvents.isEmpty()) { if (pendingEvents.isEmpty()) {
return; return;
@@ -817,7 +880,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
if (mTmpTaskSet.add(task)) { if (mTmpTaskSet.add(task)) {
// Make sure the organizer know about the Task config. // Make sure the organizer know about the Task config.
transaction.addChange(prepareChange(new PendingTaskFragmentEvent.Builder( transaction.addChange(prepareChange(new PendingTaskFragmentEvent.Builder(
PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED, organizer) PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED, state.mOrganizer)
.setTask(task) .setTask(task)
.build())); .build()));
} }
@@ -825,7 +888,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
transaction.addChange(prepareChange(event)); transaction.addChange(prepareChange(event));
} }
mTmpTaskSet.clear(); mTmpTaskSet.clear();
dispatchTransactionInfo(organizer, transaction); state.dispatchTransaction(transaction);
pendingEvents.removeAll(candidateEvents); pendingEvents.removeAll(candidateEvents);
} }
@@ -855,6 +918,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
} }
final ITaskFragmentOrganizer organizer = taskFragment.getTaskFragmentOrganizer(); final ITaskFragmentOrganizer organizer = taskFragment.getTaskFragmentOrganizer();
final TaskFragmentOrganizerState state = validateAndGetState(organizer);
final TaskFragmentTransaction transaction = new TaskFragmentTransaction(); final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
// Make sure the organizer know about the Task config. // Make sure the organizer know about the Task config.
transaction.addChange(prepareChange(new PendingTaskFragmentEvent.Builder( transaction.addChange(prepareChange(new PendingTaskFragmentEvent.Builder(
@@ -862,22 +926,10 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
.setTask(taskFragment.getTask()) .setTask(taskFragment.getTask())
.build())); .build()));
transaction.addChange(prepareChange(event)); transaction.addChange(prepareChange(event));
dispatchTransactionInfo(event.mTaskFragmentOrg, transaction); state.dispatchTransaction(transaction);
mPendingTaskFragmentEvents.get(organizer.asBinder()).remove(event); 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 @Nullable
private TaskFragmentTransaction.Change prepareChange( private TaskFragmentTransaction.Change prepareChange(
@NonNull PendingTaskFragmentEvent event) { @NonNull PendingTaskFragmentEvent event) {

View File

@@ -1815,6 +1815,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
/** This undoes one call to {@link #deferTransitionReady}. */ /** This undoes one call to {@link #deferTransitionReady}. */
void continueTransitionReady() { void continueTransitionReady() {
--mReadyTracker.mDeferReadyDepth; --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; mGlobalLock = atm.mGlobalLock;
mTaskOrganizerController = new TaskOrganizerController(mService); mTaskOrganizerController = new TaskOrganizerController(mService);
mDisplayAreaOrganizerController = new DisplayAreaOrganizerController(mService); mDisplayAreaOrganizerController = new DisplayAreaOrganizerController(mService);
mTaskFragmentOrganizerController = new TaskFragmentOrganizerController(atm); mTaskFragmentOrganizerController = new TaskFragmentOrganizerController(atm, this);
} }
void setWindowManager(WindowManagerService wms) { 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_SET_ADJACENT_TASK_FRAGMENTS;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT; 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.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED; 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.clearInvocations;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@@ -65,6 +65,7 @@ import android.window.TaskFragmentCreationParams;
import android.window.TaskFragmentInfo; import android.window.TaskFragmentInfo;
import android.window.TaskFragmentOrganizer; import android.window.TaskFragmentOrganizer;
import android.window.TaskFragmentOrganizerToken; import android.window.TaskFragmentOrganizerToken;
import android.window.TaskFragmentTransaction;
import android.window.WindowContainerToken; import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction; import android.window.WindowContainerTransaction;
import android.window.WindowContainerTransactionCallback; import android.window.WindowContainerTransactionCallback;
@@ -90,6 +91,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
private TaskFragmentOrganizerController mController; private TaskFragmentOrganizerController mController;
private WindowOrganizerController mWindowOrganizerController; private WindowOrganizerController mWindowOrganizerController;
private TransitionController mTransitionController;
private TaskFragmentOrganizer mOrganizer; private TaskFragmentOrganizer mOrganizer;
private TaskFragmentOrganizerToken mOrganizerToken; private TaskFragmentOrganizerToken mOrganizerToken;
private ITaskFragmentOrganizer mIOrganizer; private ITaskFragmentOrganizer mIOrganizer;
@@ -107,9 +109,10 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
private Task mTask; private Task mTask;
@Before @Before
public void setup() { public void setup() throws RemoteException {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
mWindowOrganizerController = mAtm.mWindowOrganizerController; mWindowOrganizerController = mAtm.mWindowOrganizerController;
mTransitionController = mWindowOrganizerController.mTransitionController;
mController = mWindowOrganizerController.mTaskFragmentOrganizerController; mController = mWindowOrganizerController.mTaskFragmentOrganizerController;
mOrganizer = new TaskFragmentOrganizer(Runnable::run); mOrganizer = new TaskFragmentOrganizer(Runnable::run);
mOrganizerToken = mOrganizer.getOrganizerToken(); mOrganizerToken = mOrganizer.getOrganizerToken();
@@ -128,11 +131,16 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
spyOn(mController); spyOn(mController);
spyOn(mOrganizer); spyOn(mOrganizer);
spyOn(mTaskFragment); spyOn(mTaskFragment);
spyOn(mWindowOrganizerController);
spyOn(mTransitionController);
doReturn(mIOrganizer).when(mTaskFragment).getTaskFragmentOrganizer(); doReturn(mIOrganizer).when(mTaskFragment).getTaskFragmentOrganizer();
doReturn(mTaskFragmentInfo).when(mTaskFragment).getTaskFragmentInfo(); doReturn(mTaskFragmentInfo).when(mTaskFragment).getTaskFragmentInfo();
doReturn(new SurfaceControl()).when(mTaskFragment).getSurfaceControl(); doReturn(new SurfaceControl()).when(mTaskFragment).getSurfaceControl();
doReturn(mFragmentToken).when(mTaskFragment).getFragmentToken(); doReturn(mFragmentToken).when(mTaskFragment).getFragmentToken();
doReturn(new Configuration()).when(mTaskFragmentInfo).getConfiguration(); doReturn(new Configuration()).when(mTaskFragmentInfo).getConfiguration();
// To prevent it from calling the real server.
doNothing().when(mOrganizer).onTransactionHandled(any(), any());
} }
@Test @Test
@@ -866,7 +874,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
assertFalse(parentTask.shouldBeVisible(null)); assertFalse(parentTask.shouldBeVisible(null));
// Verify the info changed callback still occurred despite the task being invisible // Verify the info changed callback still occurred despite the task being invisible
reset(mOrganizer); clearInvocations(mOrganizer);
mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment); mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment);
mController.dispatchPendingEvents(); mController.dispatchPendingEvents();
verify(mOrganizer).onTaskFragmentInfoChanged(any(), any()); verify(mOrganizer).onTaskFragmentInfoChanged(any(), any());
@@ -899,7 +907,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
verify(mOrganizer).onTaskFragmentInfoChanged(any(), any()); verify(mOrganizer).onTaskFragmentInfoChanged(any(), any());
// Verify the info changed callback is not called when the task is invisible // Verify the info changed callback is not called when the task is invisible
reset(mOrganizer); clearInvocations(mOrganizer);
doReturn(false).when(task).shouldBeVisible(any()); doReturn(false).when(task).shouldBeVisible(any());
mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment); mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment);
mController.dispatchPendingEvents(); mController.dispatchPendingEvents();
@@ -1092,6 +1100,40 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
.that(mTaskFragment.getBounds()).isEqualTo(task.getBounds()); .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 * Creates a {@link TaskFragment} with the {@link WindowContainerTransaction}. Calls
* {@link WindowOrganizerController#applyTransaction} to apply the transaction, * {@link WindowOrganizerController#applyTransaction} to apply the transaction,