Merge "Pass Task configuration changed for all TaskFragments at once" into tm-qpr-dev

This commit is contained in:
Chris Li
2022-08-06 00:02:07 +00:00
committed by Android (Google) Code Review
6 changed files with 351 additions and 219 deletions

View File

@@ -32,8 +32,10 @@ import android.content.res.Configuration;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.SparseArray;
import android.view.RemoteAnimationDefinition; import android.view.RemoteAnimationDefinition;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -72,6 +74,12 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
*/ */
private final Executor mExecutor; private final Executor mExecutor;
// TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next release.
/** Map from Task id to client tokens of TaskFragments in the Task. */
private final SparseArray<List<IBinder>> mTaskIdToFragmentTokens = new SparseArray<>();
/** Map from Task id to Task configuration. */
private final SparseArray<Configuration> mTaskIdToConfigurations = new SparseArray<>();
public TaskFragmentOrganizer(@NonNull Executor executor) { public TaskFragmentOrganizer(@NonNull Executor executor) {
mExecutor = executor; mExecutor = executor;
} }
@@ -160,6 +168,27 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
public void onTaskFragmentParentInfoChanged( public void onTaskFragmentParentInfoChanged(
@NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {} @NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {}
/**
* Called when the parent leaf Task of organized TaskFragments is changed.
* When the leaf Task is changed, the organizer may want to update the TaskFragments in one
* transaction.
*
* For case like screen size change, it will trigger onTaskFragmentParentInfoChanged with new
* Task bounds, but may not trigger onTaskFragmentInfoChanged because there can be an override
* bounds.
* @hide
*/
public void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig) {
// TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next release.
final List<IBinder> tokens = mTaskIdToFragmentTokens.get(taskId);
if (tokens == null || tokens.isEmpty()) {
return;
}
for (int i = tokens.size() - 1; i >= 0; i--) {
onTaskFragmentParentInfoChanged(tokens.get(i), parentConfig);
}
}
/** /**
* Called when the {@link WindowContainerTransaction} created with * Called when the {@link WindowContainerTransaction} created with
* {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side. * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side.
@@ -221,34 +250,43 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
final List<TaskFragmentTransaction.Change> changes = transaction.getChanges(); final List<TaskFragmentTransaction.Change> changes = transaction.getChanges();
for (TaskFragmentTransaction.Change change : changes) { for (TaskFragmentTransaction.Change change : changes) {
// TODO(b/240519866): apply all changes in one WCT. // TODO(b/240519866): apply all changes in one WCT.
final int taskId = change.getTaskId();
switch (change.getType()) { switch (change.getType()) {
case TYPE_TASK_FRAGMENT_APPEARED: case TYPE_TASK_FRAGMENT_APPEARED:
onTaskFragmentAppeared(change.getTaskFragmentInfo()); // TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next
if (change.getTaskConfiguration() != null) { // release.
// TODO(b/240519866): convert to pass TaskConfiguration for all TFs in the if (!mTaskIdToFragmentTokens.contains(taskId)) {
// same Task mTaskIdToFragmentTokens.put(taskId, new ArrayList<>());
onTaskFragmentParentInfoChanged(
change.getTaskFragmentToken(),
change.getTaskConfiguration());
} }
mTaskIdToFragmentTokens.get(taskId).add(change.getTaskFragmentToken());
onTaskFragmentParentInfoChanged(change.getTaskFragmentToken(),
mTaskIdToConfigurations.get(taskId));
onTaskFragmentAppeared(change.getTaskFragmentInfo());
break; break;
case TYPE_TASK_FRAGMENT_INFO_CHANGED: case TYPE_TASK_FRAGMENT_INFO_CHANGED:
if (change.getTaskConfiguration() != null) {
// TODO(b/240519866): convert to pass TaskConfiguration for all TFs in the
// same Task
onTaskFragmentParentInfoChanged(
change.getTaskFragmentToken(),
change.getTaskConfiguration());
}
onTaskFragmentInfoChanged(change.getTaskFragmentInfo()); onTaskFragmentInfoChanged(change.getTaskFragmentInfo());
break; break;
case TYPE_TASK_FRAGMENT_VANISHED: case TYPE_TASK_FRAGMENT_VANISHED:
// TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next
// release.
if (mTaskIdToFragmentTokens.contains(taskId)) {
final List<IBinder> tokens = mTaskIdToFragmentTokens.get(taskId);
tokens.remove(change.getTaskFragmentToken());
if (tokens.isEmpty()) {
mTaskIdToFragmentTokens.remove(taskId);
mTaskIdToConfigurations.remove(taskId);
}
}
onTaskFragmentVanished(change.getTaskFragmentInfo()); onTaskFragmentVanished(change.getTaskFragmentInfo());
break; break;
case TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED: case TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED:
onTaskFragmentParentInfoChanged( // TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next
change.getTaskFragmentToken(), // release.
change.getTaskConfiguration()); mTaskIdToConfigurations.put(taskId, change.getTaskConfiguration());
onTaskFragmentParentInfoChanged(taskId, change.getTaskConfiguration());
break; break;
case TYPE_TASK_FRAGMENT_ERROR: case TYPE_TASK_FRAGMENT_ERROR:
final Bundle errorBundle = change.getErrorBundle(); final Bundle errorBundle = change.getErrorBundle();

View File

@@ -51,12 +51,6 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
@VisibleForTesting @VisibleForTesting
final Map<IBinder, TaskFragmentInfo> mFragmentInfos = new ArrayMap<>(); final Map<IBinder, TaskFragmentInfo> mFragmentInfos = new ArrayMap<>();
/**
* Mapping from the client assigned unique token to the TaskFragment parent
* {@link Configuration}.
*/
final Map<IBinder, Configuration> mFragmentParentConfigs = new ArrayMap<>();
private final TaskFragmentCallback mCallback; private final TaskFragmentCallback mCallback;
@VisibleForTesting @VisibleForTesting
TaskFragmentAnimationController mAnimationController; TaskFragmentAnimationController mAnimationController;
@@ -68,8 +62,7 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo); void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo); void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo); void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken, void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig);
@NonNull Configuration parentConfig);
void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent, void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent,
@NonNull IBinder activityToken); @NonNull IBinder activityToken);
void onTaskFragmentError(@Nullable TaskFragmentInfo taskFragmentInfo, int opType); void onTaskFragmentError(@Nullable TaskFragmentInfo taskFragmentInfo, int opType);
@@ -300,7 +293,6 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
@Override @Override
public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) { public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
mFragmentInfos.remove(taskFragmentInfo.getFragmentToken()); mFragmentInfos.remove(taskFragmentInfo.getFragmentToken());
mFragmentParentConfigs.remove(taskFragmentInfo.getFragmentToken());
if (mCallback != null) { if (mCallback != null) {
mCallback.onTaskFragmentVanished(taskFragmentInfo); mCallback.onTaskFragmentVanished(taskFragmentInfo);
@@ -308,12 +300,9 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
} }
@Override @Override
public void onTaskFragmentParentInfoChanged( public void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig) {
@NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {
mFragmentParentConfigs.put(fragmentToken, parentConfig);
if (mCallback != null) { if (mCallback != null) {
mCallback.onTaskFragmentParentInfoChanged(fragmentToken, parentConfig); mCallback.onTaskFragmentParentInfoChanged(taskId, parentConfig);
} }
} }

View File

@@ -155,6 +155,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
container.setInfo(taskFragmentInfo); container.setInfo(taskFragmentInfo);
if (container.isFinished()) { if (container.isFinished()) {
mPresenter.cleanupContainer(container, false /* shouldFinishDependent */); mPresenter.cleanupContainer(container, false /* shouldFinishDependent */);
} else {
// Update with the latest Task configuration.
mPresenter.updateContainer(container);
} }
updateCallbackIfNecessary(); updateCallbackIfNecessary();
} }
@@ -233,19 +236,30 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
} }
@Override @Override
public void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken, public void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig) {
@NonNull Configuration parentConfig) {
synchronized (mLock) { synchronized (mLock) {
final TaskFragmentContainer container = getContainer(fragmentToken); onTaskConfigurationChanged(taskId, parentConfig);
if (container != null) { if (isInPictureInPicture(parentConfig)) {
onTaskConfigurationChanged(container.getTaskId(), parentConfig); // No need to update presentation in PIP until the Task exit PIP.
if (isInPictureInPicture(parentConfig)) { return;
// No need to update presentation in PIP until the Task exit PIP.
return;
}
mPresenter.updateContainer(container);
updateCallbackIfNecessary();
} }
final TaskContainer taskContainer = getTaskContainer(taskId);
if (taskContainer == null || taskContainer.isEmpty()) {
Log.e(TAG, "onTaskFragmentParentInfoChanged on empty Task id=" + taskId);
return;
}
// Update all TaskFragments in the Task. Make a copy of the list since some may be
// removed on updating.
final List<TaskFragmentContainer> containers =
new ArrayList<>(taskContainer.mContainers);
for (int i = containers.size() - 1; i >= 0; i--) {
final TaskFragmentContainer container = containers.get(i);
// Wait until onTaskFragmentAppeared to update new container.
if (!container.isFinished() && !container.isWaitingActivityAppear()) {
mPresenter.updateContainer(container);
}
}
updateCallbackIfNecessary();
} }
} }

View File

@@ -887,11 +887,12 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
logIfTransactionTooLarge(r.intent, r.getSavedState()); logIfTransactionTooLarge(r.intent, r.getSavedState());
if (r.isEmbedded()) { final TaskFragment organizedTaskFragment = r.getOrganizedTaskFragment();
if (organizedTaskFragment != null) {
// Sending TaskFragmentInfo to client to ensure the info is updated before // Sending TaskFragmentInfo to client to ensure the info is updated before
// the activity creation. // the activity creation.
mService.mTaskFragmentOrganizerController.dispatchPendingInfoChangedEvent( mService.mTaskFragmentOrganizerController.dispatchPendingInfoChangedEvent(
r.getOrganizedTaskFragment()); organizedTaskFragment);
} }
// Create activity launch transaction. // Create activity launch transaction.

View File

@@ -16,6 +16,7 @@
package com.android.server.wm; package com.android.server.wm;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.window.TaskFragmentOrganizer.putErrorInfoInBundle; import static android.window.TaskFragmentOrganizer.putErrorInfoInBundle;
import static android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENT_TO_TASK; import static android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENT_TO_TASK;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED; import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED;
@@ -28,6 +29,8 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANI
import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED; import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
import static com.android.server.wm.WindowOrganizerController.configurationsAreEqualForOrganizer; import static com.android.server.wm.WindowOrganizerController.configurationsAreEqualForOrganizer;
import static java.util.Objects.requireNonNull;
import android.annotation.IntDef; import android.annotation.IntDef;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
@@ -38,6 +41,7 @@ import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.ArrayMap; import android.util.ArrayMap;
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;
@@ -51,6 +55,7 @@ import com.android.internal.protolog.common.ProtoLog;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.WeakHashMap; import java.util.WeakHashMap;
@@ -70,15 +75,12 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
private final ArrayMap<IBinder, TaskFragmentOrganizerState> mTaskFragmentOrganizerState = private final ArrayMap<IBinder, TaskFragmentOrganizerState> mTaskFragmentOrganizerState =
new ArrayMap<>(); new ArrayMap<>();
/** /**
* A List which manages the TaskFragment pending event {@link PendingTaskFragmentEvent} * Map from {@link ITaskFragmentOrganizer} to a list of related {@link PendingTaskFragmentEvent}
*/ */
private final ArrayList<PendingTaskFragmentEvent> mPendingTaskFragmentEvents = private final ArrayMap<IBinder, List<PendingTaskFragmentEvent>> mPendingTaskFragmentEvents =
new ArrayList<>();
/** Map from {@link ITaskFragmentOrganizer} to {@link TaskFragmentTransaction}. */
private final ArrayMap<IBinder, TaskFragmentTransaction> mTmpOrganizerToTransactionMap =
new ArrayMap<>(); new ArrayMap<>();
private final ArrayList<ITaskFragmentOrganizer> mTmpOrganizerList = new ArrayList<>();
private final ArraySet<Task> mTmpTaskSet = new ArraySet<>();
TaskFragmentOrganizerController(ActivityTaskManagerService atm) { TaskFragmentOrganizerController(ActivityTaskManagerService atm) {
mAtmService = atm; mAtmService = atm;
@@ -94,10 +96,30 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
private final ITaskFragmentOrganizer mOrganizer; private final ITaskFragmentOrganizer mOrganizer;
private final int mOrganizerPid; private final int mOrganizerPid;
private final int mOrganizerUid; private final int mOrganizerUid;
/**
* Map from {@link TaskFragment} to the last {@link TaskFragmentInfo} sent to the
* organizer.
*/
private final Map<TaskFragment, TaskFragmentInfo> mLastSentTaskFragmentInfos = private final Map<TaskFragment, TaskFragmentInfo> mLastSentTaskFragmentInfos =
new WeakHashMap<>(); new WeakHashMap<>();
private final Map<TaskFragment, Configuration> mLastSentTaskFragmentParentConfigs =
new WeakHashMap<>(); /**
* Map from {@link TaskFragment} to its leaf {@link Task#mTaskId}. Embedded
* {@link TaskFragment} will not be reparented until it is removed.
*/
private final Map<TaskFragment, Integer> mTaskFragmentTaskIds = new WeakHashMap<>();
/**
* Map from {@link Task#mTaskId} to the last Task {@link Configuration} sent to the
* organizer.
*/
private final SparseArray<Configuration> mLastSentTaskFragmentParentConfigs =
new SparseArray<>();
/**
* Map from temporary activity token to the corresponding {@link ActivityRecord}.
*/
private final Map<IBinder, ActivityRecord> mTemporaryActivityTokens = private final Map<IBinder, ActivityRecord> mTemporaryActivityTokens =
new WeakHashMap<>(); new WeakHashMap<>();
@@ -161,21 +183,15 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
TaskFragmentTransaction.Change prepareTaskFragmentAppeared(@NonNull TaskFragment tf) { TaskFragmentTransaction.Change prepareTaskFragmentAppeared(@NonNull TaskFragment tf) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment appeared name=%s", tf.getName()); ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment appeared name=%s", tf.getName());
final TaskFragmentInfo info = tf.getTaskFragmentInfo(); final TaskFragmentInfo info = tf.getTaskFragmentInfo();
final int taskId = tf.getTask().mTaskId;
tf.mTaskFragmentAppearedSent = true; tf.mTaskFragmentAppearedSent = true;
mLastSentTaskFragmentInfos.put(tf, info); mLastSentTaskFragmentInfos.put(tf, info);
final TaskFragmentTransaction.Change change = mTaskFragmentTaskIds.put(tf, taskId);
new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_APPEARED) return new TaskFragmentTransaction.Change(
.setTaskFragmentToken(tf.getFragmentToken()) TYPE_TASK_FRAGMENT_APPEARED)
.setTaskFragmentInfo(info); .setTaskFragmentToken(tf.getFragmentToken())
if (shouldSendTaskFragmentParentInfoChanged(tf)) { .setTaskFragmentInfo(info)
// TODO(b/240519866): convert to pass TaskConfiguration for all TFs in the same Task .setTaskId(taskId);
final Task task = tf.getTask();
mLastSentTaskFragmentParentConfigs
.put(tf, new Configuration(task.getConfiguration()));
change.setTaskId(task.mTaskId)
.setTaskConfiguration(task.getConfiguration());
}
return change;
} }
@NonNull @NonNull
@@ -183,10 +199,24 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment vanished name=%s", tf.getName()); ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment vanished name=%s", tf.getName());
tf.mTaskFragmentAppearedSent = false; tf.mTaskFragmentAppearedSent = false;
mLastSentTaskFragmentInfos.remove(tf); mLastSentTaskFragmentInfos.remove(tf);
mLastSentTaskFragmentParentConfigs.remove(tf);
// Cleanup TaskFragmentParentConfig if this is the last TaskFragment in the Task.
final int taskId;
if (mTaskFragmentTaskIds.containsKey(tf)) {
taskId = mTaskFragmentTaskIds.remove(tf);
if (!mTaskFragmentTaskIds.containsValue(taskId)) {
// No more TaskFragment in the Task.
mLastSentTaskFragmentParentConfigs.remove(taskId);
}
} else {
// This can happen if the appeared wasn't sent before remove.
taskId = INVALID_TASK_ID;
}
return new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_VANISHED) return new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_VANISHED)
.setTaskFragmentToken(tf.getFragmentToken()) .setTaskFragmentToken(tf.getFragmentToken())
.setTaskFragmentInfo(tf.getTaskFragmentInfo()); .setTaskFragmentInfo(tf.getTaskFragmentInfo())
.setTaskId(taskId);
} }
@Nullable @Nullable
@@ -197,65 +227,39 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
final TaskFragmentInfo lastInfo = mLastSentTaskFragmentInfos.get(tf); final TaskFragmentInfo lastInfo = mLastSentTaskFragmentInfos.get(tf);
if (info.equalsForTaskFragmentOrganizer(lastInfo) && configurationsAreEqualForOrganizer( if (info.equalsForTaskFragmentOrganizer(lastInfo) && configurationsAreEqualForOrganizer(
info.getConfiguration(), lastInfo.getConfiguration())) { info.getConfiguration(), lastInfo.getConfiguration())) {
// Parent config may have changed. The controller will check if there is any return null;
// important config change for the organizer.
return prepareTaskFragmentParentInfoChanged(tf);
} }
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment info changed name=%s", ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment info changed name=%s",
tf.getName()); tf.getName());
mLastSentTaskFragmentInfos.put(tf, info); mLastSentTaskFragmentInfos.put(tf, info);
final TaskFragmentTransaction.Change change = return new TaskFragmentTransaction.Change(
new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_INFO_CHANGED) TYPE_TASK_FRAGMENT_INFO_CHANGED)
.setTaskFragmentToken(tf.getFragmentToken()) .setTaskFragmentToken(tf.getFragmentToken())
.setTaskFragmentInfo(info); .setTaskFragmentInfo(info)
if (shouldSendTaskFragmentParentInfoChanged(tf)) { .setTaskId(tf.getTask().mTaskId);
// TODO(b/240519866): convert to pass TaskConfiguration for all TFs in the same Task
// at once.
// Parent config may have changed. The controller will check if there is any
// important config change for the organizer.
final Task task = tf.getTask();
mLastSentTaskFragmentParentConfigs
.put(tf, new Configuration(task.getConfiguration()));
change.setTaskId(task.mTaskId)
.setTaskConfiguration(task.getConfiguration());
}
return change;
} }
@Nullable @Nullable
TaskFragmentTransaction.Change prepareTaskFragmentParentInfoChanged( TaskFragmentTransaction.Change prepareTaskFragmentParentInfoChanged(
@NonNull TaskFragment tf) { @NonNull Task task) {
if (!shouldSendTaskFragmentParentInfoChanged(tf)) { final int taskId = task.mTaskId;
// Check if the parent info is different from the last reported parent info.
final Configuration taskConfig = task.getConfiguration();
final Configuration lastParentConfig = mLastSentTaskFragmentParentConfigs.get(taskId);
if (configurationsAreEqualForOrganizer(taskConfig, lastParentConfig)
&& taskConfig.windowConfiguration.getWindowingMode()
== lastParentConfig.windowConfiguration.getWindowingMode()) {
return null; return null;
} }
final Task parent = tf.getTask();
final Configuration parentConfig = parent.getConfiguration();
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
"TaskFragment parent info changed name=%s parentTaskId=%d", "TaskFragment parent info changed name=%s parentTaskId=%d",
tf.getName(), parent.mTaskId); task.getName(), taskId);
mLastSentTaskFragmentParentConfigs.put(tf, new Configuration(parentConfig)); mLastSentTaskFragmentParentConfigs.put(taskId, new Configuration(taskConfig));
return new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED) return new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED)
.setTaskFragmentToken(tf.getFragmentToken()) .setTaskId(taskId)
.setTaskId(parent.mTaskId) .setTaskConfiguration(taskConfig);
.setTaskConfiguration(parent.getConfiguration());
}
/** Whether the system should report TaskFragment parent info changed to the organizer. */
private boolean shouldSendTaskFragmentParentInfoChanged(@NonNull TaskFragment tf) {
final Task parent = tf.getTask();
if (parent == null) {
// The TaskFragment is not attached.
mLastSentTaskFragmentParentConfigs.remove(tf);
return false;
}
// Check if the parent info is different from the last reported parent info.
final Configuration parentConfig = parent.getConfiguration();
final Configuration lastParentConfig = mLastSentTaskFragmentParentConfigs.get(tf);
return !configurationsAreEqualForOrganizer(parentConfig, lastParentConfig)
|| parentConfig.windowConfiguration.getWindowingMode()
!= lastParentConfig.windowConfiguration.getWindowingMode();
} }
@NonNull @NonNull
@@ -345,6 +349,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
} }
mTaskFragmentOrganizerState.put(organizer.asBinder(), mTaskFragmentOrganizerState.put(organizer.asBinder(),
new TaskFragmentOrganizerState(organizer, pid, uid)); new TaskFragmentOrganizerState(organizer, pid, uid));
mPendingTaskFragmentEvents.put(organizer.asBinder(), new ArrayList<>());
} }
} }
@@ -434,6 +439,11 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
void onTaskFragmentAppeared(@NonNull ITaskFragmentOrganizer organizer, void onTaskFragmentAppeared(@NonNull ITaskFragmentOrganizer organizer,
@NonNull TaskFragment taskFragment) { @NonNull TaskFragment taskFragment) {
if (taskFragment.getTask() == null) {
Slog.w(TAG, "onTaskFragmentAppeared failed because it is not attached tf="
+ taskFragment);
return;
}
final TaskFragmentOrganizerState state = validateAndGetState(organizer); final TaskFragmentOrganizerState state = validateAndGetState(organizer);
if (!state.addTaskFragment(taskFragment)) { if (!state.addTaskFragment(taskFragment)) {
return; return;
@@ -441,28 +451,15 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
PendingTaskFragmentEvent pendingEvent = getPendingTaskFragmentEvent(taskFragment, PendingTaskFragmentEvent pendingEvent = getPendingTaskFragmentEvent(taskFragment,
PendingTaskFragmentEvent.EVENT_APPEARED); PendingTaskFragmentEvent.EVENT_APPEARED);
if (pendingEvent == null) { if (pendingEvent == null) {
pendingEvent = new PendingTaskFragmentEvent.Builder( addPendingEvent(new PendingTaskFragmentEvent.Builder(
PendingTaskFragmentEvent.EVENT_APPEARED, organizer) PendingTaskFragmentEvent.EVENT_APPEARED, organizer)
.setTaskFragment(taskFragment) .setTaskFragment(taskFragment)
.build(); .build());
mPendingTaskFragmentEvents.add(pendingEvent);
} }
} }
void onTaskFragmentInfoChanged(@NonNull ITaskFragmentOrganizer organizer, void onTaskFragmentInfoChanged(@NonNull ITaskFragmentOrganizer organizer,
@NonNull TaskFragment taskFragment) { @NonNull TaskFragment taskFragment) {
handleTaskFragmentInfoChanged(organizer, taskFragment,
PendingTaskFragmentEvent.EVENT_INFO_CHANGED);
}
void onTaskFragmentParentInfoChanged(@NonNull ITaskFragmentOrganizer organizer,
@NonNull TaskFragment taskFragment) {
handleTaskFragmentInfoChanged(organizer, taskFragment,
PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED);
}
private void handleTaskFragmentInfoChanged(@NonNull ITaskFragmentOrganizer organizer,
@NonNull TaskFragment taskFragment, int eventType) {
validateAndGetState(organizer); validateAndGetState(organizer);
if (!taskFragment.mTaskFragmentAppearedSent) { if (!taskFragment.mTaskFragmentAppearedSent) {
// Skip if TaskFragment still not appeared. // Skip if TaskFragment still not appeared.
@@ -470,45 +467,41 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
} }
PendingTaskFragmentEvent pendingEvent = getLastPendingLifecycleEvent(taskFragment); PendingTaskFragmentEvent pendingEvent = getLastPendingLifecycleEvent(taskFragment);
if (pendingEvent == null) { if (pendingEvent == null) {
pendingEvent = new PendingTaskFragmentEvent.Builder(eventType, organizer) pendingEvent = new PendingTaskFragmentEvent.Builder(
.setTaskFragment(taskFragment) PendingTaskFragmentEvent.EVENT_INFO_CHANGED, organizer)
.build(); .setTaskFragment(taskFragment)
.build();
} else { } else {
if (pendingEvent.mEventType == PendingTaskFragmentEvent.EVENT_VANISHED) { if (pendingEvent.mEventType == PendingTaskFragmentEvent.EVENT_VANISHED) {
// Skipped the info changed event if vanished event is pending. // Skipped the info changed event if vanished event is pending.
return; return;
} }
// Remove and add for re-ordering. // Remove and add for re-ordering.
mPendingTaskFragmentEvents.remove(pendingEvent); removePendingEvent(pendingEvent);
// Reset the defer time when TaskFragment is changed, so that it can check again if // Reset the defer time when TaskFragment is changed, so that it can check again if
// the event should be sent to the organizer, for example the TaskFragment may become // the event should be sent to the organizer, for example the TaskFragment may become
// empty. // empty.
pendingEvent.mDeferTime = 0; pendingEvent.mDeferTime = 0;
} }
mPendingTaskFragmentEvents.add(pendingEvent); addPendingEvent(pendingEvent);
} }
void onTaskFragmentVanished(@NonNull ITaskFragmentOrganizer organizer, void onTaskFragmentVanished(@NonNull ITaskFragmentOrganizer organizer,
@NonNull TaskFragment taskFragment) { @NonNull TaskFragment taskFragment) {
final TaskFragmentOrganizerState state = validateAndGetState(organizer); final TaskFragmentOrganizerState state = validateAndGetState(organizer);
for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) { final List<PendingTaskFragmentEvent> pendingEvents = mPendingTaskFragmentEvents
PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i); .get(organizer.asBinder());
if (taskFragment == entry.mTaskFragment) { // Remove any pending events since this TaskFragment is being removed.
mPendingTaskFragmentEvents.remove(i); for (int i = pendingEvents.size() - 1; i >= 0; i--) {
if (entry.mEventType == PendingTaskFragmentEvent.EVENT_APPEARED) { final PendingTaskFragmentEvent event = pendingEvents.get(i);
// If taskFragment appeared callback is pending, ignore the vanished request. if (taskFragment == event.mTaskFragment) {
return; pendingEvents.remove(i);
}
} }
} }
if (!taskFragment.mTaskFragmentAppearedSent) { addPendingEvent(new PendingTaskFragmentEvent.Builder(
return;
}
final PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent.Builder(
PendingTaskFragmentEvent.EVENT_VANISHED, organizer) PendingTaskFragmentEvent.EVENT_VANISHED, organizer)
.setTaskFragment(taskFragment) .setTaskFragment(taskFragment)
.build(); .build());
mPendingTaskFragmentEvents.add(pendingEvent);
state.removeTaskFragment(taskFragment); state.removeTaskFragment(taskFragment);
} }
@@ -517,14 +510,13 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
int opType, @NonNull Throwable exception) { int opType, @NonNull Throwable exception) {
validateAndGetState(organizer); validateAndGetState(organizer);
Slog.w(TAG, "onTaskFragmentError ", exception); Slog.w(TAG, "onTaskFragmentError ", exception);
final PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent.Builder( addPendingEvent(new PendingTaskFragmentEvent.Builder(
PendingTaskFragmentEvent.EVENT_ERROR, organizer) PendingTaskFragmentEvent.EVENT_ERROR, organizer)
.setErrorCallbackToken(errorCallbackToken) .setErrorCallbackToken(errorCallbackToken)
.setTaskFragment(taskFragment) .setTaskFragment(taskFragment)
.setException(exception) .setException(exception)
.setOpType(opType) .setOpType(opType)
.build(); .build());
mPendingTaskFragmentEvents.add(pendingEvent);
// Make sure the error event will be dispatched if there are no other changes. // Make sure the error event will be dispatched if there are no other changes.
mAtmService.mWindowManager.mWindowPlacerLocked.requestTraversal(); mAtmService.mWindowManager.mWindowPlacerLocked.requestTraversal();
} }
@@ -554,11 +546,18 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
Slog.w(TAG, "The last TaskFragmentOrganizer no longer exists"); Slog.w(TAG, "The last TaskFragmentOrganizer no longer exists");
return; return;
} }
final PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent.Builder( addPendingEvent(new PendingTaskFragmentEvent.Builder(
PendingTaskFragmentEvent.EVENT_ACTIVITY_REPARENT_TO_TASK, organizer) PendingTaskFragmentEvent.EVENT_ACTIVITY_REPARENT_TO_TASK, organizer)
.setActivity(activity) .setActivity(activity)
.build(); .build());
mPendingTaskFragmentEvents.add(pendingEvent); }
private void addPendingEvent(@NonNull PendingTaskFragmentEvent event) {
mPendingTaskFragmentEvents.get(event.mTaskFragmentOrg.asBinder()).add(event);
}
private void removePendingEvent(@NonNull PendingTaskFragmentEvent event) {
mPendingTaskFragmentEvents.get(event.mTaskFragmentOrg.asBinder()).remove(event);
} }
boolean isOrganizerRegistered(@NonNull ITaskFragmentOrganizer organizer) { boolean isOrganizerRegistered(@NonNull ITaskFragmentOrganizer organizer) {
@@ -570,12 +569,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
// remove all of the children of the organized TaskFragment // remove all of the children of the organized TaskFragment
state.dispose(); state.dispose();
// Remove any pending event of this organizer. // Remove any pending event of this organizer.
for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) { mPendingTaskFragmentEvents.remove(organizer.asBinder());
final PendingTaskFragmentEvent event = mPendingTaskFragmentEvents.get(i);
if (event.mTaskFragmentOrg.asBinder().equals(organizer.asBinder())) {
mPendingTaskFragmentEvents.remove(i);
}
}
mTaskFragmentOrganizerState.remove(organizer.asBinder()); mTaskFragmentOrganizerState.remove(organizer.asBinder());
} }
@@ -631,6 +625,8 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
private final Throwable mException; private final Throwable mException;
@Nullable @Nullable
private final ActivityRecord mActivity; private final ActivityRecord mActivity;
@Nullable
private final Task mTask;
// Set when the event is deferred due to the host task is invisible. The defer time will // Set when the event is deferred due to the host task is invisible. The defer time will
// be the last active time of the host task. // be the last active time of the host task.
private long mDeferTime; private long mDeferTime;
@@ -642,6 +638,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
@Nullable IBinder errorCallbackToken, @Nullable IBinder errorCallbackToken,
@Nullable Throwable exception, @Nullable Throwable exception,
@Nullable ActivityRecord activity, @Nullable ActivityRecord activity,
@Nullable Task task,
int opType) { int opType) {
mEventType = eventType; mEventType = eventType;
mTaskFragmentOrg = taskFragmentOrg; mTaskFragmentOrg = taskFragmentOrg;
@@ -649,6 +646,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
mErrorCallbackToken = errorCallbackToken; mErrorCallbackToken = errorCallbackToken;
mException = exception; mException = exception;
mActivity = activity; mActivity = activity;
mTask = task;
mOpType = opType; mOpType = opType;
} }
@@ -680,11 +678,13 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
private Throwable mException; private Throwable mException;
@Nullable @Nullable
private ActivityRecord mActivity; private ActivityRecord mActivity;
@Nullable
private Task mTask;
private int mOpType; private int mOpType;
Builder(@EventType int eventType, ITaskFragmentOrganizer taskFragmentOrg) { Builder(@EventType int eventType, @NonNull ITaskFragmentOrganizer taskFragmentOrg) {
mEventType = eventType; mEventType = eventType;
mTaskFragmentOrg = taskFragmentOrg; mTaskFragmentOrg = requireNonNull(taskFragmentOrg);
} }
Builder setTaskFragment(@Nullable TaskFragment taskFragment) { Builder setTaskFragment(@Nullable TaskFragment taskFragment) {
@@ -697,13 +697,18 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
return this; return this;
} }
Builder setException(@Nullable Throwable exception) { Builder setException(@NonNull Throwable exception) {
mException = exception; mException = requireNonNull(exception);
return this; return this;
} }
Builder setActivity(@Nullable ActivityRecord activity) { Builder setActivity(@NonNull ActivityRecord activity) {
mActivity = activity; mActivity = requireNonNull(activity);
return this;
}
Builder setTask(@NonNull Task task) {
mTask = requireNonNull(task);
return this; return this;
} }
@@ -714,17 +719,20 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
PendingTaskFragmentEvent build() { PendingTaskFragmentEvent build() {
return new PendingTaskFragmentEvent(mEventType, mTaskFragmentOrg, mTaskFragment, return new PendingTaskFragmentEvent(mEventType, mTaskFragmentOrg, mTaskFragment,
mErrorCallbackToken, mException, mActivity, mOpType); mErrorCallbackToken, mException, mActivity, mTask, mOpType);
} }
} }
} }
@Nullable @Nullable
private PendingTaskFragmentEvent getLastPendingLifecycleEvent(@NonNull TaskFragment tf) { private PendingTaskFragmentEvent getLastPendingLifecycleEvent(@NonNull TaskFragment tf) {
for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) { final ITaskFragmentOrganizer organizer = tf.getTaskFragmentOrganizer();
PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i); final List<PendingTaskFragmentEvent> events = mPendingTaskFragmentEvents
if (tf == entry.mTaskFragment && entry.isLifecycleEvent()) { .get(organizer.asBinder());
return entry; for (int i = events.size() - 1; i >= 0; i--) {
final PendingTaskFragmentEvent event = events.get(i);
if (tf == event.mTaskFragment && event.isLifecycleEvent()) {
return event;
} }
} }
return null; return null;
@@ -733,10 +741,13 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
@Nullable @Nullable
private PendingTaskFragmentEvent getPendingTaskFragmentEvent(@NonNull TaskFragment taskFragment, private PendingTaskFragmentEvent getPendingTaskFragmentEvent(@NonNull TaskFragment taskFragment,
int type) { int type) {
for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) { final ITaskFragmentOrganizer organizer = taskFragment.getTaskFragmentOrganizer();
PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i); final List<PendingTaskFragmentEvent> events = mPendingTaskFragmentEvents
if (taskFragment == entry.mTaskFragment && type == entry.mEventType) { .get(organizer.asBinder());
return entry; for (int i = events.size() - 1; i >= 0; i--) {
final PendingTaskFragmentEvent event = events.get(i);
if (taskFragment == event.mTaskFragment && type == event.mEventType) {
return event;
} }
} }
return null; return null;
@@ -762,12 +773,25 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
|| mPendingTaskFragmentEvents.isEmpty()) { || mPendingTaskFragmentEvents.isEmpty()) {
return; return;
} }
final int organizerNum = mPendingTaskFragmentEvents.size();
for (int i = 0; i < organizerNum; i++) {
final ITaskFragmentOrganizer organizer = mTaskFragmentOrganizerState.get(
mPendingTaskFragmentEvents.keyAt(i)).mOrganizer;
dispatchPendingEvents(organizer, mPendingTaskFragmentEvents.valueAt(i));
}
}
void dispatchPendingEvents(@NonNull ITaskFragmentOrganizer organizer,
@NonNull List<PendingTaskFragmentEvent> pendingEvents) {
if (pendingEvents.isEmpty()) {
return;
}
final ArrayList<Task> visibleTasks = new ArrayList<>(); final ArrayList<Task> visibleTasks = new ArrayList<>();
final ArrayList<Task> invisibleTasks = new ArrayList<>(); final ArrayList<Task> invisibleTasks = new ArrayList<>();
final ArrayList<PendingTaskFragmentEvent> candidateEvents = new ArrayList<>(); final ArrayList<PendingTaskFragmentEvent> candidateEvents = new ArrayList<>();
for (int i = 0, n = mPendingTaskFragmentEvents.size(); i < n; i++) { for (int i = 0, n = pendingEvents.size(); i < n; i++) {
final PendingTaskFragmentEvent event = mPendingTaskFragmentEvents.get(i); final PendingTaskFragmentEvent event = pendingEvents.get(i);
final Task task = event.mTaskFragment != null ? event.mTaskFragment.getTask() : null; final Task task = event.mTaskFragment != null ? event.mTaskFragment.getTask() : null;
if (task != null && (task.lastActiveTime <= event.mDeferTime if (task != null && (task.lastActiveTime <= event.mDeferTime
|| !(isTaskVisible(task, visibleTasks, invisibleTasks) || !(isTaskVisible(task, visibleTasks, invisibleTasks)
@@ -783,27 +807,26 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
return; return;
} }
mTmpOrganizerToTransactionMap.clear(); mTmpTaskSet.clear();
mTmpOrganizerList.clear(); final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
for (int i = 0; i < numEvents; i++) { for (int i = 0; i < numEvents; i++) {
final PendingTaskFragmentEvent event = candidateEvents.get(i); final PendingTaskFragmentEvent event = candidateEvents.get(i);
if (!mTmpOrganizerToTransactionMap.containsKey(event.mTaskFragmentOrg.asBinder())) { if (event.mEventType == PendingTaskFragmentEvent.EVENT_APPEARED
mTmpOrganizerToTransactionMap.put(event.mTaskFragmentOrg.asBinder(), || event.mEventType == PendingTaskFragmentEvent.EVENT_INFO_CHANGED) {
new TaskFragmentTransaction()); final Task task = event.mTaskFragment.getTask();
mTmpOrganizerList.add(event.mTaskFragmentOrg); if (mTmpTaskSet.add(task)) {
// Make sure the organizer know about the Task config.
transaction.addChange(prepareChange(new PendingTaskFragmentEvent.Builder(
PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED, organizer)
.setTask(task)
.build()));
}
} }
mTmpOrganizerToTransactionMap.get(event.mTaskFragmentOrg.asBinder()) transaction.addChange(prepareChange(event));
.addChange(prepareChange(event));
} }
final int numOrganizers = mTmpOrganizerList.size(); mTmpTaskSet.clear();
for (int i = 0; i < numOrganizers; i++) { dispatchTransactionInfo(organizer, transaction);
final ITaskFragmentOrganizer organizer = mTmpOrganizerList.get(i); pendingEvents.removeAll(candidateEvents);
dispatchTransactionInfo(organizer,
mTmpOrganizerToTransactionMap.get(organizer.asBinder()));
}
mPendingTaskFragmentEvents.removeAll(candidateEvents);
mTmpOrganizerToTransactionMap.clear();
mTmpOrganizerList.clear();
} }
private static boolean isTaskVisible(@NonNull Task task, private static boolean isTaskVisible(@NonNull Task task,
@@ -831,10 +854,16 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
return; return;
} }
final ITaskFragmentOrganizer organizer = taskFragment.getTaskFragmentOrganizer();
final TaskFragmentTransaction transaction = new TaskFragmentTransaction(); final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
// Make sure the organizer know about the Task config.
transaction.addChange(prepareChange(new PendingTaskFragmentEvent.Builder(
PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED, organizer)
.setTask(taskFragment.getTask())
.build()));
transaction.addChange(prepareChange(event)); transaction.addChange(prepareChange(event));
dispatchTransactionInfo(event.mTaskFragmentOrg, transaction); dispatchTransactionInfo(event.mTaskFragmentOrg, transaction);
mPendingTaskFragmentEvents.remove(event); mPendingTaskFragmentEvents.get(organizer.asBinder()).remove(event);
} }
private void dispatchTransactionInfo(@NonNull ITaskFragmentOrganizer organizer, private void dispatchTransactionInfo(@NonNull ITaskFragmentOrganizer organizer,
@@ -867,7 +896,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
case PendingTaskFragmentEvent.EVENT_INFO_CHANGED: case PendingTaskFragmentEvent.EVENT_INFO_CHANGED:
return state.prepareTaskFragmentInfoChanged(taskFragment); return state.prepareTaskFragmentInfoChanged(taskFragment);
case PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED: case PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED:
return state.prepareTaskFragmentParentInfoChanged(taskFragment); return state.prepareTaskFragmentParentInfoChanged(event.mTask);
case PendingTaskFragmentEvent.EVENT_ERROR: case PendingTaskFragmentEvent.EVENT_ERROR:
return state.prepareTaskFragmentError(event.mErrorCallbackToken, taskFragment, return state.prepareTaskFragmentError(event.mErrorCallbackToken, taskFragment,
event.mOpType, event.mException); event.mOpType, event.mException);

View File

@@ -75,6 +75,8 @@ import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/** /**
* Build/Install/Run: * Build/Install/Run:
@@ -92,7 +94,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
private TaskFragmentOrganizerToken mOrganizerToken; private TaskFragmentOrganizerToken mOrganizerToken;
private ITaskFragmentOrganizer mIOrganizer; private ITaskFragmentOrganizer mIOrganizer;
private TaskFragment mTaskFragment; private TaskFragment mTaskFragment;
private TaskFragmentInfo mTaskFragmentInfo;
private IBinder mFragmentToken; private IBinder mFragmentToken;
private WindowContainerTransaction mTransaction; private WindowContainerTransaction mTransaction;
private WindowContainerToken mFragmentWindowToken; private WindowContainerToken mFragmentWindowToken;
@@ -100,14 +101,19 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
private IBinder mErrorToken; private IBinder mErrorToken;
private Rect mTaskFragBounds; private Rect mTaskFragBounds;
@Mock
private TaskFragmentInfo mTaskFragmentInfo;
@Mock
private Task mTask;
@Before @Before
public void setup() { public void setup() {
MockitoAnnotations.initMocks(this);
mWindowOrganizerController = mAtm.mWindowOrganizerController; mWindowOrganizerController = mAtm.mWindowOrganizerController;
mController = mWindowOrganizerController.mTaskFragmentOrganizerController; mController = mWindowOrganizerController.mTaskFragmentOrganizerController;
mOrganizer = new TaskFragmentOrganizer(Runnable::run); mOrganizer = new TaskFragmentOrganizer(Runnable::run);
mOrganizerToken = mOrganizer.getOrganizerToken(); mOrganizerToken = mOrganizer.getOrganizerToken();
mIOrganizer = ITaskFragmentOrganizer.Stub.asInterface(mOrganizerToken.asBinder()); mIOrganizer = ITaskFragmentOrganizer.Stub.asInterface(mOrganizerToken.asBinder());
mTaskFragmentInfo = mock(TaskFragmentInfo.class);
mFragmentToken = new Binder(); mFragmentToken = new Binder();
mTaskFragment = mTaskFragment =
new TaskFragment(mAtm, mFragmentToken, true /* createdByOrganizer */); new TaskFragment(mAtm, mFragmentToken, true /* createdByOrganizer */);
@@ -131,6 +137,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
@Test @Test
public void testCallTaskFragmentCallbackWithoutRegister_throwsException() { public void testCallTaskFragmentCallbackWithoutRegister_throwsException() {
doReturn(mTask).when(mTaskFragment).getTask();
assertThrows(IllegalArgumentException.class, () -> mController assertThrows(IllegalArgumentException.class, () -> mController
.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment)); .onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment));
@@ -140,16 +148,21 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
assertThrows(IllegalArgumentException.class, () -> mController assertThrows(IllegalArgumentException.class, () -> mController
.onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment)); .onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment));
assertThrows(IllegalArgumentException.class, () -> mController
.onTaskFragmentParentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(),
mTaskFragment));
} }
@Test @Test
public void testOnTaskFragmentAppeared() { public void testOnTaskFragmentAppeared() {
mController.registerOrganizer(mIOrganizer); mController.registerOrganizer(mIOrganizer);
// No-op when the TaskFragment is not attached.
mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents();
verify(mOrganizer, never()).onTaskFragmentAppeared(any());
// Send callback when the TaskFragment is attached.
setupMockParent(mTaskFragment, mTask);
mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents(); mController.dispatchPendingEvents();
@@ -159,9 +172,21 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
@Test @Test
public void testOnTaskFragmentInfoChanged() { public void testOnTaskFragmentInfoChanged() {
mController.registerOrganizer(mIOrganizer); mController.registerOrganizer(mIOrganizer);
setupMockParent(mTaskFragment, mTask);
// No-op if onTaskFragmentAppeared is not called yet.
mController.onTaskFragmentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(),
mTaskFragment);
mController.dispatchPendingEvents();
verify(mOrganizer, never()).onTaskFragmentInfoChanged(any());
// Call onTaskFragmentAppeared first.
mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents(); mController.dispatchPendingEvents();
verify(mOrganizer).onTaskFragmentAppeared(any());
// No callback if the info is not changed. // No callback if the info is not changed.
doReturn(true).when(mTaskFragmentInfo).equalsForTaskFragmentOrganizer(any()); doReturn(true).when(mTaskFragmentInfo).equalsForTaskFragmentOrganizer(any());
doReturn(new Configuration()).when(mTaskFragmentInfo).getConfiguration(); doReturn(new Configuration()).when(mTaskFragmentInfo).getConfiguration();
@@ -193,50 +218,75 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
verify(mOrganizer).onTaskFragmentVanished(any()); verify(mOrganizer).onTaskFragmentVanished(any());
} }
@Test
public void testOnTaskFragmentVanished_clearUpRemaining() {
mController.registerOrganizer(mIOrganizer);
setupMockParent(mTaskFragment, mTask);
// Not trigger onTaskFragmentAppeared.
mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents();
verify(mOrganizer, never()).onTaskFragmentAppeared(any());
verify(mOrganizer, never()).onTaskFragmentInfoChanged(any());
verify(mOrganizer, never()).onTaskFragmentParentInfoChanged(anyInt(), any());
verify(mOrganizer).onTaskFragmentVanished(mTaskFragmentInfo);
// Not trigger onTaskFragmentInfoChanged.
// Call onTaskFragmentAppeared before calling onTaskFragmentInfoChanged.
mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents();
clearInvocations(mOrganizer);
doReturn(true).when(mTaskFragmentInfo).equalsForTaskFragmentOrganizer(any());
mController.onTaskFragmentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(),
mTaskFragment);
mController.onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents();
verify(mOrganizer, never()).onTaskFragmentAppeared(any());
verify(mOrganizer, never()).onTaskFragmentInfoChanged(any());
verify(mOrganizer, never()).onTaskFragmentParentInfoChanged(anyInt(), any());
verify(mOrganizer).onTaskFragmentVanished(mTaskFragmentInfo);
}
@Test @Test
public void testOnTaskFragmentParentInfoChanged() { public void testOnTaskFragmentParentInfoChanged() {
mController.registerOrganizer(mIOrganizer); mController.registerOrganizer(mIOrganizer);
final Task parent = mock(Task.class); setupMockParent(mTaskFragment, mTask);
final Configuration parentConfig = new Configuration(); mTask.getConfiguration().smallestScreenWidthDp = 10;
parentConfig.smallestScreenWidthDp = 10;
doReturn(parent).when(mTaskFragment).getTask();
doReturn(parentConfig).when(parent).getConfiguration();
// Task needs to be visible
parent.lastActiveTime = 100;
doReturn(true).when(parent).shouldBeVisible(any());
mTaskFragment.mTaskFragmentAppearedSent = true; mController.onTaskFragmentAppeared(
mController.onTaskFragmentParentInfoChanged(
mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents(); mController.dispatchPendingEvents();
verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mFragmentToken), any()); verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mTask.mTaskId), any());
// No extra callback if the info is not changed. // No extra callback if the info is not changed.
clearInvocations(mOrganizer); clearInvocations(mOrganizer);
mController.onTaskFragmentParentInfoChanged( mController.onTaskFragmentInfoChanged(
mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents(); mController.dispatchPendingEvents();
verify(mOrganizer, never()).onTaskFragmentParentInfoChanged(any(), any()); verify(mOrganizer, never()).onTaskFragmentParentInfoChanged(anyInt(), any());
// Trigger callback if the size is changed. // Trigger callback if the size is changed.
parentConfig.smallestScreenWidthDp = 100; mTask.getConfiguration().smallestScreenWidthDp = 100;
mController.onTaskFragmentParentInfoChanged( mController.onTaskFragmentInfoChanged(
mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents(); mController.dispatchPendingEvents();
verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mFragmentToken), any()); verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mTask.mTaskId), any());
// Trigger callback if the windowing mode is changed. // Trigger callback if the windowing mode is changed.
clearInvocations(mOrganizer); clearInvocations(mOrganizer);
parentConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED); mTask.getConfiguration().windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
mController.onTaskFragmentParentInfoChanged( mController.onTaskFragmentInfoChanged(
mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents(); mController.dispatchPendingEvents();
verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mFragmentToken), any()); verify(mOrganizer).onTaskFragmentParentInfoChanged(eq(mTask.mTaskId), any());
} }
@Test @Test
@@ -1091,4 +1141,15 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
.put(mFragmentToken, mTaskFragment); .put(mFragmentToken, mTaskFragment);
mTaskFragment.getTask().setWindowingMode(WINDOWING_MODE_PINNED); mTaskFragment.getTask().setWindowingMode(WINDOWING_MODE_PINNED);
} }
/** Setups the mock Task as the parent of the given TaskFragment. */
private static void setupMockParent(TaskFragment taskFragment, Task mockParent) {
doReturn(mockParent).when(taskFragment).getTask();
final Configuration taskConfig = new Configuration();
doReturn(taskConfig).when(mockParent).getConfiguration();
// Task needs to be visible
mockParent.lastActiveTime = 100;
doReturn(true).when(mockParent).shouldBeVisible(any());
}
} }