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

Commit 6a5daa98 authored by Chris Li's avatar Chris Li Committed by Android (Google) Code Review
Browse files

Merge "Synchronize window config updates (7/n)" into main

parents 1576f845 def520df
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -58,6 +58,11 @@ public abstract class ActivityLifecycleItem extends ActivityTransactionItem {
        super(in);
    }

    @Override
    boolean isActivityLifecycleItem() {
        return true;
    }

    /** A final lifecycle state that an activity should reach. */
    @LifecycleState
    public abstract int getTargetState();
+97 −8
Original line number Diff line number Diff line
@@ -45,6 +45,13 @@ import java.util.Objects;
 */
public class ClientTransaction implements Parcelable, ObjectPoolItem {

    /**
     * List of transaction items that should be executed in order. Including both
     * {@link ActivityLifecycleItem} and other {@link ClientTransactionItem}.
     */
    @Nullable
    private List<ClientTransactionItem> mTransactionItems;

    /** A list of individual callbacks to a client. */
    @UnsupportedAppUsage
    private List<ClientTransactionItem> mActivityCallbacks;
@@ -64,9 +71,32 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
    }

    /**
     * Add a message to the end of the sequence of callbacks.
     * Adds a message to the end of the sequence of transaction items.
     * @param item A single message that can contain a client activity/window request/callback.
     * TODO(b/260873529): replace both {@link #addCallback} and {@link #setLifecycleStateRequest}.
     */
    public void addTransactionItem(@NonNull ClientTransactionItem item) {
        if (mTransactionItems == null) {
            mTransactionItems = new ArrayList<>();
        }
        mTransactionItems.add(item);
    }

    /**
     * Gets the list of client window requests/callbacks.
     * TODO(b/260873529): must be non null after remove the deprecated methods.
     */
    @Nullable
    public List<ClientTransactionItem> getTransactionItems() {
        return mTransactionItems;
    }

    /**
     * Adds a message to the end of the sequence of callbacks.
     * @param activityCallback A single message that can contain a lifecycle request/callback.
     * @deprecated use {@link #addTransactionItem(ClientTransactionItem)} instead.
     */
    @Deprecated
    public void addCallback(@NonNull ClientTransactionItem activityCallback) {
        if (mActivityCallbacks == null) {
            mActivityCallbacks = new ArrayList<>();
@@ -74,25 +104,35 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
        mActivityCallbacks.add(activityCallback);
    }

    /** Get the list of callbacks. */
    /**
     * Gets the list of callbacks.
     * @deprecated use {@link #getTransactionItems()} instead.
     */
    @Nullable
    @VisibleForTesting
    @UnsupportedAppUsage
    @Deprecated
    public List<ClientTransactionItem> getCallbacks() {
        return mActivityCallbacks;
    }

    /** Get the target state lifecycle request. */
    /**
     * Gets the target state lifecycle request.
     * @deprecated use {@link #getTransactionItems()} instead.
     */
    @VisibleForTesting(visibility = PACKAGE)
    @UnsupportedAppUsage
    @Deprecated
    public ActivityLifecycleItem getLifecycleStateRequest() {
        return mLifecycleStateRequest;
    }

    /**
     * Set the lifecycle state in which the client should be after executing the transaction.
     * Sets the lifecycle state in which the client should be after executing the transaction.
     * @param stateRequest A lifecycle request initialized with right parameters.
     * @deprecated use {@link #addTransactionItem(ClientTransactionItem)} instead.
     */
    @Deprecated
    public void setLifecycleStateRequest(@NonNull ActivityLifecycleItem stateRequest) {
        mLifecycleStateRequest = stateRequest;
    }
@@ -103,6 +143,14 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
     *                                 requested by transaction items.
     */
    public void preExecute(@NonNull ClientTransactionHandler clientTransactionHandler) {
        if (mTransactionItems != null) {
            final int size = mTransactionItems.size();
            for (int i = 0; i < size; ++i) {
                mTransactionItems.get(i).preExecute(clientTransactionHandler);
            }
            return;
        }

        if (mActivityCallbacks != null) {
            final int size = mActivityCallbacks.size();
            for (int i = 0; i < size; ++i) {
@@ -147,12 +195,19 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {

    @Override
    public void recycle() {
        if (mTransactionItems != null) {
            int size = mTransactionItems.size();
            for (int i = 0; i < size; i++) {
                mTransactionItems.get(i).recycle();
            }
            mTransactionItems = null;
        }
        if (mActivityCallbacks != null) {
            int size = mActivityCallbacks.size();
            for (int i = 0; i < size; i++) {
                mActivityCallbacks.get(i).recycle();
            }
            mActivityCallbacks.clear();
            mActivityCallbacks = null;
        }
        if (mLifecycleStateRequest != null) {
            mLifecycleStateRequest.recycle();
@@ -165,8 +220,15 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
    // Parcelable implementation

    /** Write to Parcel. */
    @SuppressWarnings("AndroidFrameworkEfficientParcelable") // Item class is not final.
    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        final boolean writeTransactionItems = mTransactionItems != null;
        dest.writeBoolean(writeTransactionItems);
        if (writeTransactionItems) {
            dest.writeParcelableList(mTransactionItems, flags);
        }

        dest.writeParcelable(mLifecycleStateRequest, flags);
        final boolean writeActivityCallbacks = mActivityCallbacks != null;
        dest.writeBoolean(writeActivityCallbacks);
@@ -177,11 +239,20 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {

    /** Read from Parcel. */
    private ClientTransaction(@NonNull Parcel in) {
        mLifecycleStateRequest = in.readParcelable(getClass().getClassLoader(), android.app.servertransaction.ActivityLifecycleItem.class);
        final boolean readTransactionItems = in.readBoolean();
        if (readTransactionItems) {
            mTransactionItems = new ArrayList<>();
            in.readParcelableList(mTransactionItems, getClass().getClassLoader(),
                    ClientTransactionItem.class);
        }

        mLifecycleStateRequest = in.readParcelable(getClass().getClassLoader(),
                ActivityLifecycleItem.class);
        final boolean readActivityCallbacks = in.readBoolean();
        if (readActivityCallbacks) {
            mActivityCallbacks = new ArrayList<>();
            in.readParcelableList(mActivityCallbacks, getClass().getClassLoader(), android.app.servertransaction.ClientTransactionItem.class);
            in.readParcelableList(mActivityCallbacks, getClass().getClassLoader(),
                    ClientTransactionItem.class);
        }
    }

@@ -209,7 +280,8 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
            return false;
        }
        final ClientTransaction other = (ClientTransaction) o;
        return Objects.equals(mActivityCallbacks, other.mActivityCallbacks)
        return Objects.equals(mTransactionItems, other.mTransactionItems)
                && Objects.equals(mActivityCallbacks, other.mActivityCallbacks)
                && Objects.equals(mLifecycleStateRequest, other.mLifecycleStateRequest)
                && mClient == other.mClient;
    }
@@ -217,6 +289,7 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
    @Override
    public int hashCode() {
        int result = 17;
        result = 31 * result + Objects.hashCode(mTransactionItems);
        result = 31 * result + Objects.hashCode(mActivityCallbacks);
        result = 31 * result + Objects.hashCode(mLifecycleStateRequest);
        result = 31 * result + Objects.hashCode(mClient);
@@ -227,6 +300,22 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem {
    void dump(@NonNull String prefix, @NonNull PrintWriter pw,
            @NonNull ClientTransactionHandler transactionHandler) {
        pw.append(prefix).println("ClientTransaction{");
        if (mTransactionItems != null) {
            pw.append(prefix).print("  transactionItems=[");
            final String itemPrefix = prefix + "    ";
            final int size = mTransactionItems.size();
            if (size > 0) {
                pw.println();
                for (int i = 0; i < size; i++) {
                    mTransactionItems.get(i).dump(itemPrefix, pw, transactionHandler);
                }
                pw.append(prefix).println("  ]");
            } else {
                pw.println("]");
            }
            pw.append(prefix).println("}");
            return;
        }
        pw.append(prefix).print("  callbacks=[");
        final String itemPrefix = prefix + "    ";
        final int size = mActivityCallbacks != null ? mActivityCallbacks.size() : 0;
+7 −0
Original line number Diff line number Diff line
@@ -72,6 +72,13 @@ public abstract class ClientTransactionItem implements BaseClientRequest, Parcel
        return null;
    }

    /**
     * Whether this is a {@link ActivityLifecycleItem}.
     */
    boolean isActivityLifecycleItem() {
        return false;
    }

    /** Dumps this transaction item. */
    void dump(@NonNull String prefix, @NonNull PrintWriter pw,
            @NonNull ClientTransactionHandler transactionHandler) {
+105 −64
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
import static android.app.servertransaction.TransactionExecutorHelper.getShortActivityName;
import static android.app.servertransaction.TransactionExecutorHelper.getStateName;
import static android.app.servertransaction.TransactionExecutorHelper.lastCallbackRequestingState;
import static android.app.servertransaction.TransactionExecutorHelper.shouldExcludeLastLifecycleState;
import static android.app.servertransaction.TransactionExecutorHelper.tId;
import static android.app.servertransaction.TransactionExecutorHelper.transactionToString;

@@ -61,6 +62,9 @@ public class TransactionExecutor {
    private final PendingTransactionActions mPendingActions = new PendingTransactionActions();
    private final TransactionExecutorHelper mHelper = new TransactionExecutorHelper();

    /** Keeps track of display ids whose Configuration got updated within a transaction. */
    private final ArraySet<Integer> mConfigUpdatedDisplayIds = new ArraySet<>();

    /** Initialize an instance with transaction handler, that will execute all requested actions. */
    public TransactionExecutor(@NonNull ClientTransactionHandler clientTransactionHandler) {
        mTransactionHandler = clientTransactionHandler;
@@ -79,15 +83,52 @@ public class TransactionExecutor {
            Slog.d(TAG, transactionToString(transaction, mTransactionHandler));
        }

        if (transaction.getTransactionItems() != null) {
            executeTransactionItems(transaction);
        } else {
            // TODO(b/260873529): cleanup after launch.
            executeCallbacks(transaction);
            executeLifecycleState(transaction);
        }

        if (!mConfigUpdatedDisplayIds.isEmpty()) {
            // Whether this transaction should trigger DisplayListener#onDisplayChanged.
            final ClientTransactionListenerController controller =
                    ClientTransactionListenerController.getInstance();
            final int displayCount = mConfigUpdatedDisplayIds.size();
            for (int i = 0; i < displayCount; i++) {
                final int displayId = mConfigUpdatedDisplayIds.valueAt(i);
                controller.onDisplayChanged(displayId);
            }
            mConfigUpdatedDisplayIds.clear();
        }

        mPendingActions.clear();
        if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "End resolving transaction");
    }

    /** Cycle through all states requested by callbacks and execute them at proper times. */
    /** Cycles through all transaction items and execute them at proper times. */
    @VisibleForTesting
    public void executeTransactionItems(@NonNull ClientTransaction transaction) {
        final List<ClientTransactionItem> items = transaction.getTransactionItems();
        final int size = items.size();
        for (int i = 0; i < size; i++) {
            final ClientTransactionItem item = items.get(i);
            if (item.isActivityLifecycleItem()) {
                executeLifecycleItem(transaction, (ActivityLifecycleItem) item);
            } else {
                executeNonLifecycleItem(transaction, item,
                        shouldExcludeLastLifecycleState(items, i));
            }
        }
    }

    /**
     * Cycle through all states requested by callbacks and execute them at proper times.
     * @deprecated use {@link #executeTransactionItems} instead.
     */
    @VisibleForTesting
    @Deprecated
    public void executeCallbacks(@NonNull ClientTransaction transaction) {
        final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
        if (callbacks == null || callbacks.isEmpty()) {
@@ -105,12 +146,20 @@ public class TransactionExecutor {
        // Index of the last callback that requests some post-execution state.
        final int lastCallbackRequestingState = lastCallbackRequestingState(transaction);

        // Keep track of display ids whose Configuration got updated with this transaction.
        ArraySet<Integer> configUpdatedDisplays = null;

        final int size = callbacks.size();
        for (int i = 0; i < size; ++i) {
            final ClientTransactionItem item = callbacks.get(i);

            // Skip the very last transition and perform it by explicit state request instead.
            final int postExecutionState = item.getPostExecutionState();
            final boolean shouldExcludeLastLifecycleState = postExecutionState != UNDEFINED
                    && i == lastCallbackRequestingState && finalState == postExecutionState;
            executeNonLifecycleItem(transaction, item, shouldExcludeLastLifecycleState);
        }
    }

    private void executeNonLifecycleItem(@NonNull ClientTransaction transaction,
            @NonNull ClientTransactionItem item, boolean shouldExcludeLastLifecycleState) {
        final IBinder token = item.getActivityToken();
        ActivityClientRecord r = mTransactionHandler.getActivityClient(token);

@@ -119,7 +168,7 @@ public class TransactionExecutor {
            // The activity has not been created but has been requested to destroy, so all
            // transactions for the token are just like being cancelled.
            Slog.w(TAG, "Skip pre-destroyed transaction item:\n" + item);
                continue;
            return;
        }

        if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Resolving callback: " + item);
@@ -127,7 +176,7 @@ public class TransactionExecutor {

        if (item.shouldHaveDefinedPreExecutionState()) {
            final int closestPreExecutionState = mHelper.getClosestPreExecutionState(r,
                        item.getPostExecutionState());
                    postExecutionState);
            if (closestPreExecutionState != UNDEFINED) {
                cycleToPath(r, closestPreExecutionState, transaction);
            }
@@ -149,10 +198,7 @@ public class TransactionExecutor {
            final Configuration postExecutedConfig = configUpdatedContext.getResources()
                    .getConfiguration();
            if (!areConfigurationsEqualForDisplay(postExecutedConfig, preExecutedConfig)) {
                    if (configUpdatedDisplays == null) {
                        configUpdatedDisplays = new ArraySet<>();
                    }
                    configUpdatedDisplays.add(configUpdatedContext.getDisplayId());
                mConfigUpdatedDisplayIds.add(configUpdatedContext.getDisplayId());
            }
        }

@@ -163,25 +209,15 @@ public class TransactionExecutor {
        }

        if (postExecutionState != UNDEFINED && r != null) {
                // Skip the very last transition and perform it by explicit state request instead.
                final boolean shouldExcludeLastTransition =
                        i == lastCallbackRequestingState && finalState == postExecutionState;
                cycleToPath(r, postExecutionState, shouldExcludeLastTransition, transaction);
            cycleToPath(r, postExecutionState, shouldExcludeLastLifecycleState, transaction);
        }
    }

        if (configUpdatedDisplays != null) {
            final ClientTransactionListenerController controller =
                    ClientTransactionListenerController.getInstance();
            final int displayCount = configUpdatedDisplays.size();
            for (int i = 0; i < displayCount; i++) {
                final int displayId = configUpdatedDisplays.valueAt(i);
                controller.onDisplayChanged(displayId);
            }
        }
    }

    /** Transition to the final state if requested by the transaction. */
    /**
     * Transition to the final state if requested by the transaction.
     * @deprecated use {@link #executeTransactionItems} instead
     */
    @Deprecated
    private void executeLifecycleState(@NonNull ClientTransaction transaction) {
        final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
        if (lifecycleItem == null) {
@@ -189,6 +225,11 @@ public class TransactionExecutor {
            return;
        }

        executeLifecycleItem(transaction, lifecycleItem);
    }

    private void executeLifecycleItem(@NonNull ClientTransaction transaction,
            @NonNull ActivityLifecycleItem lifecycleItem) {
        final IBinder token = lifecycleItem.getActivityToken();
        final ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
        if (DEBUG_RESOLVER) {
+72 −7
Original line number Diff line number Diff line
@@ -236,21 +236,39 @@ public class TransactionExecutorHelper {
     * index 1 will be returned, because ActivityResult request on position 1 will be the last
     * request that moves activity to the RESUMED state where it will eventually end.
     */
    static int lastCallbackRequestingState(ClientTransaction transaction) {
    static int lastCallbackRequestingState(@NonNull ClientTransaction transaction) {
        final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
        if (callbacks == null || callbacks.size() == 0) {
        if (callbacks == null || callbacks.isEmpty()
                || transaction.getLifecycleStateRequest() == null) {
            return -1;
        }
        return lastCallbackRequestingStateIndex(callbacks, 0, callbacks.size() - 1,
                transaction.getLifecycleStateRequest().getActivityToken());
    }

    /**
     * Returns the index of the last callback between the start index and last index that requests
     * the state for the given activity token in which that activity will be after execution.
     * If there is a group of callbacks in the end that requests the same specific state or doesn't
     * request any - we will find the first one from such group.
     *
     * E.g. ActivityResult requests RESUMED post-execution state, Configuration does not request any
     * specific state. If there is a sequence
     *   Configuration - ActivityResult - Configuration - ActivityResult
     * index 1 will be returned, because ActivityResult request on position 1 will be the last
     * request that moves activity to the RESUMED state where it will eventually end.
     */
    private static int lastCallbackRequestingStateIndex(@NonNull List<ClientTransactionItem> items,
            int startIndex, int lastIndex, @NonNull IBinder activityToken) {
        // Go from the back of the list to front, look for the request closes to the beginning that
        // requests the state in which activity will end after all callbacks are executed.
        int lastRequestedState = UNDEFINED;
        int lastRequestingCallback = -1;
        for (int i = callbacks.size() - 1; i >= 0; i--) {
            final ClientTransactionItem callback = callbacks.get(i);
            final int postExecutionState = callback.getPostExecutionState();
            if (postExecutionState != UNDEFINED) {
                // Found a callback that requests some post-execution state.
        for (int i = lastIndex; i >= startIndex; i--) {
            final ClientTransactionItem item = items.get(i);
            final int postExecutionState = item.getPostExecutionState();
            if (postExecutionState != UNDEFINED && activityToken.equals(item.getActivityToken())) {
                // Found a callback that requests some post-execution state for the given activity.
                if (lastRequestedState == UNDEFINED || lastRequestedState == postExecutionState) {
                    // It's either a first-from-end callback that requests state or it requests
                    // the same state as the last one. In both cases, we will use it as the new
@@ -266,6 +284,53 @@ public class TransactionExecutorHelper {
        return lastRequestingCallback;
    }

    /**
     * For the transaction item at {@code currentIndex}, if it is requesting post execution state,
     * whether or not to exclude the last state. This only returns {@code true} when there is a
     * following explicit {@link ActivityLifecycleItem} requesting the same state for the same
     * activity, so that last state will be covered by the following {@link ActivityLifecycleItem}.
     */
    static boolean shouldExcludeLastLifecycleState(@NonNull List<ClientTransactionItem> items,
            int currentIndex) {
        final ClientTransactionItem item = items.get(currentIndex);
        final IBinder activityToken = item.getActivityToken();
        final int postExecutionState = item.getPostExecutionState();
        if (activityToken == null || postExecutionState == UNDEFINED) {
            // Not a transaction item requesting post execution state.
            return false;
        }
        final int nextLifecycleItemIndex = findNextLifecycleItemIndex(items, currentIndex + 1,
                activityToken);
        if (nextLifecycleItemIndex == -1) {
            // No following ActivityLifecycleItem for this activity token.
            return false;
        }
        final ActivityLifecycleItem lifecycleItem =
                (ActivityLifecycleItem) items.get(nextLifecycleItemIndex);
        if (postExecutionState != lifecycleItem.getTargetState()) {
            // The explicit ActivityLifecycleItem is not requesting the same state.
            return false;
        }
        // Only exclude for the first non-lifecycle item that requests the same specific state.
        return currentIndex == lastCallbackRequestingStateIndex(items, currentIndex,
                nextLifecycleItemIndex - 1, activityToken);
    }

    /**
     * Finds the index of the next {@link ActivityLifecycleItem} for the given activity token.
     */
    private static int findNextLifecycleItemIndex(@NonNull List<ClientTransactionItem> items,
            int startIndex, @NonNull IBinder activityToken) {
        final int size = items.size();
        for (int i = startIndex; i < size; i++) {
            final ClientTransactionItem item = items.get(i);
            if (item.isActivityLifecycleItem() && item.getActivityToken().equals(activityToken)) {
                return i;
            }
        }
        return -1;
    }

    /** Dump transaction to string. */
    static String transactionToString(@NonNull ClientTransaction transaction,
            @NonNull ClientTransactionHandler transactionHandler) {
Loading