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.IBinder;
import android.os.RemoteException;
import android.util.SparseArray;
import android.view.RemoteAnimationDefinition;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
@@ -72,6 +74,12 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
*/
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) {
mExecutor = executor;
}
@@ -160,6 +168,27 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
public void onTaskFragmentParentInfoChanged(
@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
* {@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();
for (TaskFragmentTransaction.Change change : changes) {
// TODO(b/240519866): apply all changes in one WCT.
final int taskId = change.getTaskId();
switch (change.getType()) {
case TYPE_TASK_FRAGMENT_APPEARED:
onTaskFragmentAppeared(change.getTaskFragmentInfo());
if (change.getTaskConfiguration() != null) {
// TODO(b/240519866): convert to pass TaskConfiguration for all TFs in the
// same Task
onTaskFragmentParentInfoChanged(
change.getTaskFragmentToken(),
change.getTaskConfiguration());
// TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next
// release.
if (!mTaskIdToFragmentTokens.contains(taskId)) {
mTaskIdToFragmentTokens.put(taskId, new ArrayList<>());
}
mTaskIdToFragmentTokens.get(taskId).add(change.getTaskFragmentToken());
onTaskFragmentParentInfoChanged(change.getTaskFragmentToken(),
mTaskIdToConfigurations.get(taskId));
onTaskFragmentAppeared(change.getTaskFragmentInfo());
break;
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());
break;
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());
break;
case TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED:
onTaskFragmentParentInfoChanged(
change.getTaskFragmentToken(),
change.getTaskConfiguration());
// TODO(b/240519866): doing so to keep CTS compatibility. Remove in the next
// release.
mTaskIdToConfigurations.put(taskId, change.getTaskConfiguration());
onTaskFragmentParentInfoChanged(taskId, change.getTaskConfiguration());
break;
case TYPE_TASK_FRAGMENT_ERROR:
final Bundle errorBundle = change.getErrorBundle();

View File

@@ -51,12 +51,6 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
@VisibleForTesting
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;
@VisibleForTesting
TaskFragmentAnimationController mAnimationController;
@@ -68,8 +62,7 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
@NonNull Configuration parentConfig);
void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig);
void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent,
@NonNull IBinder activityToken);
void onTaskFragmentError(@Nullable TaskFragmentInfo taskFragmentInfo, int opType);
@@ -300,7 +293,6 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
@Override
public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
mFragmentInfos.remove(taskFragmentInfo.getFragmentToken());
mFragmentParentConfigs.remove(taskFragmentInfo.getFragmentToken());
if (mCallback != null) {
mCallback.onTaskFragmentVanished(taskFragmentInfo);
@@ -308,12 +300,9 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
}
@Override
public void onTaskFragmentParentInfoChanged(
@NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {
mFragmentParentConfigs.put(fragmentToken, parentConfig);
public void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig) {
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);
if (container.isFinished()) {
mPresenter.cleanupContainer(container, false /* shouldFinishDependent */);
} else {
// Update with the latest Task configuration.
mPresenter.updateContainer(container);
}
updateCallbackIfNecessary();
}
@@ -233,19 +236,30 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
@Override
public void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken,
@NonNull Configuration parentConfig) {
public void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig) {
synchronized (mLock) {
final TaskFragmentContainer container = getContainer(fragmentToken);
if (container != null) {
onTaskConfigurationChanged(container.getTaskId(), parentConfig);
if (isInPictureInPicture(parentConfig)) {
// No need to update presentation in PIP until the Task exit PIP.
return;
}
mPresenter.updateContainer(container);
updateCallbackIfNecessary();
onTaskConfigurationChanged(taskId, parentConfig);
if (isInPictureInPicture(parentConfig)) {
// No need to update presentation in PIP until the Task exit PIP.
return;
}
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());
if (r.isEmbedded()) {
final TaskFragment organizedTaskFragment = r.getOrganizedTaskFragment();
if (organizedTaskFragment != null) {
// Sending TaskFragmentInfo to client to ensure the info is updated before
// the activity creation.
mService.mTaskFragmentOrganizerController.dispatchPendingInfoChangedEvent(
r.getOrganizedTaskFragment());
organizedTaskFragment);
}
// Create activity launch transaction.

View File

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

View File

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