Convert TaskFragmentOrganizer callbacks to a transaction callback

Before, we have several different callbacks, such as
onTaskFragmentAppeared, onTaskFragmentInfoChanged, etc. As migrating to
Shell transition, we need a new model to better sync between app
process, WM Core and Shell.

The flow will be:
1. WM Shell startTransition with a WindowContainerTransaction
2. After apply the WCT, notify TaskFragmentOrganizerController
3. TFOrganizerController pack the TF related events into one transaction
4. TFOrganizerController call deferTransitionReady and send the
   transaction to the TFOrganizer in app process
5. TFOrganizer update TFs based on the transaction and apply a WCT
6. When TFOrganizerController get that WCT, call continueTransitionReady

Bug: 240519866
Test: pass existing
Change-Id: I110953f7b73f565a2b1299ef5c3827eaa39e84b8
This commit is contained in:
Chris Li
2022-07-27 15:43:30 +08:00
parent 8a1fa44759
commit 6b94ca2d5f
6 changed files with 611 additions and 192 deletions

View File

@@ -16,54 +16,9 @@
package android.window;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentTransaction;
/** @hide */
oneway interface ITaskFragmentOrganizer {
void onTaskFragmentAppeared(in TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentInfoChanged(in TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentVanished(in TaskFragmentInfo taskFragmentInfo);
/**
* 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.
*/
void onTaskFragmentParentInfoChanged(in IBinder fragmentToken, in Configuration parentConfig);
/**
* Called when the {@link WindowContainerTransaction} created with
* {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side.
*
* @param errorCallbackToken Token set through {@link
* WindowContainerTransaction#setErrorCallbackToken(IBinder)}
* @param errorBundle Bundle containing the exception, operation type and TaskFragmentInfo
* if any. Should be created with
* {@link TaskFragmentOrganizer#putErrorInfoInBundle}.
*/
void onTaskFragmentError(in IBinder errorCallbackToken, in Bundle errorBundle);
/**
* Called when an Activity is reparented to the Task with organized TaskFragment. For example,
* when an Activity enters and then exits Picture-in-picture, it will be reparented back to its
* orginial Task. In this case, we need to notify the organizer so that it can check if the
* Activity matches any split rule.
*
* @param taskId The Task that the activity is reparented to.
* @param activityIntent The intent that the activity is original launched with.
* @param activityToken If the activity belongs to the same process as the organizer, this
* will be the actual activity token; if the activity belongs to a
* different process, the server will generate a temporary token that
* the organizer can use to reparent the activity through
* {@link WindowContainerTransaction} if needed.
*/
void onActivityReparentToTask(int taskId, in Intent activityIntent, in IBinder activityToken);
void onTransactionReady(in TaskFragmentTransaction transaction);
}

View File

@@ -16,6 +16,13 @@
package android.window;
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_ERROR;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_INFO_CHANGED;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED;
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -27,6 +34,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.view.RemoteAnimationDefinition;
import java.util.List;
import java.util.concurrent.Executor;
/**
@@ -204,6 +212,67 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
public void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent,
@NonNull IBinder activityToken) {}
/**
* Called when the transaction is ready so that the organizer can update the TaskFragments based
* on the changes in transaction.
* @hide
*/
public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) {
final List<TaskFragmentTransaction.Change> changes = transaction.getChanges();
for (TaskFragmentTransaction.Change change : changes) {
// TODO(b/240519866): apply all changes in one WCT.
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());
}
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:
onTaskFragmentVanished(change.getTaskFragmentInfo());
break;
case TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED:
onTaskFragmentParentInfoChanged(
change.getTaskFragmentToken(),
change.getTaskConfiguration());
break;
case TYPE_TASK_FRAGMENT_ERROR:
final Bundle errorBundle = change.getErrorBundle();
onTaskFragmentError(
change.getErrorCallbackToken(),
errorBundle.getParcelable(
KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO, TaskFragmentInfo.class),
errorBundle.getInt(KEY_ERROR_CALLBACK_OP_TYPE),
errorBundle.getSerializable(KEY_ERROR_CALLBACK_EXCEPTION,
java.lang.Throwable.class));
break;
case TYPE_ACTIVITY_REPARENT_TO_TASK:
onActivityReparentToTask(
change.getTaskId(),
change.getActivityIntent(),
change.getActivityToken());
break;
default:
throw new IllegalArgumentException(
"Unknown TaskFragmentEvent=" + change.getType());
}
}
}
@Override
public void applyTransaction(@NonNull WindowContainerTransaction t) {
t.setTaskFragmentOrganizer(mInterface);
@@ -221,51 +290,8 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
private final ITaskFragmentOrganizer mInterface = new ITaskFragmentOrganizer.Stub() {
@Override
public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {
mExecutor.execute(
() -> TaskFragmentOrganizer.this.onTaskFragmentAppeared(taskFragmentInfo));
}
@Override
public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) {
mExecutor.execute(
() -> TaskFragmentOrganizer.this.onTaskFragmentInfoChanged(taskFragmentInfo));
}
@Override
public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
mExecutor.execute(
() -> TaskFragmentOrganizer.this.onTaskFragmentVanished(taskFragmentInfo));
}
@Override
public void onTaskFragmentParentInfoChanged(
@NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {
mExecutor.execute(
() -> TaskFragmentOrganizer.this.onTaskFragmentParentInfoChanged(
fragmentToken, parentConfig));
}
@Override
public void onTaskFragmentError(
@NonNull IBinder errorCallbackToken, @NonNull Bundle errorBundle) {
mExecutor.execute(() -> {
final TaskFragmentInfo info = errorBundle.getParcelable(
KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO, TaskFragmentInfo.class);
TaskFragmentOrganizer.this.onTaskFragmentError(
errorCallbackToken, info,
errorBundle.getInt(KEY_ERROR_CALLBACK_OP_TYPE),
(Throwable) errorBundle.getSerializable(KEY_ERROR_CALLBACK_EXCEPTION,
java.lang.Throwable.class));
});
}
@Override
public void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent,
@NonNull IBinder activityToken) {
mExecutor.execute(
() -> TaskFragmentOrganizer.this.onActivityReparentToTask(
taskId, activityIntent, activityToken));
public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) {
mExecutor.execute(() -> TaskFragmentOrganizer.this.onTransactionReady(transaction));
}
};

View File

@@ -0,0 +1,20 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.window;
parcelable TaskFragmentTransaction;
parcelable TaskFragmentTransaction.Change;

View File

@@ -0,0 +1,335 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.window;
import static java.util.Objects.requireNonNull;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
/**
* Used to communicate information about what are changing on embedded TaskFragments belonging to
* the same TaskFragmentOrganizer. A transaction can contain multiple changes.
* @see TaskFragmentTransaction.Change
* @hide
*/
public final class TaskFragmentTransaction implements Parcelable {
private final ArrayList<Change> mChanges = new ArrayList<>();
public TaskFragmentTransaction() {}
private TaskFragmentTransaction(Parcel in) {
in.readTypedList(mChanges, Change.CREATOR);
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeTypedList(mChanges);
}
/** Adds a {@link Change} to this transaction. */
public void addChange(@Nullable Change change) {
if (change != null) {
mChanges.add(change);
}
}
/** Whether this transaction contains any {@link Change}. */
public boolean isEmpty() {
return mChanges.isEmpty();
}
@NonNull
public List<Change> getChanges() {
return mChanges;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("TaskFragmentTransaction{changes=[");
for (int i = 0; i < mChanges.size(); ++i) {
if (i > 0) {
sb.append(',');
}
sb.append(mChanges.get(i));
}
sb.append("]}");
return sb.toString();
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<TaskFragmentTransaction> CREATOR = new Creator<>() {
@Override
public TaskFragmentTransaction createFromParcel(Parcel in) {
return new TaskFragmentTransaction(in);
}
@Override
public TaskFragmentTransaction[] newArray(int size) {
return new TaskFragmentTransaction[size];
}
};
/** Change type: the TaskFragment is attached to the hierarchy. */
public static final int TYPE_TASK_FRAGMENT_APPEARED = 1;
/** Change type: the status of the TaskFragment is changed. */
public static final int TYPE_TASK_FRAGMENT_INFO_CHANGED = 2;
/** Change type: the TaskFragment is removed form the hierarchy. */
public static final int TYPE_TASK_FRAGMENT_VANISHED = 3;
/** Change type: the status of the parent leaf Task is changed. */
public static final int TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED = 4;
/** Change type: the TaskFragment related operation failed on the server side. */
public static final int TYPE_TASK_FRAGMENT_ERROR = 5;
/**
* Change type: an Activity is reparented to the Task. For example, when an Activity enters and
* then exits Picture-in-picture, it will be reparented back to its original Task. In this case,
* we need to notify the organizer so that it can check if the Activity matches any split rule.
*/
public static final int TYPE_ACTIVITY_REPARENT_TO_TASK = 6;
@IntDef(prefix = { "TYPE_" }, value = {
TYPE_TASK_FRAGMENT_APPEARED,
TYPE_TASK_FRAGMENT_INFO_CHANGED,
TYPE_TASK_FRAGMENT_VANISHED,
TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED,
TYPE_TASK_FRAGMENT_ERROR,
TYPE_ACTIVITY_REPARENT_TO_TASK
})
@Retention(RetentionPolicy.SOURCE)
@interface ChangeType {}
/** Represents the change an embedded TaskFragment undergoes. */
public static final class Change implements Parcelable {
/** @see ChangeType */
@ChangeType
private final int mType;
/** @see #setTaskFragmentToken(IBinder) */
@Nullable
private IBinder mTaskFragmentToken;
/** @see #setTaskFragmentInfo(TaskFragmentInfo) */
@Nullable
private TaskFragmentInfo mTaskFragmentInfo;
/** @see #setTaskId(int) */
private int mTaskId;
/** @see #setTaskConfiguration(Configuration) */
@Nullable
private Configuration mTaskConfiguration;
/** @see #setErrorCallbackToken(IBinder) */
@Nullable
private IBinder mErrorCallbackToken;
/** @see #setErrorBundle(Bundle) */
@Nullable
private Bundle mErrorBundle;
/** @see #setActivityIntent(Intent) */
@Nullable
private Intent mActivityIntent;
/** @see #setActivityToken(IBinder) */
@Nullable
private IBinder mActivityToken;
public Change(@ChangeType int type) {
mType = type;
}
private Change(Parcel in) {
mType = in.readInt();
mTaskFragmentToken = in.readStrongBinder();
mTaskFragmentInfo = in.readTypedObject(TaskFragmentInfo.CREATOR);
mTaskId = in.readInt();
mTaskConfiguration = in.readTypedObject(Configuration.CREATOR);
mErrorCallbackToken = in.readStrongBinder();
mErrorBundle = in.readBundle(TaskFragmentTransaction.class.getClassLoader());
mActivityIntent = in.readTypedObject(Intent.CREATOR);
mActivityToken = in.readStrongBinder();
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(mType);
dest.writeStrongBinder(mTaskFragmentToken);
dest.writeTypedObject(mTaskFragmentInfo, flags);
dest.writeInt(mTaskId);
dest.writeTypedObject(mTaskConfiguration, flags);
dest.writeStrongBinder(mErrorCallbackToken);
dest.writeBundle(mErrorBundle);
dest.writeTypedObject(mActivityIntent, flags);
dest.writeStrongBinder(mActivityToken);
}
/** The change is related to the TaskFragment created with this unique token. */
public Change setTaskFragmentToken(@NonNull IBinder taskFragmentToken) {
mTaskFragmentToken = requireNonNull(taskFragmentToken);
return this;
}
/** Info of the embedded TaskFragment. */
public Change setTaskFragmentInfo(@NonNull TaskFragmentInfo info) {
mTaskFragmentInfo = requireNonNull(info);
return this;
}
/** Task id the parent Task. */
public Change setTaskId(int taskId) {
mTaskId = taskId;
return this;
}
/** Configuration of the parent Task. */
public Change setTaskConfiguration(@NonNull Configuration configuration) {
mTaskConfiguration = requireNonNull(configuration);
return this;
}
/**
* If the {@link #TYPE_TASK_FRAGMENT_ERROR} is from a {@link WindowContainerTransaction}
* from the {@link TaskFragmentOrganizer}, it may come with an error callback token to
* report back.
*/
public Change setErrorCallbackToken(@Nullable IBinder errorCallbackToken) {
mErrorCallbackToken = errorCallbackToken;
return this;
}
/**
* Bundle with necessary info about the failure operation of
* {@link #TYPE_TASK_FRAGMENT_ERROR}.
*/
public Change setErrorBundle(@NonNull Bundle errorBundle) {
mErrorBundle = requireNonNull(errorBundle);
return this;
}
/**
* Intent of the activity that is reparented to the Task for
* {@link #TYPE_ACTIVITY_REPARENT_TO_TASK}.
*/
public Change setActivityIntent(@NonNull Intent intent) {
mActivityIntent = requireNonNull(intent);
return this;
}
/**
* Token of the reparent activity for {@link #TYPE_ACTIVITY_REPARENT_TO_TASK}.
* If the activity belongs to the same process as the organizer, this will be the actual
* activity token; if the activity belongs to a different process, the server will generate
* a temporary token that the organizer can use to reparent the activity through
* {@link WindowContainerTransaction} if needed.
*/
public Change setActivityToken(@NonNull IBinder activityToken) {
mActivityToken = requireNonNull(activityToken);
return this;
}
@ChangeType
public int getType() {
return mType;
}
@Nullable
public IBinder getTaskFragmentToken() {
return mTaskFragmentToken;
}
@Nullable
public TaskFragmentInfo getTaskFragmentInfo() {
return mTaskFragmentInfo;
}
public int getTaskId() {
return mTaskId;
}
@Nullable
public Configuration getTaskConfiguration() {
return mTaskConfiguration;
}
@Nullable
public IBinder getErrorCallbackToken() {
return mErrorCallbackToken;
}
@Nullable
public Bundle getErrorBundle() {
return mErrorBundle;
}
@Nullable
public Intent getActivityIntent() {
return mActivityIntent;
}
@Nullable
public IBinder getActivityToken() {
return mActivityToken;
}
@Override
public String toString() {
return "Change{ type=" + mType + " }";
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<Change> CREATOR = new Creator<>() {
@Override
public Change createFromParcel(Parcel in) {
return new Change(in);
}
@Override
public Change[] newArray(int size) {
return new Change[size];
}
};
}
}

View File

@@ -17,6 +17,12 @@
package com.android.server.wm;
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;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_ERROR;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_INFO_CHANGED;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
@@ -38,6 +44,7 @@ import android.view.RemoteAnimationDefinition;
import android.window.ITaskFragmentOrganizer;
import android.window.ITaskFragmentOrganizerController;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentTransaction;
import com.android.internal.protolog.common.ProtoLog;
@@ -68,6 +75,11 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
private final ArrayList<PendingTaskFragmentEvent> mPendingTaskFragmentEvents =
new ArrayList<>();
/** Map from {@link ITaskFragmentOrganizer} to {@link TaskFragmentTransaction}. */
private final ArrayMap<IBinder, TaskFragmentTransaction> mTmpOrganizerToTransactionMap =
new ArrayMap<>();
private final ArrayList<ITaskFragmentOrganizer> mTmpOrganizerList = new ArrayList<>();
TaskFragmentOrganizerController(ActivityTaskManagerService atm) {
mAtmService = atm;
mGlobalLock = atm.mGlobalLock;
@@ -145,107 +157,138 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
mOrganizer.asBinder().unlinkToDeath(this, 0 /*flags*/);
}
void onTaskFragmentAppeared(TaskFragment tf) {
@NonNull
TaskFragmentTransaction.Change prepareTaskFragmentAppeared(@NonNull TaskFragment tf) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment appeared name=%s", tf.getName());
final TaskFragmentInfo info = tf.getTaskFragmentInfo();
try {
mOrganizer.onTaskFragmentAppeared(info);
mLastSentTaskFragmentInfos.put(tf, info);
tf.mTaskFragmentAppearedSent = true;
} catch (RemoteException e) {
Slog.d(TAG, "Exception sending onTaskFragmentAppeared callback", e);
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());
}
onTaskFragmentParentInfoChanged(tf);
return change;
}
void onTaskFragmentVanished(TaskFragment tf) {
@NonNull
TaskFragmentTransaction.Change prepareTaskFragmentVanished(@NonNull TaskFragment tf) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment vanished name=%s", tf.getName());
try {
mOrganizer.onTaskFragmentVanished(tf.getTaskFragmentInfo());
} catch (RemoteException e) {
Slog.d(TAG, "Exception sending onTaskFragmentVanished callback", e);
}
tf.mTaskFragmentAppearedSent = false;
mLastSentTaskFragmentInfos.remove(tf);
mLastSentTaskFragmentParentConfigs.remove(tf);
return new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_VANISHED)
.setTaskFragmentToken(tf.getFragmentToken())
.setTaskFragmentInfo(tf.getTaskFragmentInfo());
}
void onTaskFragmentInfoChanged(TaskFragment tf) {
// Parent config may have changed. The controller will check if there is any important
// config change for the organizer.
onTaskFragmentParentInfoChanged(tf);
@Nullable
TaskFragmentTransaction.Change prepareTaskFragmentInfoChanged(
@NonNull TaskFragment tf) {
// Check if the info is different from the last reported info.
final TaskFragmentInfo info = tf.getTaskFragmentInfo();
final TaskFragmentInfo lastInfo = mLastSentTaskFragmentInfos.get(tf);
if (info.equalsForTaskFragmentOrganizer(lastInfo) && configurationsAreEqualForOrganizer(
info.getConfiguration(), lastInfo.getConfiguration())) {
return;
// Parent config may have changed. The controller will check if there is any
// important config change for the organizer.
return prepareTaskFragmentParentInfoChanged(tf);
}
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment info changed name=%s",
tf.getName());
try {
mOrganizer.onTaskFragmentInfoChanged(info);
mLastSentTaskFragmentInfos.put(tf, info);
} catch (RemoteException e) {
Slog.d(TAG, "Exception sending onTaskFragmentInfoChanged callback", e);
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;
}
void onTaskFragmentParentInfoChanged(TaskFragment tf) {
// Check if the parent info is different from the last reported parent info.
if (tf.getParent() == null || tf.getParent().asTask() == null) {
mLastSentTaskFragmentParentConfigs.remove(tf);
return;
@Nullable
TaskFragmentTransaction.Change prepareTaskFragmentParentInfoChanged(
@NonNull TaskFragment tf) {
if (!shouldSendTaskFragmentParentInfoChanged(tf)) {
return null;
}
final Task parent = tf.getParent().asTask();
final Task parent = tf.getTask();
final Configuration parentConfig = parent.getConfiguration();
final Configuration lastParentConfig = mLastSentTaskFragmentParentConfigs.get(tf);
if (configurationsAreEqualForOrganizer(parentConfig, lastParentConfig)
&& parentConfig.windowConfiguration.getWindowingMode()
== lastParentConfig.windowConfiguration.getWindowingMode()) {
return;
}
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
"TaskFragment parent info changed name=%s parentTaskId=%d",
tf.getName(), parent.mTaskId);
try {
mOrganizer.onTaskFragmentParentInfoChanged(tf.getFragmentToken(), parentConfig);
mLastSentTaskFragmentParentConfigs.put(tf, new Configuration(parentConfig));
} catch (RemoteException e) {
Slog.d(TAG, "Exception sending onTaskFragmentParentInfoChanged callback", e);
}
mLastSentTaskFragmentParentConfigs.put(tf, new Configuration(parentConfig));
return new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED)
.setTaskFragmentToken(tf.getFragmentToken())
.setTaskId(parent.mTaskId)
.setTaskConfiguration(parent.getConfiguration());
}
void onTaskFragmentError(IBinder errorCallbackToken, @Nullable TaskFragment taskFragment,
int opType, Throwable exception) {
/** 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
TaskFragmentTransaction.Change prepareTaskFragmentError(
@Nullable IBinder errorCallbackToken, @Nullable TaskFragment taskFragment,
int opType, @NonNull Throwable exception) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
"Sending TaskFragment error exception=%s", exception.toString());
final TaskFragmentInfo info =
taskFragment != null ? taskFragment.getTaskFragmentInfo() : null;
final Bundle errorBundle = putErrorInfoInBundle(exception, info, opType);
try {
mOrganizer.onTaskFragmentError(errorCallbackToken, errorBundle);
} catch (RemoteException e) {
Slog.d(TAG, "Exception sending onTaskFragmentError callback", e);
}
return new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_ERROR)
.setErrorCallbackToken(errorCallbackToken)
.setErrorBundle(errorBundle);
}
void onActivityReparentToTask(ActivityRecord activity) {
@Nullable
TaskFragmentTransaction.Change prepareActivityReparentToTask(
@NonNull ActivityRecord activity) {
if (activity.finishing) {
Slog.d(TAG, "Reparent activity=" + activity.token + " is finishing");
return;
return null;
}
final Task task = activity.getTask();
if (task == null || task.effectiveUid != mOrganizerUid) {
Slog.d(TAG, "Reparent activity=" + activity.token
+ " is not in a task belong to the organizer app.");
return;
return null;
}
if (task.isAllowedToEmbedActivity(activity, mOrganizerUid) != EMBEDDING_ALLOWED) {
Slog.d(TAG, "Reparent activity=" + activity.token
+ " is not allowed to be embedded.");
return;
return null;
}
final IBinder activityToken;
@@ -268,11 +311,10 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Activity=%s reparent to taskId=%d",
activity.token, task.mTaskId);
try {
mOrganizer.onActivityReparentToTask(task.mTaskId, activity.intent, activityToken);
} catch (RemoteException e) {
Slog.d(TAG, "Exception sending onActivityReparentToTask callback", e);
}
return new TaskFragmentTransaction.Change(TYPE_ACTIVITY_REPARENT_TO_TASK)
.setTaskId(task.mTaskId)
.setActivityIntent(activity.intent)
.setActivityToken(activityToken);
}
}
@@ -375,7 +417,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
*/
@Nullable
public RemoteAnimationDefinition getRemoteAnimationDefinition(
ITaskFragmentOrganizer organizer, int taskId) {
@NonNull ITaskFragmentOrganizer organizer, int taskId) {
synchronized (mGlobalLock) {
final TaskFragmentOrganizerState organizerState =
mTaskFragmentOrganizerState.get(organizer.asBinder());
@@ -385,12 +427,13 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
}
int getTaskFragmentOrganizerUid(ITaskFragmentOrganizer organizer) {
int getTaskFragmentOrganizerUid(@NonNull ITaskFragmentOrganizer organizer) {
final TaskFragmentOrganizerState state = validateAndGetState(organizer);
return state.mOrganizerUid;
}
void onTaskFragmentAppeared(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
void onTaskFragmentAppeared(@NonNull ITaskFragmentOrganizer organizer,
@NonNull TaskFragment taskFragment) {
final TaskFragmentOrganizerState state = validateAndGetState(organizer);
if (!state.addTaskFragment(taskFragment)) {
return;
@@ -406,19 +449,20 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
}
void onTaskFragmentInfoChanged(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
void onTaskFragmentInfoChanged(@NonNull ITaskFragmentOrganizer organizer,
@NonNull TaskFragment taskFragment) {
handleTaskFragmentInfoChanged(organizer, taskFragment,
PendingTaskFragmentEvent.EVENT_INFO_CHANGED);
}
void onTaskFragmentParentInfoChanged(ITaskFragmentOrganizer organizer,
TaskFragment taskFragment) {
void onTaskFragmentParentInfoChanged(@NonNull ITaskFragmentOrganizer organizer,
@NonNull TaskFragment taskFragment) {
handleTaskFragmentInfoChanged(organizer, taskFragment,
PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED);
}
private void handleTaskFragmentInfoChanged(ITaskFragmentOrganizer organizer,
TaskFragment taskFragment, int eventType) {
private void handleTaskFragmentInfoChanged(@NonNull ITaskFragmentOrganizer organizer,
@NonNull TaskFragment taskFragment, int eventType) {
validateAndGetState(organizer);
if (!taskFragment.mTaskFragmentAppearedSent) {
// Skip if TaskFragment still not appeared.
@@ -444,7 +488,8 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
mPendingTaskFragmentEvents.add(pendingEvent);
}
void onTaskFragmentVanished(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
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);
@@ -467,8 +512,9 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
state.removeTaskFragment(taskFragment);
}
void onTaskFragmentError(ITaskFragmentOrganizer organizer, IBinder errorCallbackToken,
TaskFragment taskFragment, int opType, Throwable exception) {
void onTaskFragmentError(@NonNull ITaskFragmentOrganizer organizer,
@Nullable IBinder errorCallbackToken, @Nullable TaskFragment taskFragment,
int opType, @NonNull Throwable exception) {
validateAndGetState(organizer);
Slog.w(TAG, "onTaskFragmentError ", exception);
final PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent.Builder(
@@ -483,7 +529,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
mAtmService.mWindowManager.mWindowPlacerLocked.requestTraversal();
}
void onActivityReparentToTask(ActivityRecord activity) {
void onActivityReparentToTask(@NonNull ActivityRecord activity) {
final ITaskFragmentOrganizer organizer;
if (activity.mLastTaskFragmentOrganizerBeforePip != null) {
// If the activity is previously embedded in an organized TaskFragment.
@@ -515,11 +561,11 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
mPendingTaskFragmentEvents.add(pendingEvent);
}
boolean isOrganizerRegistered(ITaskFragmentOrganizer organizer) {
boolean isOrganizerRegistered(@NonNull ITaskFragmentOrganizer organizer) {
return mTaskFragmentOrganizerState.containsKey(organizer.asBinder());
}
private void removeOrganizer(ITaskFragmentOrganizer organizer) {
private void removeOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
final TaskFragmentOrganizerState state = validateAndGetState(organizer);
// remove all of the children of the organized TaskFragment
state.dispose();
@@ -539,7 +585,9 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
* we wouldn't register {@link DeathRecipient} for the organizer, and might not remove the
* {@link TaskFragment} after the organizer process died.
*/
private TaskFragmentOrganizerState validateAndGetState(ITaskFragmentOrganizer organizer) {
@NonNull
private TaskFragmentOrganizerState validateAndGetState(
@NonNull ITaskFragmentOrganizer organizer) {
final TaskFragmentOrganizerState state =
mTaskFragmentOrganizerState.get(organizer.asBinder());
if (state == null) {
@@ -672,7 +720,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
@Nullable
private PendingTaskFragmentEvent getLastPendingLifecycleEvent(TaskFragment tf) {
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()) {
@@ -683,7 +731,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
@Nullable
private PendingTaskFragmentEvent getPendingTaskFragmentEvent(TaskFragment taskFragment,
private PendingTaskFragmentEvent getPendingTaskFragmentEvent(@NonNull TaskFragment taskFragment,
int type) {
for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i);
@@ -731,16 +779,36 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
candidateEvents.add(event);
}
final int numEvents = candidateEvents.size();
if (numEvents == 0) {
return;
}
mTmpOrganizerToTransactionMap.clear();
mTmpOrganizerList.clear();
for (int i = 0; i < numEvents; i++) {
dispatchEvent(candidateEvents.get(i));
final PendingTaskFragmentEvent event = candidateEvents.get(i);
if (!mTmpOrganizerToTransactionMap.containsKey(event.mTaskFragmentOrg.asBinder())) {
mTmpOrganizerToTransactionMap.put(event.mTaskFragmentOrg.asBinder(),
new TaskFragmentTransaction());
mTmpOrganizerList.add(event.mTaskFragmentOrg);
}
mTmpOrganizerToTransactionMap.get(event.mTaskFragmentOrg.asBinder())
.addChange(prepareChange(event));
}
if (numEvents > 0) {
mPendingTaskFragmentEvents.removeAll(candidateEvents);
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();
}
private static boolean isTaskVisible(Task task, ArrayList<Task> knownVisibleTasks,
ArrayList<Task> knownInvisibleTasks) {
private static boolean isTaskVisible(@NonNull Task task,
@NonNull ArrayList<Task> knownVisibleTasks,
@NonNull ArrayList<Task> knownInvisibleTasks) {
if (knownVisibleTasks.contains(task)) {
return true;
}
@@ -756,44 +824,57 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
}
void dispatchPendingInfoChangedEvent(TaskFragment taskFragment) {
PendingTaskFragmentEvent event = getPendingTaskFragmentEvent(taskFragment,
void dispatchPendingInfoChangedEvent(@NonNull TaskFragment taskFragment) {
final PendingTaskFragmentEvent event = getPendingTaskFragmentEvent(taskFragment,
PendingTaskFragmentEvent.EVENT_INFO_CHANGED);
if (event == null) {
return;
}
dispatchEvent(event);
final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
transaction.addChange(prepareChange(event));
dispatchTransactionInfo(event.mTaskFragmentOrg, transaction);
mPendingTaskFragmentEvents.remove(event);
}
private void dispatchEvent(PendingTaskFragmentEvent event) {
private void dispatchTransactionInfo(@NonNull ITaskFragmentOrganizer organizer,
@NonNull TaskFragmentTransaction transaction) {
if (transaction.isEmpty()) {
return;
}
try {
organizer.onTransactionReady(transaction);
} catch (RemoteException e) {
Slog.d(TAG, "Exception sending TaskFragmentTransaction", e);
}
}
@Nullable
private TaskFragmentTransaction.Change prepareChange(
@NonNull PendingTaskFragmentEvent event) {
final ITaskFragmentOrganizer taskFragmentOrg = event.mTaskFragmentOrg;
final TaskFragment taskFragment = event.mTaskFragment;
final TaskFragmentOrganizerState state =
mTaskFragmentOrganizerState.get(taskFragmentOrg.asBinder());
if (state == null) {
return;
return null;
}
switch (event.mEventType) {
case PendingTaskFragmentEvent.EVENT_APPEARED:
state.onTaskFragmentAppeared(taskFragment);
break;
return state.prepareTaskFragmentAppeared(taskFragment);
case PendingTaskFragmentEvent.EVENT_VANISHED:
state.onTaskFragmentVanished(taskFragment);
break;
return state.prepareTaskFragmentVanished(taskFragment);
case PendingTaskFragmentEvent.EVENT_INFO_CHANGED:
state.onTaskFragmentInfoChanged(taskFragment);
break;
return state.prepareTaskFragmentInfoChanged(taskFragment);
case PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED:
state.onTaskFragmentParentInfoChanged(taskFragment);
break;
return state.prepareTaskFragmentParentInfoChanged(taskFragment);
case PendingTaskFragmentEvent.EVENT_ERROR:
state.onTaskFragmentError(event.mErrorCallbackToken, taskFragment, event.mOpType,
event.mException);
break;
return state.prepareTaskFragmentError(event.mErrorCallbackToken, taskFragment,
event.mOpType, event.mException);
case PendingTaskFragmentEvent.EVENT_ACTIVITY_REPARENT_TO_TASK:
state.onActivityReparentToTask(event.mActivity);
return state.prepareActivityReparentToTask(event.mActivity);
default:
throw new IllegalArgumentException("Unknown TaskFragmentEvent=" + event.mEventType);
}
}

View File

@@ -199,9 +199,11 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
final Task parent = mock(Task.class);
final Configuration parentConfig = new Configuration();
parentConfig.smallestScreenWidthDp = 10;
doReturn(parent).when(mTaskFragment).getParent();
doReturn(parent).when(mTaskFragment).getTask();
doReturn(parentConfig).when(parent).getConfiguration();
doReturn(parent).when(parent).asTask();
// Task needs to be visible
parent.lastActiveTime = 100;
doReturn(true).when(parent).shouldBeVisible(any());
mTaskFragment.mTaskFragmentAppearedSent = true;
mController.onTaskFragmentParentInfoChanged(