Merge "Add TaskFragmentOrganizer#applyTransaction with request transition" into tm-qpr-dev

This commit is contained in:
Chris Li
2022-09-01 06:59:10 +00:00
committed by Android (Google) Code Review
11 changed files with 362 additions and 212 deletions

View File

@@ -57,6 +57,12 @@ interface ITaskFragmentOrganizerController {
* Notifies the server that the organizer has finished handling the given transaction. The * Notifies the server that the organizer has finished handling the given transaction. The
* server should apply the given {@link WindowContainerTransaction} for the necessary changes. * server should apply the given {@link WindowContainerTransaction} for the necessary changes.
*/ */
void onTransactionHandled(in ITaskFragmentOrganizer organizer, in IBinder transactionToken, void onTransactionHandled(in IBinder transactionToken, in WindowContainerTransaction wct,
in WindowContainerTransaction wct); int transitionType, boolean shouldApplyIndependently);
/**
* Requests the server to apply the given {@link WindowContainerTransaction}.
*/
void applyTransaction(in WindowContainerTransaction wct, int transitionType,
boolean shouldApplyIndependently);
} }

View File

@@ -16,17 +16,25 @@
package android.window; package android.window;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENTED_TO_TASK; import static android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENTED_TO_TASK;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED; import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_ERROR; import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_ERROR;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_INFO_CHANGED; import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_INFO_CHANGED;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED; import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED; import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT;
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.TestApi; import android.annotation.TestApi;
import android.app.WindowConfiguration;
import android.content.Intent; import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.os.Bundle; import android.os.Bundle;
@@ -34,6 +42,7 @@ import android.os.IBinder;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.SparseArray; import android.util.SparseArray;
import android.view.RemoteAnimationDefinition; import android.view.RemoteAnimationDefinition;
import android.view.WindowManager;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -168,20 +177,110 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
* {@link #onTransactionReady(TaskFragmentTransaction)} * {@link #onTransactionReady(TaskFragmentTransaction)}
* @param wct {@link WindowContainerTransaction} that the server should apply for * @param wct {@link WindowContainerTransaction} that the server should apply for
* update of the transaction. * update of the transaction.
* @see com.android.server.wm.WindowOrganizerController#enforceTaskPermission for permission * @param transitionType {@link WindowManager.TransitionType} if it needs to start a
* requirement. * transition.
* @param shouldApplyIndependently If {@code true}, the {@code wct} will request a new
* transition, which will be queued until the sync engine is
* free if there is any other active sync. If {@code false},
* the {@code wct} will be directly applied to the active sync.
* @see com.android.server.wm.WindowOrganizerController#enforceTaskFragmentOrganizerPermission
* for permission enforcement.
* @hide * @hide
*/ */
public void onTransactionHandled(@NonNull IBinder transactionToken, public void onTransactionHandled(@NonNull IBinder transactionToken,
@NonNull WindowContainerTransaction wct) { @NonNull WindowContainerTransaction wct,
@WindowManager.TransitionType int transitionType, boolean shouldApplyIndependently) {
wct.setTaskFragmentOrganizer(mInterface); wct.setTaskFragmentOrganizer(mInterface);
try { try {
getController().onTransactionHandled(mInterface, transactionToken, wct); getController().onTransactionHandled(transactionToken, wct, transitionType,
shouldApplyIndependently);
} catch (RemoteException e) { } catch (RemoteException e) {
throw e.rethrowFromSystemServer(); throw e.rethrowFromSystemServer();
} }
} }
/**
* Routes to {@link ITaskFragmentOrganizerController#applyTransaction} instead of
* {@link IWindowOrganizerController#applyTransaction} for the different transition options.
*
* @see #applyTransaction(WindowContainerTransaction, int, boolean, boolean)
*/
@Override
public void applyTransaction(@NonNull WindowContainerTransaction wct) {
// TODO(b/207070762) doing so to keep CTS compatibility. Remove in the next release.
applyTransaction(wct, getTransitionType(wct), false /* shouldApplyIndependently */);
}
/**
* Requests the server to apply the given {@link WindowContainerTransaction}.
*
* @param wct {@link WindowContainerTransaction} to apply.
* @param transitionType {@link WindowManager.TransitionType} if it needs to start a
* transition.
* @param shouldApplyIndependently If {@code true}, the {@code wct} will request a new
* transition, which will be queued until the sync engine is
* free if there is any other active sync. If {@code false},
* the {@code wct} will be directly applied to the active sync.
* @see com.android.server.wm.WindowOrganizerController#enforceTaskFragmentOrganizerPermission
* for permission enforcement.
* @hide
*/
public void applyTransaction(@NonNull WindowContainerTransaction wct,
@WindowManager.TransitionType int transitionType, boolean shouldApplyIndependently) {
if (wct.isEmpty()) {
return;
}
wct.setTaskFragmentOrganizer(mInterface);
try {
getController().applyTransaction(wct, transitionType, shouldApplyIndependently);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Gets the default {@link WindowManager.TransitionType} based on the requested
* {@link WindowContainerTransaction}.
* @hide
*/
// TODO(b/207070762): let Extensions to set the transition type instead.
@WindowManager.TransitionType
public static int getTransitionType(@NonNull WindowContainerTransaction wct) {
if (wct.isEmpty()) {
return TRANSIT_NONE;
}
for (WindowContainerTransaction.Change change : wct.getChanges().values()) {
if ((change.getWindowSetMask() & WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0) {
// Treat as TRANSIT_CHANGE when there is TaskFragment resizing.
return TRANSIT_CHANGE;
}
}
boolean containsCreatingTaskFragment = false;
boolean containsDeleteTaskFragment = false;
final List<WindowContainerTransaction.HierarchyOp> ops = wct.getHierarchyOps();
for (int i = ops.size() - 1; i >= 0; i--) {
final int type = ops.get(i).getType();
if (type == HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT) {
// Treat as TRANSIT_CHANGE when there is activity reparent.
return TRANSIT_CHANGE;
}
if (type == HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT) {
containsCreatingTaskFragment = true;
} else if (type == HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT) {
containsDeleteTaskFragment = true;
}
}
if (containsCreatingTaskFragment) {
return TRANSIT_OPEN;
}
if (containsDeleteTaskFragment) {
return TRANSIT_CLOSE;
}
// Use TRANSIT_CHANGE as default.
return TRANSIT_CHANGE;
}
/** /**
* Called when a TaskFragment is created and organized by this organizer. * Called when a TaskFragment is created and organized by this organizer.
* *
@@ -424,22 +523,8 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
} }
// Notify the server, and the server should apply the WindowContainerTransaction. // Notify the server, and the server should apply the WindowContainerTransaction.
onTransactionHandled(transaction.getTransactionToken(), wct); onTransactionHandled(transaction.getTransactionToken(), wct, getTransitionType(wct),
} false /* shouldApplyIndependently */);
@Override
public void applyTransaction(@NonNull WindowContainerTransaction t) {
t.setTaskFragmentOrganizer(mInterface);
super.applyTransaction(t);
}
// Suppress the lint because it is not a registration method.
@SuppressWarnings("ExecutorRegistration")
@Override
public int applySyncTransaction(@NonNull WindowContainerTransaction t,
@NonNull WindowContainerTransactionCallback callback) {
t.setTaskFragmentOrganizer(mInterface);
return super.applySyncTransaction(t, callback);
} }
private final ITaskFragmentOrganizer mInterface = new ITaskFragmentOrganizer.Stub() { private final ITaskFragmentOrganizer mInterface = new ITaskFragmentOrganizer.Stub() {

View File

@@ -763,10 +763,8 @@ public final class WindowContainerTransaction implements Parcelable {
* @hide * @hide
*/ */
@NonNull @NonNull
WindowContainerTransaction setTaskFragmentOrganizer(@NonNull ITaskFragmentOrganizer organizer) { public WindowContainerTransaction setTaskFragmentOrganizer(
if (mTaskFragmentOrganizer != null) { @NonNull ITaskFragmentOrganizer organizer) {
throw new IllegalStateException("Can't set multiple organizers for one transaction.");
}
mTaskFragmentOrganizer = organizer; mTaskFragmentOrganizer = organizer;
return this; return this;
} }

View File

@@ -1801,6 +1801,12 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS", "group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/Transition.java" "at": "com\/android\/server\/wm\/Transition.java"
}, },
"-353495930": {
"message": "TaskFragmentTransaction changes are not collected in transition because there is an ongoing sync for applySyncTransaction().",
"level": "WARN",
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/WindowOrganizerController.java"
},
"-347866078": { "-347866078": {
"message": "Setting move animation on %s", "message": "Setting move animation on %s",
"level": "VERBOSE", "level": "VERBOSE",
@@ -2491,6 +2497,12 @@
"group": "WM_DEBUG_ANIM", "group": "WM_DEBUG_ANIM",
"at": "com\/android\/server\/wm\/WindowState.java" "at": "com\/android\/server\/wm\/WindowState.java"
}, },
"286170861": {
"message": "Creating Pending Transition for TaskFragment: %s",
"level": "VERBOSE",
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/WindowOrganizerController.java"
},
"288485303": { "288485303": {
"message": "Attempted to set remove mode to a display that does not exist: %d", "message": "Attempted to set remove mode to a display that does not exist: %d",
"level": "WARN", "level": "WARN",

View File

@@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_OP_TYPE; import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_OP_TYPE;
import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO; import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO;
import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_THROWABLE; import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_THROWABLE;
import static android.window.TaskFragmentOrganizer.getTransitionType;
import static android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENTED_TO_TASK; import static android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENTED_TO_TASK;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED; import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_ERROR; import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_ERROR;
@@ -109,7 +110,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
private Consumer<List<SplitInfo>> mEmbeddingCallback; private Consumer<List<SplitInfo>> mEmbeddingCallback;
private final List<SplitInfo> mLastReportedSplitStates = new ArrayList<>(); private final List<SplitInfo> mLastReportedSplitStates = new ArrayList<>();
private final Handler mHandler; private final Handler mHandler;
private final Object mLock = new Object(); final Object mLock = new Object();
private final ActivityStartMonitor mActivityStartMonitor; private final ActivityStartMonitor mActivityStartMonitor;
public SplitController() { public SplitController() {
@@ -209,8 +210,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
} }
} }
// Notify the server, and the server should apply the WindowContainerTransaction. // Notify the server, and the server should apply and merge the
mPresenter.onTransactionHandled(transaction.getTransactionToken(), wct); // WindowContainerTransaction to the active sync to finish the TaskFragmentTransaction.
mPresenter.onTransactionHandled(transaction.getTransactionToken(), wct,
getTransitionType(wct), false /* shouldApplyIndependently */);
updateCallbackIfNecessary(); updateCallbackIfNecessary();
} }
} }
@@ -221,6 +224,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* @param wct The {@link WindowContainerTransaction} to make any changes with if needed. * @param wct The {@link WindowContainerTransaction} to make any changes with if needed.
* @param taskFragmentInfo Info of the TaskFragment that is created. * @param taskFragmentInfo Info of the TaskFragment that is created.
*/ */
// Suppress GuardedBy warning because lint ask to mark this method as
// @GuardedBy(container.mController.mLock), which is mLock itself
@SuppressWarnings("GuardedBy")
@VisibleForTesting @VisibleForTesting
@GuardedBy("mLock") @GuardedBy("mLock")
void onTaskFragmentAppeared(@NonNull WindowContainerTransaction wct, void onTaskFragmentAppeared(@NonNull WindowContainerTransaction wct,
@@ -245,6 +251,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* @param wct The {@link WindowContainerTransaction} to make any changes with if needed. * @param wct The {@link WindowContainerTransaction} to make any changes with if needed.
* @param taskFragmentInfo Info of the TaskFragment that is changed. * @param taskFragmentInfo Info of the TaskFragment that is changed.
*/ */
// Suppress GuardedBy warning because lint ask to mark this method as
// @GuardedBy(container.mController.mLock), which is mLock itself
@SuppressWarnings("GuardedBy")
@VisibleForTesting @VisibleForTesting
@GuardedBy("mLock") @GuardedBy("mLock")
void onTaskFragmentInfoChanged(@NonNull WindowContainerTransaction wct, void onTaskFragmentInfoChanged(@NonNull WindowContainerTransaction wct,
@@ -430,6 +439,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* transaction operation. * transaction operation.
* @param exception exception from the server side. * @param exception exception from the server side.
*/ */
// Suppress GuardedBy warning because lint ask to mark this method as
// @GuardedBy(container.mController.mLock), which is mLock itself
@SuppressWarnings("GuardedBy")
@VisibleForTesting @VisibleForTesting
@GuardedBy("mLock") @GuardedBy("mLock")
void onTaskFragmentError(@NonNull WindowContainerTransaction wct, void onTaskFragmentError(@NonNull WindowContainerTransaction wct,
@@ -869,23 +881,23 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* Called when we have been waiting too long for the TaskFragment to become non-empty after * Called when we have been waiting too long for the TaskFragment to become non-empty after
* creation. * creation.
*/ */
@GuardedBy("mLock")
void onTaskFragmentAppearEmptyTimeout(@NonNull TaskFragmentContainer container) { void onTaskFragmentAppearEmptyTimeout(@NonNull TaskFragmentContainer container) {
synchronized (mLock) { final WindowContainerTransaction wct = new WindowContainerTransaction();
final WindowContainerTransaction wct = new WindowContainerTransaction(); onTaskFragmentAppearEmptyTimeout(wct, container);
onTaskFragmentAppearEmptyTimeout(wct, container); // Can be applied independently as a timeout callback.
mPresenter.applyTransaction(wct); mPresenter.applyTransaction(wct, getTransitionType(wct),
} true /* shouldApplyIndependently */);
} }
/** /**
* Called when we have been waiting too long for the TaskFragment to become non-empty after * Called when we have been waiting too long for the TaskFragment to become non-empty after
* creation. * creation.
*/ */
@GuardedBy("mLock")
void onTaskFragmentAppearEmptyTimeout(@NonNull WindowContainerTransaction wct, void onTaskFragmentAppearEmptyTimeout(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentContainer container) { @NonNull TaskFragmentContainer container) {
synchronized (mLock) { mPresenter.cleanupContainer(wct, container, false /* shouldFinishDependent */);
mPresenter.cleanupContainer(wct, container, false /* shouldFinishDependent */);
}
} }
/** /**
@@ -1714,7 +1726,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
synchronized (mLock) { synchronized (mLock) {
final WindowContainerTransaction wct = new WindowContainerTransaction(); final WindowContainerTransaction wct = new WindowContainerTransaction();
SplitController.this.onActivityCreated(wct, activity); SplitController.this.onActivityCreated(wct, activity);
mPresenter.applyTransaction(wct); // The WCT should be applied and merged to the activity launch transition.
mPresenter.applyTransaction(wct, getTransitionType(wct),
false /* shouldApplyIndependently */);
} }
} }
@@ -1723,7 +1737,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
synchronized (mLock) { synchronized (mLock) {
final WindowContainerTransaction wct = new WindowContainerTransaction(); final WindowContainerTransaction wct = new WindowContainerTransaction();
SplitController.this.onActivityConfigurationChanged(wct, activity); SplitController.this.onActivityConfigurationChanged(wct, activity);
mPresenter.applyTransaction(wct); // The WCT should be applied and merged to the Task change transition so that the
// placeholder is launched in the same transition.
mPresenter.applyTransaction(wct, getTransitionType(wct),
false /* shouldApplyIndependently */);
} }
} }
@@ -1775,7 +1792,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
final TaskFragmentContainer launchedInTaskFragment = resolveStartActivityIntent(wct, final TaskFragmentContainer launchedInTaskFragment = resolveStartActivityIntent(wct,
taskId, intent, launchingActivity); taskId, intent, launchingActivity);
if (launchedInTaskFragment != null) { if (launchedInTaskFragment != null) {
mPresenter.applyTransaction(wct); // Make sure the WCT is applied immediately instead of being queued so that the
// TaskFragment will be ready before activity attachment.
mPresenter.applyTransaction(wct, getTransitionType(wct),
false /* shouldApplyIndependently */);
// Amend the request to let the WM know that the activity should be placed in // Amend the request to let the WM know that the activity should be placed in
// the dedicated container. // the dedicated container.
options.putBinder(ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN, options.putBinder(ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN,

View File

@@ -28,6 +28,7 @@ import android.util.Size;
import android.window.TaskFragmentInfo; import android.window.TaskFragmentInfo;
import android.window.WindowContainerTransaction; import android.window.WindowContainerTransaction;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@@ -251,6 +252,7 @@ class TaskFragmentContainer {
return mInfo; return mInfo;
} }
@GuardedBy("mController.mLock")
void setInfo(@NonNull WindowContainerTransaction wct, @NonNull TaskFragmentInfo info) { void setInfo(@NonNull WindowContainerTransaction wct, @NonNull TaskFragmentInfo info) {
if (!mIsFinished && mInfo == null && info.isEmpty()) { if (!mIsFinished && mInfo == null && info.isEmpty()) {
// onTaskFragmentAppeared with empty info. We will remove the TaskFragment if no // onTaskFragmentAppeared with empty info. We will remove the TaskFragment if no
@@ -258,10 +260,12 @@ class TaskFragmentContainer {
// it is still empty after timeout. // it is still empty after timeout.
if (mPendingAppearedIntent != null || !mPendingAppearedActivities.isEmpty()) { if (mPendingAppearedIntent != null || !mPendingAppearedActivities.isEmpty()) {
mAppearEmptyTimeout = () -> { mAppearEmptyTimeout = () -> {
mAppearEmptyTimeout = null; synchronized (mController.mLock) {
// Call without the pass-in wct when timeout. We need to applyWct directly mAppearEmptyTimeout = null;
// in this case. // Call without the pass-in wct when timeout. We need to applyWct directly
mController.onTaskFragmentAppearEmptyTimeout(this); // in this case.
mController.onTaskFragmentAppearEmptyTimeout(this);
}
}; };
mController.getHandler().postDelayed(mAppearEmptyTimeout, APPEAR_EMPTY_TIMEOUT_MS); mController.getHandler().postDelayed(mAppearEmptyTimeout, APPEAR_EMPTY_TIMEOUT_MS);
} else { } else {

View File

@@ -20,7 +20,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID; import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
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.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -33,7 +32,6 @@ import static org.mockito.Mockito.never;
import android.content.Intent; import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.Point; import android.graphics.Point;
import android.os.Handler;
import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.Presubmit;
import android.window.TaskFragmentInfo; import android.window.TaskFragmentInfo;
import android.window.TaskFragmentTransaction; import android.window.TaskFragmentTransaction;
@@ -67,10 +65,7 @@ public class JetpackTaskFragmentOrganizerTest {
private WindowContainerTransaction mTransaction; private WindowContainerTransaction mTransaction;
@Mock @Mock
private JetpackTaskFragmentOrganizer.TaskFragmentCallback mCallback; private JetpackTaskFragmentOrganizer.TaskFragmentCallback mCallback;
@Mock
private SplitController mSplitController; private SplitController mSplitController;
@Mock
private Handler mHandler;
private JetpackTaskFragmentOrganizer mOrganizer; private JetpackTaskFragmentOrganizer mOrganizer;
@Before @Before
@@ -78,8 +73,9 @@ public class JetpackTaskFragmentOrganizerTest {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
mOrganizer = new JetpackTaskFragmentOrganizer(Runnable::run, mCallback); mOrganizer = new JetpackTaskFragmentOrganizer(Runnable::run, mCallback);
mOrganizer.registerOrganizer(); mOrganizer.registerOrganizer();
mSplitController = new SplitController();
spyOn(mOrganizer); spyOn(mOrganizer);
doReturn(mHandler).when(mSplitController).getHandler(); spyOn(mSplitController);
} }
@Test @Test

View File

@@ -127,7 +127,7 @@ public class SplitControllerTest {
mSplitPresenter = mSplitController.mPresenter; mSplitPresenter = mSplitController.mPresenter;
spyOn(mSplitController); spyOn(mSplitController);
spyOn(mSplitPresenter); spyOn(mSplitPresenter);
doNothing().when(mSplitPresenter).applyTransaction(any()); doNothing().when(mSplitPresenter).applyTransaction(any(), anyInt(), anyBoolean());
final Configuration activityConfig = new Configuration(); final Configuration activityConfig = new Configuration();
activityConfig.windowConfiguration.setBounds(TASK_BOUNDS); activityConfig.windowConfiguration.setBounds(TASK_BOUNDS);
activityConfig.windowConfiguration.setMaxBounds(TASK_BOUNDS); activityConfig.windowConfiguration.setMaxBounds(TASK_BOUNDS);
@@ -1000,7 +1000,8 @@ public class SplitControllerTest {
mSplitController.onTransactionReady(transaction); mSplitController.onTransactionReady(transaction);
verify(mSplitController).onTaskFragmentAppeared(any(), eq(info)); verify(mSplitController).onTaskFragmentAppeared(any(), eq(info));
verify(mSplitPresenter).onTransactionHandled(eq(transaction.getTransactionToken()), any()); verify(mSplitPresenter).onTransactionHandled(eq(transaction.getTransactionToken()), any(),
anyInt(), anyBoolean());
} }
@Test @Test
@@ -1014,7 +1015,8 @@ public class SplitControllerTest {
mSplitController.onTransactionReady(transaction); mSplitController.onTransactionReady(transaction);
verify(mSplitController).onTaskFragmentInfoChanged(any(), eq(info)); verify(mSplitController).onTaskFragmentInfoChanged(any(), eq(info));
verify(mSplitPresenter).onTransactionHandled(eq(transaction.getTransactionToken()), any()); verify(mSplitPresenter).onTransactionHandled(eq(transaction.getTransactionToken()), any(),
anyInt(), anyBoolean());
} }
@Test @Test
@@ -1028,7 +1030,8 @@ public class SplitControllerTest {
mSplitController.onTransactionReady(transaction); mSplitController.onTransactionReady(transaction);
verify(mSplitController).onTaskFragmentVanished(any(), eq(info)); verify(mSplitController).onTaskFragmentVanished(any(), eq(info));
verify(mSplitPresenter).onTransactionHandled(eq(transaction.getTransactionToken()), any()); verify(mSplitPresenter).onTransactionHandled(eq(transaction.getTransactionToken()), any(),
anyInt(), anyBoolean());
} }
@Test @Test
@@ -1043,7 +1046,8 @@ public class SplitControllerTest {
verify(mSplitController).onTaskFragmentParentInfoChanged(any(), eq(TASK_ID), verify(mSplitController).onTaskFragmentParentInfoChanged(any(), eq(TASK_ID),
eq(taskConfig)); eq(taskConfig));
verify(mSplitPresenter).onTransactionHandled(eq(transaction.getTransactionToken()), any()); verify(mSplitPresenter).onTransactionHandled(eq(transaction.getTransactionToken()), any(),
anyInt(), anyBoolean());
} }
@Test @Test
@@ -1062,7 +1066,8 @@ public class SplitControllerTest {
verify(mSplitController).onTaskFragmentError(any(), eq(errorToken), eq(info), eq(opType), verify(mSplitController).onTaskFragmentError(any(), eq(errorToken), eq(info), eq(opType),
eq(exception)); eq(exception));
verify(mSplitPresenter).onTransactionHandled(eq(transaction.getTransactionToken()), any()); verify(mSplitPresenter).onTransactionHandled(eq(transaction.getTransactionToken()), any(),
anyInt(), anyBoolean());
} }
@Test @Test
@@ -1078,7 +1083,8 @@ public class SplitControllerTest {
verify(mSplitController).onActivityReparentedToTask(any(), eq(TASK_ID), eq(intent), verify(mSplitController).onActivityReparentedToTask(any(), eq(TASK_ID), eq(intent),
eq(activityToken)); eq(activityToken));
verify(mSplitPresenter).onTransactionHandled(eq(transaction.getTransactionToken()), any()); verify(mSplitPresenter).onTransactionHandled(eq(transaction.getTransactionToken()), any(),
anyInt(), anyBoolean());
} }
/** Creates a mock activity in the organizer process. */ /** Creates a mock activity in the organizer process. */

View File

@@ -45,6 +45,7 @@ import android.util.ArraySet;
import android.util.Slog; import android.util.Slog;
import android.util.SparseArray; import android.util.SparseArray;
import android.view.RemoteAnimationDefinition; import android.view.RemoteAnimationDefinition;
import android.view.WindowManager;
import android.window.ITaskFragmentOrganizer; import android.window.ITaskFragmentOrganizer;
import android.window.ITaskFragmentOrganizerController; import android.window.ITaskFragmentOrganizerController;
import android.window.TaskFragmentInfo; import android.window.TaskFragmentInfo;
@@ -484,16 +485,31 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
} }
@Override @Override
public void onTransactionHandled(@NonNull ITaskFragmentOrganizer organizer, public void onTransactionHandled(@NonNull IBinder transactionToken,
@NonNull IBinder transactionToken, @NonNull WindowContainerTransaction wct) { @NonNull WindowContainerTransaction wct,
@WindowManager.TransitionType int transitionType, boolean shouldApplyIndependently) {
// Keep the calling identity to avoid unsecure change.
synchronized (mGlobalLock) { synchronized (mGlobalLock) {
// Keep the calling identity to avoid unsecure change. applyTransaction(wct, transitionType, shouldApplyIndependently);
mWindowOrganizerController.applyTransaction(wct); final TaskFragmentOrganizerState state = validateAndGetState(
final TaskFragmentOrganizerState state = validateAndGetState(organizer); wct.getTaskFragmentOrganizer());
state.onTransactionFinished(transactionToken); state.onTransactionFinished(transactionToken);
} }
} }
@Override
public void applyTransaction(@NonNull WindowContainerTransaction wct,
@WindowManager.TransitionType int transitionType, boolean shouldApplyIndependently) {
// Keep the calling identity to avoid unsecure change.
synchronized (mGlobalLock) {
if (wct.isEmpty()) {
return;
}
mWindowOrganizerController.applyTaskFragmentTransactionLocked(wct, transitionType,
shouldApplyIndependently);
}
}
/** /**
* 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.

View File

@@ -75,6 +75,7 @@ import android.util.ArraySet;
import android.util.Slog; import android.util.Slog;
import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationAdapter;
import android.view.SurfaceControl; import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.IDisplayAreaOrganizerController; import android.window.IDisplayAreaOrganizerController;
import android.window.ITaskFragmentOrganizer; import android.window.ITaskFragmentOrganizer;
import android.window.ITaskFragmentOrganizerController; import android.window.ITaskFragmentOrganizerController;
@@ -177,7 +178,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
if (t == null) { if (t == null) {
throw new IllegalArgumentException("Null transaction passed to applyTransaction"); throw new IllegalArgumentException("Null transaction passed to applyTransaction");
} }
enforceTaskPermission("applyTransaction()", t); enforceTaskPermission("applyTransaction()");
final CallerInfo caller = new CallerInfo(); final CallerInfo caller = new CallerInfo();
final long ident = Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity();
try { try {
@@ -195,7 +196,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
if (t == null) { if (t == null) {
throw new IllegalArgumentException("Null transaction passed to applySyncTransaction"); throw new IllegalArgumentException("Null transaction passed to applySyncTransaction");
} }
enforceTaskPermission("applySyncTransaction()", t); enforceTaskPermission("applySyncTransaction()");
final CallerInfo caller = new CallerInfo(); final CallerInfo caller = new CallerInfo();
final long ident = Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity();
try { try {
@@ -374,6 +375,87 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
} }
} }
/**
* Applies the {@link WindowContainerTransaction} as a request from
* {@link android.window.TaskFragmentOrganizer}.
*
* @param wct {@link WindowContainerTransaction} to apply.
* @param type {@link WindowManager.TransitionType} if it needs to start a new transition.
* @param shouldApplyIndependently If {@code true}, the {@code wct} will request a new
* transition, which will be queued until the sync engine is
* free if there is any other active sync. If {@code false},
* the {@code wct} will be directly applied to the active sync.
*/
void applyTaskFragmentTransactionLocked(@NonNull WindowContainerTransaction wct,
@WindowManager.TransitionType int type, boolean shouldApplyIndependently) {
if (!isValidTransaction(wct)) {
return;
}
enforceTaskFragmentOrganizerPermission("applyTaskFragmentTransaction()",
Objects.requireNonNull(wct.getTaskFragmentOrganizer()),
Objects.requireNonNull(wct));
final CallerInfo caller = new CallerInfo();
final long ident = Binder.clearCallingIdentity();
try {
if (mTransitionController.getTransitionPlayer() == null) {
// No need to worry about transition when Shell transition is not enabled.
applyTransaction(wct, -1 /* syncId */, null /* transition */, caller);
return;
}
if (!mService.mWindowManager.mSyncEngine.hasActiveSync()) {
// Sync is for either transition or applySyncTransaction(). We don't support
// multiple sync at the same time because it may cause conflict.
// Create a new transition when there is no active sync to collect the changes.
final Transition transition = mTransitionController.createTransition(type);
applyTransaction(wct, -1 /* syncId */, transition, caller);
mTransitionController.requestStartTransition(transition, null /* startTask */,
null /* remoteTransition */, null /* displayChange */);
return;
}
if (!shouldApplyIndependently) {
// Although there is an active sync, we want to apply the transaction now.
if (!mTransitionController.isCollecting()) {
// This should rarely happen, and we should try to avoid using
// {@link #applySyncTransaction} with Shell transition.
// We still want to apply and merge the transaction to the active sync
// because {@code shouldApplyIndependently} is {@code false}.
ProtoLog.w(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"TaskFragmentTransaction changes are not collected in transition"
+ " because there is an ongoing sync for"
+ " applySyncTransaction().");
}
// TODO(b/207070762) make sure changes are all collected.
applyTransaction(wct, -1 /* syncId */, null /* transition */, caller);
return;
}
// It is ok to queue the WCT until the sync engine is free.
final Transition nextTransition = new Transition(type, 0 /* flags */,
mTransitionController, mService.mWindowManager.mSyncEngine);
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Creating Pending Transition for TaskFragment: %s", nextTransition);
mService.mWindowManager.mSyncEngine.queueSyncSet(
// Make sure to collect immediately to prevent another transition
// from sneaking in before it. Note: moveToCollecting internally
// calls startSyncSet.
() -> mTransitionController.moveToCollecting(nextTransition),
() -> {
if (isValidTransaction(wct)) {
applyTransaction(wct, -1 /*syncId*/, nextTransition, caller);
mTransitionController.requestStartTransition(nextTransition,
null /* startTask */, null /* remoteTransition */,
null /* displayChange */);
} else {
nextTransition.abort();
}
});
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void applyTransaction(@NonNull WindowContainerTransaction t, int syncId, private void applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
@Nullable Transition transition, @NonNull CallerInfo caller) { @Nullable Transition transition, @NonNull CallerInfo caller) {
applyTransaction(t, syncId, transition, caller, null /* finishTransition */); applyTransaction(t, syncId, transition, caller, null /* finishTransition */);
@@ -388,12 +470,6 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
private void applyTransaction(@NonNull WindowContainerTransaction t, int syncId, private void applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
@Nullable Transition transition, @NonNull CallerInfo caller, @Nullable Transition transition, @NonNull CallerInfo caller,
@Nullable Transition finishTransition) { @Nullable Transition finishTransition) {
if (t.getTaskFragmentOrganizer() != null && !mTaskFragmentOrganizerController
.isOrganizerRegistered(t.getTaskFragmentOrganizer())) {
Slog.e(TAG, "Caller organizer=" + t.getTaskFragmentOrganizer()
+ " is no longer registered");
return;
}
int effects = 0; int effects = 0;
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId); ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId);
mService.deferWindowLayout(); mService.deferWindowLayout();
@@ -1495,25 +1571,24 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
mService.enforceTaskPermission(func); mService.enforceTaskPermission(func);
} }
private void enforceTaskPermission(String func, @Nullable WindowContainerTransaction t) { private boolean isValidTransaction(@NonNull WindowContainerTransaction t) {
if (t == null || t.getTaskFragmentOrganizer() == null) { if (t.getTaskFragmentOrganizer() != null && !mTaskFragmentOrganizerController
enforceTaskPermission(func); .isOrganizerRegistered(t.getTaskFragmentOrganizer())) {
return; // Transaction from an unregistered organizer should not be applied. This can happen
// when the organizer process died before the transaction is applied.
Slog.e(TAG, "Caller organizer=" + t.getTaskFragmentOrganizer()
+ " is no longer registered");
return false;
} }
return true;
// Apps may not have the permission to manage Tasks, but we are allowing apps to manage
// TaskFragments belonging to their own Task.
enforceOperationsAllowedForTaskFragmentOrganizer(func, t);
} }
/** /**
* Makes sure that the transaction only contains operations that are allowed for the * Makes sure that the transaction only contains operations that are allowed for the
* {@link WindowContainerTransaction#getTaskFragmentOrganizer()}. * {@link WindowContainerTransaction#getTaskFragmentOrganizer()}.
*/ */
private void enforceOperationsAllowedForTaskFragmentOrganizer( private void enforceTaskFragmentOrganizerPermission(@NonNull String func,
String func, WindowContainerTransaction t) { @NonNull ITaskFragmentOrganizer organizer, @NonNull WindowContainerTransaction t) {
final ITaskFragmentOrganizer organizer = t.getTaskFragmentOrganizer();
// Configuration changes // Configuration changes
final Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries = final Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
t.getChanges().entrySet().iterator(); t.getChanges().entrySet().iterator();

View File

@@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_OP_TYPE; import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_OP_TYPE;
import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_THROWABLE; import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_THROWABLE;
import static android.window.TaskFragmentOrganizer.getTransitionType;
import static android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENTED_TO_TASK; import static android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENTED_TO_TASK;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED; import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_ERROR; import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_ERROR;
@@ -49,8 +50,8 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.clearInvocations;
@@ -78,7 +79,6 @@ import android.window.TaskFragmentOrganizerToken;
import android.window.TaskFragmentTransaction; 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 androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
@@ -136,6 +136,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
mTaskFragment = mTaskFragment =
new TaskFragment(mAtm, mFragmentToken, true /* createdByOrganizer */); new TaskFragment(mAtm, mFragmentToken, true /* createdByOrganizer */);
mTransaction = new WindowContainerTransaction(); mTransaction = new WindowContainerTransaction();
mTransaction.setTaskFragmentOrganizer(mIOrganizer);
mFragmentWindowToken = mTaskFragment.mRemoteToken.toWindowContainerToken(); mFragmentWindowToken = mTaskFragment.mRemoteToken.toWindowContainerToken();
mDefinition = new RemoteAnimationDefinition(); mDefinition = new RemoteAnimationDefinition();
mErrorToken = new Binder(); mErrorToken = new Binder();
@@ -155,11 +156,16 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
doReturn(new Configuration()).when(mTaskFragmentInfo).getConfiguration(); doReturn(new Configuration()).when(mTaskFragmentInfo).getConfiguration();
// To prevent it from calling the real server. // To prevent it from calling the real server.
doNothing().when(mOrganizer).onTransactionHandled(any(), any()); doNothing().when(mOrganizer).applyTransaction(any(), anyInt(), anyBoolean());
doNothing().when(mOrganizer).onTransactionHandled(any(), any(), anyInt(), anyBoolean());
mController.registerOrganizer(mIOrganizer);
} }
@Test @Test
public void testCallTaskFragmentCallbackWithoutRegister_throwsException() { public void testCallTaskFragmentCallbackWithoutRegister_throwsException() {
mController.unregisterOrganizer(mIOrganizer);
doReturn(mTask).when(mTaskFragment).getTask(); doReturn(mTask).when(mTaskFragment).getTask();
assertThrows(IllegalArgumentException.class, () -> mController assertThrows(IllegalArgumentException.class, () -> mController
@@ -175,8 +181,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
@Test @Test
public void testOnTaskFragmentAppeared() { public void testOnTaskFragmentAppeared() {
mController.registerOrganizer(mIOrganizer);
// No-op when the TaskFragment is not attached. // No-op when the TaskFragment is not attached.
mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents(); mController.dispatchPendingEvents();
@@ -194,7 +198,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
@Test @Test
public void testOnTaskFragmentInfoChanged() { public void testOnTaskFragmentInfoChanged() {
mController.registerOrganizer(mIOrganizer);
setupMockParent(mTaskFragment, mTask); setupMockParent(mTaskFragment, mTask);
// No-op if onTaskFragmentAppeared is not called yet. // No-op if onTaskFragmentAppeared is not called yet.
@@ -233,8 +236,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
@Test @Test
public void testOnTaskFragmentVanished() { public void testOnTaskFragmentVanished() {
mController.registerOrganizer(mIOrganizer);
mTaskFragment.mTaskFragmentAppearedSent = true; mTaskFragment.mTaskFragmentAppearedSent = true;
mController.onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); mController.onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents(); mController.dispatchPendingEvents();
@@ -244,7 +245,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
@Test @Test
public void testOnTaskFragmentVanished_clearUpRemaining() { public void testOnTaskFragmentVanished_clearUpRemaining() {
mController.registerOrganizer(mIOrganizer);
setupMockParent(mTaskFragment, mTask); setupMockParent(mTaskFragment, mTask);
// Not trigger onTaskFragmentAppeared. // Not trigger onTaskFragmentAppeared.
@@ -270,7 +270,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
@Test @Test
public void testOnTaskFragmentParentInfoChanged() { public void testOnTaskFragmentParentInfoChanged() {
mController.registerOrganizer(mIOrganizer);
setupMockParent(mTaskFragment, mTask); setupMockParent(mTaskFragment, mTask);
mTask.getConfiguration().smallestScreenWidthDp = 10; mTask.getConfiguration().smallestScreenWidthDp = 10;
@@ -317,7 +316,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
public void testOnTaskFragmentError() { public void testOnTaskFragmentError() {
final Throwable exception = new IllegalArgumentException("Test exception"); final Throwable exception = new IllegalArgumentException("Test exception");
mController.registerOrganizer(mIOrganizer);
mController.onTaskFragmentError(mTaskFragment.getTaskFragmentOrganizer(), mController.onTaskFragmentError(mTaskFragment.getTaskFragmentOrganizer(),
mErrorToken, null /* taskFragment */, HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS, mErrorToken, null /* taskFragment */, HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS,
exception); exception);
@@ -332,7 +330,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// Make sure the activity pid/uid is the same as the organizer caller. // Make sure the activity pid/uid is the same as the organizer caller.
final int pid = Binder.getCallingPid(); final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid(); final int uid = Binder.getCallingUid();
mController.registerOrganizer(mIOrganizer);
final ActivityRecord activity = createActivityRecord(mDisplayContent); final ActivityRecord activity = createActivityRecord(mDisplayContent);
final Task task = activity.getTask(); final Task task = activity.getTask();
activity.info.applicationInfo.uid = uid; activity.info.applicationInfo.uid = uid;
@@ -375,8 +372,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
mTaskFragment.setTaskFragmentOrganizer(mOrganizer.getOrganizerToken(), uid, mTaskFragment.setTaskFragmentOrganizer(mOrganizer.getOrganizerToken(), uid,
DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME); DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME);
mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment); mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment);
mController.registerOrganizer(mIOrganizer);
mOrganizer.applyTransaction(mTransaction);
final Task task = createTask(mDisplayContent); final Task task = createTask(mDisplayContent);
task.addChild(mTaskFragment, POSITION_TOP); task.addChild(mTaskFragment, POSITION_TOP);
final ActivityRecord activity = createActivityRecord(task); final ActivityRecord activity = createActivityRecord(task);
@@ -404,7 +399,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
assertEquals(activity.intent, change.getActivityIntent()); assertEquals(activity.intent, change.getActivityIntent());
assertNotEquals(activity.token, change.getActivityToken()); assertNotEquals(activity.token, change.getActivityToken());
mTransaction.reparentActivityToTaskFragment(mFragmentToken, change.getActivityToken()); mTransaction.reparentActivityToTaskFragment(mFragmentToken, change.getActivityToken());
mWindowOrganizerController.applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
assertEquals(mTaskFragment, activity.getTaskFragment()); assertEquals(mTaskFragment, activity.getTaskFragment());
// The temporary token can only be used once. // The temporary token can only be used once.
@@ -414,7 +409,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
@Test @Test
public void testRegisterRemoteAnimations() { public void testRegisterRemoteAnimations() {
mController.registerOrganizer(mIOrganizer);
mController.registerRemoteAnimations(mIOrganizer, TASK_ID, mDefinition); mController.registerRemoteAnimations(mIOrganizer, TASK_ID, mDefinition);
assertEquals(mDefinition, mController.getRemoteAnimationDefinition(mIOrganizer, TASK_ID)); assertEquals(mDefinition, mController.getRemoteAnimationDefinition(mIOrganizer, TASK_ID));
@@ -425,23 +419,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
} }
@Test @Test
public void testWindowContainerTransaction_setTaskFragmentOrganizer() { public void testApplyTransaction_enforceConfigurationChangeOnOrganizedTaskFragment() {
mOrganizer.applyTransaction(mTransaction);
assertEquals(mIOrganizer, mTransaction.getTaskFragmentOrganizer());
mTransaction = new WindowContainerTransaction();
mOrganizer.applySyncTransaction(
mTransaction, mock(WindowContainerTransactionCallback.class));
assertEquals(mIOrganizer, mTransaction.getTaskFragmentOrganizer());
}
@Test
public void testApplyTransaction_enforceConfigurationChangeOnOrganizedTaskFragment()
throws RemoteException {
mOrganizer.applyTransaction(mTransaction);
// Throw exception if the transaction is trying to change a window that is not organized by // Throw exception if the transaction is trying to change a window that is not organized by
// the organizer. // the organizer.
mTransaction.setBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100)); mTransaction.setBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100));
@@ -457,10 +435,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
@Test @Test
public void testApplyTransaction_enforceHierarchyChange_deleteTaskFragment() public void testApplyTransaction_enforceHierarchyChange_deleteTaskFragment() {
throws RemoteException {
mController.registerOrganizer(mIOrganizer);
mOrganizer.applyTransaction(mTransaction);
doReturn(true).when(mTaskFragment).isAttached(); doReturn(true).when(mTaskFragment).isAttached();
// Throw exception if the transaction is trying to change a window that is not organized by // Throw exception if the transaction is trying to change a window that is not organized by
@@ -486,13 +461,10 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
} }
@Test @Test
public void testApplyTransaction_enforceHierarchyChange_setAdjacentRoots() public void testApplyTransaction_enforceHierarchyChange_setAdjacentRoots() {
throws RemoteException {
mAtm.mTaskFragmentOrganizerController.registerOrganizer(mIOrganizer);
final TaskFragment taskFragment2 = final TaskFragment taskFragment2 =
new TaskFragment(mAtm, new Binder(), true /* createdByOrganizer */); new TaskFragment(mAtm, new Binder(), true /* createdByOrganizer */);
final WindowContainerToken token2 = taskFragment2.mRemoteToken.toWindowContainerToken(); final WindowContainerToken token2 = taskFragment2.mRemoteToken.toWindowContainerToken();
mOrganizer.applyTransaction(mTransaction);
// Throw exception if the transaction is trying to change a window that is not organized by // Throw exception if the transaction is trying to change a window that is not organized by
// the organizer. // the organizer.
@@ -513,9 +485,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
} }
@Test @Test
public void testApplyTransaction_enforceHierarchyChange_createTaskFragment() public void testApplyTransaction_enforceHierarchyChange_createTaskFragment() {
throws RemoteException {
mController.registerOrganizer(mIOrganizer);
final ActivityRecord ownerActivity = createActivityRecord(mDisplayContent); final ActivityRecord ownerActivity = createActivityRecord(mDisplayContent);
final IBinder fragmentToken = new Binder(); final IBinder fragmentToken = new Binder();
@@ -526,11 +496,10 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
mTransaction.reparentActivityToTaskFragment(mFragmentToken, mock(IBinder.class)); mTransaction.reparentActivityToTaskFragment(mFragmentToken, mock(IBinder.class));
mTransaction.setAdjacentTaskFragments(mFragmentToken, mock(IBinder.class), mTransaction.setAdjacentTaskFragments(mFragmentToken, mock(IBinder.class),
null /* options */); null /* options */);
mAtm.getWindowOrganizerController().applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
// Successfully created a TaskFragment. // Successfully created a TaskFragment.
final TaskFragment taskFragment = mAtm.mWindowOrganizerController final TaskFragment taskFragment = mWindowOrganizerController.getTaskFragment(fragmentToken);
.getTaskFragment(fragmentToken);
assertNotNull(taskFragment); assertNotNull(taskFragment);
assertEquals(ownerActivity.getTask(), taskFragment.getTask()); assertEquals(ownerActivity.getTask(), taskFragment.getTask());
} }
@@ -539,7 +508,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
public void testApplyTransaction_enforceTaskFragmentOrganized_startActivityInTaskFragment() { public void testApplyTransaction_enforceTaskFragmentOrganized_startActivityInTaskFragment() {
final Task task = createTask(mDisplayContent); final Task task = createTask(mDisplayContent);
final ActivityRecord ownerActivity = createActivityRecord(task); final ActivityRecord ownerActivity = createActivityRecord(task);
mController.registerOrganizer(mIOrganizer);
mTaskFragment = new TaskFragmentBuilder(mAtm) mTaskFragment = new TaskFragmentBuilder(mAtm)
.setParentTask(task) .setParentTask(task)
.setFragmentToken(mFragmentToken) .setFragmentToken(mFragmentToken)
@@ -562,7 +530,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
public void testApplyTransaction_enforceTaskFragmentOrganized_reparentActivityInTaskFragment() { public void testApplyTransaction_enforceTaskFragmentOrganized_reparentActivityInTaskFragment() {
final Task task = createTask(mDisplayContent); final Task task = createTask(mDisplayContent);
final ActivityRecord activity = createActivityRecord(task); final ActivityRecord activity = createActivityRecord(task);
mController.registerOrganizer(mIOrganizer);
mTaskFragment = new TaskFragmentBuilder(mAtm) mTaskFragment = new TaskFragmentBuilder(mAtm)
.setParentTask(task) .setParentTask(task)
.setFragmentToken(mFragmentToken) .setFragmentToken(mFragmentToken)
@@ -583,7 +550,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
@Test @Test
public void testApplyTransaction_enforceTaskFragmentOrganized_setAdjacentTaskFragments() { public void testApplyTransaction_enforceTaskFragmentOrganized_setAdjacentTaskFragments() {
final Task task = createTask(mDisplayContent); final Task task = createTask(mDisplayContent);
mController.registerOrganizer(mIOrganizer);
mTaskFragment = new TaskFragmentBuilder(mAtm) mTaskFragment = new TaskFragmentBuilder(mAtm)
.setParentTask(task) .setParentTask(task)
.setFragmentToken(mFragmentToken) .setFragmentToken(mFragmentToken)
@@ -623,7 +589,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
@Test @Test
public void testApplyTransaction_enforceTaskFragmentOrganized_requestFocusOnTaskFragment() { public void testApplyTransaction_enforceTaskFragmentOrganized_requestFocusOnTaskFragment() {
final Task task = createTask(mDisplayContent); final Task task = createTask(mDisplayContent);
mController.registerOrganizer(mIOrganizer);
mTaskFragment = new TaskFragmentBuilder(mAtm) mTaskFragment = new TaskFragmentBuilder(mAtm)
.setParentTask(task) .setParentTask(task)
.setFragmentToken(mFragmentToken) .setFragmentToken(mFragmentToken)
@@ -642,44 +607,38 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
} }
@Test @Test
public void testApplyTransaction_createTaskFragment_failForDifferentUid() public void testApplyTransaction_createTaskFragment_failForDifferentUid() {
throws RemoteException {
mController.registerOrganizer(mIOrganizer);
final ActivityRecord activity = createActivityRecord(mDisplayContent); final ActivityRecord activity = createActivityRecord(mDisplayContent);
final int uid = Binder.getCallingUid(); final int uid = Binder.getCallingUid();
final IBinder fragmentToken = new Binder(); final IBinder fragmentToken = new Binder();
final TaskFragmentCreationParams params = new TaskFragmentCreationParams.Builder( final TaskFragmentCreationParams params = new TaskFragmentCreationParams.Builder(
mOrganizerToken, fragmentToken, activity.token).build(); mOrganizerToken, fragmentToken, activity.token).build();
mOrganizer.applyTransaction(mTransaction);
mTransaction.createTaskFragment(params); mTransaction.createTaskFragment(params);
// Fail to create TaskFragment when the task uid is different from caller. // Fail to create TaskFragment when the task uid is different from caller.
activity.info.applicationInfo.uid = uid; activity.info.applicationInfo.uid = uid;
activity.getTask().effectiveUid = uid + 1; activity.getTask().effectiveUid = uid + 1;
mWindowOrganizerController.applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
assertNull(mWindowOrganizerController.getTaskFragment(fragmentToken)); assertNull(mWindowOrganizerController.getTaskFragment(fragmentToken));
// Fail to create TaskFragment when the task uid is different from owner activity. // Fail to create TaskFragment when the task uid is different from owner activity.
activity.info.applicationInfo.uid = uid + 1; activity.info.applicationInfo.uid = uid + 1;
activity.getTask().effectiveUid = uid; activity.getTask().effectiveUid = uid;
mWindowOrganizerController.applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
assertNull(mWindowOrganizerController.getTaskFragment(fragmentToken)); assertNull(mWindowOrganizerController.getTaskFragment(fragmentToken));
// Successfully created a TaskFragment for same uid. // Successfully created a TaskFragment for same uid.
activity.info.applicationInfo.uid = uid; activity.info.applicationInfo.uid = uid;
activity.getTask().effectiveUid = uid; activity.getTask().effectiveUid = uid;
mWindowOrganizerController.applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
assertNotNull(mWindowOrganizerController.getTaskFragment(fragmentToken)); assertNotNull(mWindowOrganizerController.getTaskFragment(fragmentToken));
} }
@Test @Test
public void testApplyTransaction_enforceHierarchyChange_reparentChildren() public void testApplyTransaction_enforceHierarchyChange_reparentChildren() {
throws RemoteException {
mOrganizer.applyTransaction(mTransaction);
mController.registerOrganizer(mIOrganizer);
doReturn(true).when(mTaskFragment).isAttached(); doReturn(true).when(mTaskFragment).isAttached();
// Throw exception if the transaction is trying to change a window that is not organized by // Throw exception if the transaction is trying to change a window that is not organized by
@@ -699,14 +658,12 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
} }
@Test @Test
public void testApplyTransaction_reparentActivityToTaskFragment_triggerLifecycleUpdate() public void testApplyTransaction_reparentActivityToTaskFragment_triggerLifecycleUpdate() {
throws RemoteException {
final Task task = createTask(mDisplayContent); final Task task = createTask(mDisplayContent);
final ActivityRecord activity = createActivityRecord(task); final ActivityRecord activity = createActivityRecord(task);
// Skip manipulate the SurfaceControl. // Skip manipulate the SurfaceControl.
doNothing().when(activity).setDropInputMode(anyInt()); doNothing().when(activity).setDropInputMode(anyInt());
mOrganizer.applyTransaction(mTransaction); mOrganizer.applyTransaction(mTransaction);
mController.registerOrganizer(mIOrganizer);
mTaskFragment = new TaskFragmentBuilder(mAtm) mTaskFragment = new TaskFragmentBuilder(mAtm)
.setParentTask(task) .setParentTask(task)
.setFragmentToken(mFragmentToken) .setFragmentToken(mFragmentToken)
@@ -717,15 +674,13 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
doReturn(EMBEDDING_ALLOWED).when(mTaskFragment).isAllowedToEmbedActivity(activity); doReturn(EMBEDDING_ALLOWED).when(mTaskFragment).isAllowedToEmbedActivity(activity);
clearInvocations(mAtm.mRootWindowContainer); clearInvocations(mAtm.mRootWindowContainer);
mAtm.getWindowOrganizerController().applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities(); verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities();
} }
@Test @Test
public void testApplyTransaction_requestFocusOnTaskFragment() { public void testApplyTransaction_requestFocusOnTaskFragment() {
mOrganizer.applyTransaction(mTransaction);
mController.registerOrganizer(mIOrganizer);
final Task task = createTask(mDisplayContent); final Task task = createTask(mDisplayContent);
final IBinder token0 = new Binder(); final IBinder token0 = new Binder();
final TaskFragment tf0 = new TaskFragmentBuilder(mAtm) final TaskFragment tf0 = new TaskFragmentBuilder(mAtm)
@@ -750,7 +705,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
final ActivityRecord activityInOtherTask = createActivityRecord(mDefaultDisplay); final ActivityRecord activityInOtherTask = createActivityRecord(mDefaultDisplay);
mDisplayContent.setFocusedApp(activityInOtherTask); mDisplayContent.setFocusedApp(activityInOtherTask);
mTransaction.requestFocusOnTaskFragment(token0); mTransaction.requestFocusOnTaskFragment(token0);
mWindowOrganizerController.applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
assertEquals(activityInOtherTask, mDisplayContent.mFocusedApp); assertEquals(activityInOtherTask, mDisplayContent.mFocusedApp);
@@ -758,7 +713,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
activity0.setState(ActivityRecord.State.PAUSED, "test"); activity0.setState(ActivityRecord.State.PAUSED, "test");
activity1.setState(ActivityRecord.State.RESUMED, "test"); activity1.setState(ActivityRecord.State.RESUMED, "test");
mDisplayContent.setFocusedApp(activity1); mDisplayContent.setFocusedApp(activity1);
mWindowOrganizerController.applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
assertEquals(activity1, mDisplayContent.mFocusedApp); assertEquals(activity1, mDisplayContent.mFocusedApp);
@@ -766,28 +721,29 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// has a resumed activity. // has a resumed activity.
activity0.setState(ActivityRecord.State.RESUMED, "test"); activity0.setState(ActivityRecord.State.RESUMED, "test");
mDisplayContent.setFocusedApp(activity1); mDisplayContent.setFocusedApp(activity1);
mWindowOrganizerController.applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
assertEquals(activity0, mDisplayContent.mFocusedApp); assertEquals(activity0, mDisplayContent.mFocusedApp);
} }
@Test @Test
public void testApplyTransaction_skipTransactionForUnregisterOrganizer() { public void testApplyTransaction_skipTransactionForUnregisterOrganizer() {
mController.unregisterOrganizer(mIOrganizer);
final ActivityRecord ownerActivity = createActivityRecord(mDisplayContent); final ActivityRecord ownerActivity = createActivityRecord(mDisplayContent);
final IBinder fragmentToken = new Binder(); final IBinder fragmentToken = new Binder();
// Allow organizer to create TaskFragment and start/reparent activity to TaskFragment. // Allow organizer to create TaskFragment and start/reparent activity to TaskFragment.
createTaskFragmentFromOrganizer(mTransaction, ownerActivity, fragmentToken); createTaskFragmentFromOrganizer(mTransaction, ownerActivity, fragmentToken);
mAtm.mWindowOrganizerController.applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
// Nothing should happen as the organizer is not registered. // Nothing should happen as the organizer is not registered.
assertNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken)); assertNull(mWindowOrganizerController.getTaskFragment(fragmentToken));
mController.registerOrganizer(mIOrganizer); mController.registerOrganizer(mIOrganizer);
mAtm.mWindowOrganizerController.applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
// Successfully created when the organizer is registered. // Successfully created when the organizer is registered.
assertNotNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken)); assertNotNull(mWindowOrganizerController.getTaskFragment(fragmentToken));
} }
@Test @Test
@@ -799,13 +755,13 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// Not allow to start activity in a TaskFragment that is in a PIP Task. // Not allow to start activity in a TaskFragment that is in a PIP Task.
mTransaction.startActivityInTaskFragment( mTransaction.startActivityInTaskFragment(
mFragmentToken, activity.token, new Intent(), null /* activityOptions */) mFragmentToken, activity.token, new Intent(), null /* activityOptions */)
.setErrorCallbackToken(mErrorToken); .setErrorCallbackToken(mErrorToken);
mWindowOrganizerController.applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
verify(mAtm.getActivityStartController(), never()).startActivityInTaskFragment(any(), any(), verify(mAtm.getActivityStartController(), never()).startActivityInTaskFragment(any(), any(),
any(), any(), anyInt(), anyInt(), any()); any(), any(), anyInt(), anyInt(), any());
verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), verify(mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
eq(mErrorToken), eq(mTaskFragment), eq(mErrorToken), eq(mTaskFragment),
eq(HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT), eq(HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT),
any(IllegalArgumentException.class)); any(IllegalArgumentException.class));
@@ -820,7 +776,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// Not allow to reparent activity to a TaskFragment that is in a PIP Task. // Not allow to reparent activity to a TaskFragment that is in a PIP Task.
mTransaction.reparentActivityToTaskFragment(mFragmentToken, activity.token) mTransaction.reparentActivityToTaskFragment(mFragmentToken, activity.token)
.setErrorCallbackToken(mErrorToken); .setErrorCallbackToken(mErrorToken);
mWindowOrganizerController.applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
verify(mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), verify(mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
eq(mErrorToken), eq(mTaskFragment), eq(mErrorToken), eq(mTaskFragment),
@@ -836,9 +792,9 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// Not allow to set adjacent on a TaskFragment that is in a PIP Task. // Not allow to set adjacent on a TaskFragment that is in a PIP Task.
mTransaction.setAdjacentTaskFragments(mFragmentToken, null /* fragmentToken2 */, mTransaction.setAdjacentTaskFragments(mFragmentToken, null /* fragmentToken2 */,
null /* options */) null /* options */)
.setErrorCallbackToken(mErrorToken); .setErrorCallbackToken(mErrorToken);
mWindowOrganizerController.applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
verify(mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), verify(mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
eq(mErrorToken), eq(mTaskFragment), eq(mErrorToken), eq(mTaskFragment),
@@ -849,7 +805,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
@Test @Test
public void testTaskFragmentInPip_createTaskFragment() { public void testTaskFragmentInPip_createTaskFragment() {
mController.registerOrganizer(mIOrganizer);
final Task pipTask = createTask(mDisplayContent, WINDOWING_MODE_PINNED, final Task pipTask = createTask(mDisplayContent, WINDOWING_MODE_PINNED,
ACTIVITY_TYPE_STANDARD); ACTIVITY_TYPE_STANDARD);
final ActivityRecord activity = createActivityRecord(pipTask); final ActivityRecord activity = createActivityRecord(pipTask);
@@ -859,7 +814,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// Not allow to create TaskFragment in a PIP Task. // Not allow to create TaskFragment in a PIP Task.
createTaskFragmentFromOrganizer(mTransaction, activity, fragmentToken); createTaskFragmentFromOrganizer(mTransaction, activity, fragmentToken);
mTransaction.setErrorCallbackToken(mErrorToken); mTransaction.setErrorCallbackToken(mErrorToken);
mWindowOrganizerController.applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
verify(mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), verify(mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
eq(mErrorToken), eq(null), eq(HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT), eq(mErrorToken), eq(null), eq(HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT),
@@ -875,7 +830,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// Not allow to delete a TaskFragment that is in a PIP Task. // Not allow to delete a TaskFragment that is in a PIP Task.
mTransaction.deleteTaskFragment(mFragmentWindowToken) mTransaction.deleteTaskFragment(mFragmentWindowToken)
.setErrorCallbackToken(mErrorToken); .setErrorCallbackToken(mErrorToken);
mWindowOrganizerController.applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
verify(mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), verify(mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
eq(mErrorToken), eq(mTaskFragment), eq(HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT), eq(mErrorToken), eq(mTaskFragment), eq(HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT),
@@ -885,7 +840,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// Allow organizer to delete empty TaskFragment for cleanup. // Allow organizer to delete empty TaskFragment for cleanup.
final Task task = mTaskFragment.getTask(); final Task task = mTaskFragment.getTask();
mTaskFragment.removeChild(mTaskFragment.getTopMostActivity()); mTaskFragment.removeChild(mTaskFragment.getTopMostActivity());
mWindowOrganizerController.applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
assertNull(mWindowOrganizerController.getTaskFragment(mFragmentToken)); assertNull(mWindowOrganizerController.getTaskFragment(mFragmentToken));
assertNull(task.getTopChild()); assertNull(task.getTopChild());
@@ -916,7 +871,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
doReturn(false).when(task).shouldBeVisible(any()); doReturn(false).when(task).shouldBeVisible(any());
// Sending events // Sending events
mController.registerOrganizer(mIOrganizer);
taskFragment.mTaskFragmentAppearedSent = true; taskFragment.mTaskFragmentAppearedSent = true;
mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment); mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment);
mController.dispatchPendingEvents(); mController.dispatchPendingEvents();
@@ -942,7 +896,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
taskFragment.setResumedActivity(null, "test"); taskFragment.setResumedActivity(null, "test");
// Sending events // Sending events
mController.registerOrganizer(mIOrganizer);
taskFragment.mTaskFragmentAppearedSent = true; taskFragment.mTaskFragmentAppearedSent = true;
mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment); mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment);
mController.dispatchPendingEvents(); mController.dispatchPendingEvents();
@@ -977,7 +930,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
assertTrue(parentTask.shouldBeVisible(null)); assertTrue(parentTask.shouldBeVisible(null));
// Dispatch pending info changed event from creating the activity // Dispatch pending info changed event from creating the activity
mController.registerOrganizer(mIOrganizer);
taskFragment.mTaskFragmentAppearedSent = true; taskFragment.mTaskFragmentAppearedSent = true;
mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment); mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment);
mController.dispatchPendingEvents(); mController.dispatchPendingEvents();
@@ -1013,7 +965,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
assertTrue(task.shouldBeVisible(null)); assertTrue(task.shouldBeVisible(null));
// Dispatch pending info changed event from creating the activity // Dispatch pending info changed event from creating the activity
mController.registerOrganizer(mIOrganizer);
taskFragment.mTaskFragmentAppearedSent = true; taskFragment.mTaskFragmentAppearedSent = true;
mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment); mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment);
mController.dispatchPendingEvents(); mController.dispatchPendingEvents();
@@ -1039,13 +990,11 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
* {@link WindowOrganizerController}. * {@link WindowOrganizerController}.
*/ */
@Test @Test
public void testTaskFragmentRemoved_cleanUpEmbeddedTaskFragment() public void testTaskFragmentRemoved_cleanUpEmbeddedTaskFragment() {
throws RemoteException {
mController.registerOrganizer(mIOrganizer);
final ActivityRecord ownerActivity = createActivityRecord(mDisplayContent); final ActivityRecord ownerActivity = createActivityRecord(mDisplayContent);
final IBinder fragmentToken = new Binder(); final IBinder fragmentToken = new Binder();
createTaskFragmentFromOrganizer(mTransaction, ownerActivity, fragmentToken); createTaskFragmentFromOrganizer(mTransaction, ownerActivity, fragmentToken);
mAtm.getWindowOrganizerController().applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
final TaskFragment taskFragment = mWindowOrganizerController.getTaskFragment(fragmentToken); final TaskFragment taskFragment = mWindowOrganizerController.getTaskFragment(fragmentToken);
assertNotNull(taskFragment); assertNotNull(taskFragment);
@@ -1060,9 +1009,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
* its parent bounds. * its parent bounds.
*/ */
@Test @Test
public void testUntrustedEmbedding_configChange() throws RemoteException { public void testUntrustedEmbedding_configChange() {
mController.registerOrganizer(mIOrganizer);
mOrganizer.applyTransaction(mTransaction);
mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */, mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
"Test:TaskFragmentOrganizer" /* processName */); "Test:TaskFragmentOrganizer" /* processName */);
doReturn(false).when(mTaskFragment).isAllowedToBeEmbeddedInTrustedMode(); doReturn(false).when(mTaskFragment).isAllowedToBeEmbeddedInTrustedMode();
@@ -1123,8 +1070,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// Make minWidth/minHeight exceeds the TaskFragment bounds. // Make minWidth/minHeight exceeds the TaskFragment bounds.
activity.info.windowLayout = new ActivityInfo.WindowLayout( activity.info.windowLayout = new ActivityInfo.WindowLayout(
0, 0, 0, 0, 0, mTaskFragBounds.width() + 10, mTaskFragBounds.height() + 10); 0, 0, 0, 0, 0, mTaskFragBounds.width() + 10, mTaskFragBounds.height() + 10);
mOrganizer.applyTransaction(mTransaction);
mController.registerOrganizer(mIOrganizer);
mTaskFragment = new TaskFragmentBuilder(mAtm) mTaskFragment = new TaskFragmentBuilder(mAtm)
.setParentTask(task) .setParentTask(task)
.setFragmentToken(mFragmentToken) .setFragmentToken(mFragmentToken)
@@ -1137,7 +1082,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// minimum dimensions. // minimum dimensions.
mTransaction.reparentActivityToTaskFragment(mFragmentToken, activity.token) mTransaction.reparentActivityToTaskFragment(mFragmentToken, activity.token)
.setErrorCallbackToken(mErrorToken); .setErrorCallbackToken(mErrorToken);
mWindowOrganizerController.applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
// The pending event will be dispatched on the handler (from requestTraversal). // The pending event will be dispatched on the handler (from requestTraversal).
waitHandlerIdle(mWm.mAnimationHandler); waitHandlerIdle(mWm.mAnimationHandler);
@@ -1148,8 +1093,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
@Test @Test
public void testMinDimensionViolation_ReparentChildren() { public void testMinDimensionViolation_ReparentChildren() {
final Task task = createTask(mDisplayContent); final Task task = createTask(mDisplayContent);
mOrganizer.applyTransaction(mTransaction);
mController.registerOrganizer(mIOrganizer);
final IBinder oldFragToken = new Binder(); final IBinder oldFragToken = new Binder();
final TaskFragment oldTaskFrag = new TaskFragmentBuilder(mAtm) final TaskFragment oldTaskFrag = new TaskFragmentBuilder(mAtm)
.setParentTask(task) .setParentTask(task)
@@ -1175,7 +1118,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
mTransaction.reparentChildren(oldTaskFrag.mRemoteToken.toWindowContainerToken(), mTransaction.reparentChildren(oldTaskFrag.mRemoteToken.toWindowContainerToken(),
mTaskFragment.mRemoteToken.toWindowContainerToken()) mTaskFragment.mRemoteToken.toWindowContainerToken())
.setErrorCallbackToken(mErrorToken); .setErrorCallbackToken(mErrorToken);
mWindowOrganizerController.applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
// The pending event will be dispatched on the handler (from requestTraversal). // The pending event will be dispatched on the handler (from requestTraversal).
waitHandlerIdle(mWm.mAnimationHandler); waitHandlerIdle(mWm.mAnimationHandler);
@@ -1186,8 +1129,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
@Test @Test
public void testMinDimensionViolation_SetBounds() { public void testMinDimensionViolation_SetBounds() {
final Task task = createTask(mDisplayContent); final Task task = createTask(mDisplayContent);
mOrganizer.applyTransaction(mTransaction);
mController.registerOrganizer(mIOrganizer);
mTaskFragment = new TaskFragmentBuilder(mAtm) mTaskFragment = new TaskFragmentBuilder(mAtm)
.setParentTask(task) .setParentTask(task)
.createActivityCount(1) .createActivityCount(1)
@@ -1206,7 +1147,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// minimum dimensions. // minimum dimensions.
mTransaction.setBounds(mTaskFragment.mRemoteToken.toWindowContainerToken(), mTaskFragBounds) mTransaction.setBounds(mTaskFragment.mRemoteToken.toWindowContainerToken(), mTaskFragBounds)
.setErrorCallbackToken(mErrorToken); .setErrorCallbackToken(mErrorToken);
mWindowOrganizerController.applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
assertWithMessage("setBounds must not be performed.") assertWithMessage("setBounds must not be performed.")
.that(mTaskFragment.getBounds()).isEqualTo(task.getBounds()); .that(mTaskFragment.getBounds()).isEqualTo(task.getBounds());
@@ -1214,18 +1155,17 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
@Test @Test
public void testOnTransactionReady_invokeOnTransactionHandled() { public void testOnTransactionReady_invokeOnTransactionHandled() {
mController.registerOrganizer(mIOrganizer);
final TaskFragmentTransaction transaction = new TaskFragmentTransaction(); final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
mOrganizer.onTransactionReady(transaction); mOrganizer.onTransactionReady(transaction);
// Organizer should always trigger #onTransactionHandled when receives #onTransactionReady // Organizer should always trigger #onTransactionHandled when receives #onTransactionReady
verify(mOrganizer).onTransactionHandled(eq(transaction.getTransactionToken()), any()); verify(mOrganizer).onTransactionHandled(eq(transaction.getTransactionToken()), any(),
verify(mOrganizer, never()).applyTransaction(any()); anyInt(), anyBoolean());
verify(mOrganizer, never()).applyTransaction(any(), anyInt(), anyBoolean());
} }
@Test @Test
public void testDispatchTransaction_deferTransitionReady() { public void testDispatchTransaction_deferTransitionReady() {
mController.registerOrganizer(mIOrganizer);
setupMockParent(mTaskFragment, mTask); setupMockParent(mTaskFragment, mTask);
final ArgumentCaptor<IBinder> tokenCaptor = ArgumentCaptor.forClass(IBinder.class); final ArgumentCaptor<IBinder> tokenCaptor = ArgumentCaptor.forClass(IBinder.class);
final ArgumentCaptor<WindowContainerTransaction> wctCaptor = final ArgumentCaptor<WindowContainerTransaction> wctCaptor =
@@ -1238,12 +1178,15 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// Defer transition when send TaskFragment transaction during transition collection. // Defer transition when send TaskFragment transaction during transition collection.
verify(mTransitionController).deferTransitionReady(); verify(mTransitionController).deferTransitionReady();
verify(mOrganizer).onTransactionHandled(tokenCaptor.capture(), wctCaptor.capture()); verify(mOrganizer).onTransactionHandled(tokenCaptor.capture(), wctCaptor.capture(),
anyInt(), anyBoolean());
mController.onTransactionHandled(mIOrganizer, tokenCaptor.getValue(), wctCaptor.getValue()); final IBinder transactionToken = tokenCaptor.getValue();
final WindowContainerTransaction wct = wctCaptor.getValue();
wct.setTaskFragmentOrganizer(mIOrganizer);
mController.onTransactionHandled(transactionToken, wct, getTransitionType(wct),
false /* shouldApplyIndependently */);
// Apply the organizer change and continue transition.
verify(mWindowOrganizerController).applyTransaction(wctCaptor.getValue());
verify(mTransitionController).continueTransitionReady(); verify(mTransitionController).continueTransitionReady();
} }
@@ -1258,7 +1201,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
ownerActivity.getTask().effectiveUid = uid; ownerActivity.getTask().effectiveUid = uid;
final TaskFragmentCreationParams params = new TaskFragmentCreationParams.Builder( final TaskFragmentCreationParams params = new TaskFragmentCreationParams.Builder(
mOrganizerToken, fragmentToken, ownerActivity.token).build(); mOrganizerToken, fragmentToken, ownerActivity.token).build();
mOrganizer.applyTransaction(wct); wct.setTaskFragmentOrganizer(mIOrganizer);
// Allow organizer to create TaskFragment and start/reparent activity to TaskFragment. // Allow organizer to create TaskFragment and start/reparent activity to TaskFragment.
wct.createTaskFragment(params); wct.createTaskFragment(params);
@@ -1266,22 +1209,14 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
/** Asserts that applying the given transaction will throw a {@link SecurityException}. */ /** Asserts that applying the given transaction will throw a {@link SecurityException}. */
private void assertApplyTransactionDisallowed(WindowContainerTransaction t) { private void assertApplyTransactionDisallowed(WindowContainerTransaction t) {
assertThrows(SecurityException.class, () -> { assertThrows(SecurityException.class, () ->
try { mController.applyTransaction(t, getTransitionType(t),
mAtm.getWindowOrganizerController().applyTransaction(t); false /* shouldApplyIndependently */));
} catch (RemoteException e) {
fail();
}
});
} }
/** Asserts that applying the given transaction will not throw any exception. */ /** Asserts that applying the given transaction will not throw any exception. */
private void assertApplyTransactionAllowed(WindowContainerTransaction t) { private void assertApplyTransactionAllowed(WindowContainerTransaction t) {
try { mController.applyTransaction(t, getTransitionType(t), false /* shouldApplyIndependently */);
mAtm.getWindowOrganizerController().applyTransaction(t);
} catch (RemoteException e) {
fail();
}
} }
/** Asserts that there will be a transaction for TaskFragment appeared. */ /** Asserts that there will be a transaction for TaskFragment appeared. */
@@ -1367,8 +1302,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
/** Setups an embedded TaskFragment in a PIP Task. */ /** Setups an embedded TaskFragment in a PIP Task. */
private void setupTaskFragmentInPip() { private void setupTaskFragmentInPip() {
mOrganizer.applyTransaction(mTransaction);
mController.registerOrganizer(mIOrganizer);
mTaskFragment = new TaskFragmentBuilder(mAtm) mTaskFragment = new TaskFragmentBuilder(mAtm)
.setCreateParentTask() .setCreateParentTask()
.setFragmentToken(mFragmentToken) .setFragmentToken(mFragmentToken)
@@ -1376,8 +1309,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
.createActivityCount(1) .createActivityCount(1)
.build(); .build();
mFragmentWindowToken = mTaskFragment.mRemoteToken.toWindowContainerToken(); mFragmentWindowToken = mTaskFragment.mRemoteToken.toWindowContainerToken();
mAtm.mWindowOrganizerController.mLaunchTaskFragments mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment);
.put(mFragmentToken, mTaskFragment);
mTaskFragment.getTask().setWindowingMode(WINDOWING_MODE_PINNED); mTaskFragment.getTask().setWindowingMode(WINDOWING_MODE_PINNED);
} }