Merge "Convert TaskFragmentOrganizer callbacks to a transaction callback" into tm-qpr-dev

This commit is contained in:
TreeHugger Robot
2022-08-04 11:12:36 +00:00
committed by Android (Google) Code Review
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(