Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6b94ca2d authored by Chris Li's avatar Chris Li
Browse files

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
parent 8a1fa447
Loading
Loading
Loading
Loading
+2 −47
Original line number Diff line number Diff line
@@ -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);
}
+71 −45
Original line number Diff line number Diff line
@@ -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));
        }
    };

+20 −0
Original line number Diff line number Diff line
/*
 * 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;
+335 −0
Original line number Diff line number Diff line
/*
 * 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];
            }
        };
    }
}
+184 −103

File changed.

Preview size limit exceeded, changes collapsed.

Loading