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

Commit b8871190 authored by Chris Li's avatar Chris Li
Browse files

Ensure onDisplayChanged is always triggered

Before, when Configuration is updated as a result of window relayout,
the onDisplayChanged will not be triggered.

Bug: 260873529
Test: atest FrameworksCoreTests:ClientTransactionListenerControllerTest
Change-Id: I15be9a006d2d760559453f9825193ca5923b1ebf
parent 55a2e077
Loading
Loading
Loading
Loading
+23 −1
Original line number Diff line number Diff line
@@ -2608,7 +2608,14 @@ public final class ActivityThread extends ClientTransactionHandler
                    break;
                case EXECUTE_TRANSACTION:
                    final ClientTransaction transaction = (ClientTransaction) msg.obj;
                    final ClientTransactionListenerController controller =
                            ClientTransactionListenerController.getInstance();
                    controller.onClientTransactionStarted();
                    try {
                        mTransactionExecutor.execute(transaction);
                    } finally {
                        controller.onClientTransactionFinished();
                    }
                    if (isSystem()) {
                        // Client transactions inside system process are recycled on the client side
                        // instead of ClientLifecycleManager to avoid being cleared before this
@@ -6747,6 +6754,21 @@ public final class ActivityThread extends ClientTransactionHandler
    void handleActivityConfigurationChanged(@NonNull ActivityClientRecord r,
            @NonNull Configuration overrideConfig, int displayId,
            @NonNull ActivityWindowInfo activityWindowInfo, boolean alwaysReportChange) {
        final ClientTransactionListenerController controller =
                ClientTransactionListenerController.getInstance();
        final Context contextToUpdate = r.activity;
        controller.onContextConfigurationPreChanged(contextToUpdate);
        try {
            handleActivityConfigurationChangedInner(r, overrideConfig, displayId,
                    activityWindowInfo, alwaysReportChange);
        } finally {
            controller.onContextConfigurationPostChanged(contextToUpdate);
        }
    }

    private void handleActivityConfigurationChangedInner(@NonNull ActivityClientRecord r,
            @NonNull Configuration overrideConfig, int displayId,
            @NonNull ActivityWindowInfo activityWindowInfo, boolean alwaysReportChange) {
        synchronized (mPendingOverrideConfigs) {
            final Configuration pendingOverrideConfig = mPendingOverrideConfigs.get(r.token);
            if (overrideConfig.isOtherSeqNewer(pendingOverrideConfig)) {
+19 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.servertransaction.ClientTransactionListenerController;
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.res.CompatibilityInfo;
@@ -145,6 +146,24 @@ class ConfigurationController {
     */
    void handleConfigurationChanged(@Nullable Configuration config,
            @Nullable CompatibilityInfo compat) {
        final ClientTransactionListenerController controller =
                ClientTransactionListenerController.getInstance();
        final Context contextToUpdate = ActivityThread.currentApplication();
        controller.onContextConfigurationPreChanged(contextToUpdate);
        try {
            handleConfigurationChangedInner(config, compat);
        } finally {
            controller.onContextConfigurationPostChanged(contextToUpdate);
        }
    }

    /**
     * Update the configuration to latest.
     * @param config The new configuration.
     * @param compat The new compatibility information.
     */
    private void handleConfigurationChangedInner(@Nullable Configuration config,
            @Nullable CompatibilityInfo compat) {
        int configDiff;
        boolean equivalent;

+1 −0
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ public abstract class ClientTransactionItem implements BaseClientRequest, Parcel
        return true;
    }

    // TODO(b/260873529): cleanup
    /**
     * If this {@link ClientTransactionItem} is updating configuration, returns the {@link Context}
     * it is updating; otherwise, returns {@code null}.
+95 −7
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.app.servertransaction;

import static android.app.WindowConfiguration.areConfigurationsEqualForDisplay;

import static com.android.window.flags.Flags.activityWindowInfoFlag;
import static com.android.window.flags.Flags.bundleClientTransactionFlag;

@@ -24,8 +26,11 @@ import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
import android.app.Activity;
import android.app.ActivityThread;
import android.content.Context;
import android.content.res.Configuration;
import android.hardware.display.DisplayManagerGlobal;
import android.os.IBinder;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.window.ActivityWindowInfo;

@@ -51,6 +56,15 @@ public class ClientTransactionListenerController {
    private final ArraySet<BiConsumer<IBinder, ActivityWindowInfo>>
            mActivityWindowInfoChangedListeners = new ArraySet<>();

    /**
     * Keeps track of the Context whose Configuration will get updated, mapping to the config before
     * the change.
     */
    private final ArrayMap<Context, Configuration> mContextToPreChangedConfigMap = new ArrayMap<>();

    /** Whether there is an {@link ClientTransaction} being executed. */
    private boolean mIsClientTransactionExecuting;

    /** Gets the singleton controller. */
    @NonNull
    public static ClientTransactionListenerController getInstance() {
@@ -126,18 +140,92 @@ public class ClientTransactionListenerController {
        }
    }

    /**
     * Called when receives a {@link ClientTransaction} that is updating display-related
     * window configuration.
     */
    public void onDisplayChanged(int displayId) {
        if (!bundleClientTransactionFlag()) {
    /** Called when starts executing a remote {@link ClientTransaction}. */
    public void onClientTransactionStarted() {
        mIsClientTransactionExecuting = true;
    }

    /** Called when finishes executing a remote {@link ClientTransaction}. */
    public void onClientTransactionFinished() {
        notifyDisplayManagerIfNeeded();
        mIsClientTransactionExecuting = false;
    }

    /** Called before updating the Configuration of the given {@code context}. */
    public void onContextConfigurationPreChanged(@NonNull Context context) {
        if (!bundleClientTransactionFlag() || ActivityThread.isSystem()) {
            // Not enable for system server.
            return;
        }
        if (mContextToPreChangedConfigMap.containsKey(context)) {
            // There is an earlier change that hasn't been reported yet.
            return;
        }
        if (ActivityThread.isSystem()) {
        mContextToPreChangedConfigMap.put(context,
                new Configuration(context.getResources().getConfiguration()));
    }

    /** Called after updating the Configuration of the given {@code context}. */
    public void onContextConfigurationPostChanged(@NonNull Context context) {
        if (!bundleClientTransactionFlag() || ActivityThread.isSystem()) {
            // Not enable for system server.
            return;
        }
        if (mIsClientTransactionExecuting) {
            // Wait until #onClientTransactionFinished to prevent it from triggering the same
            // #onDisplayChanged multiple times within the same ClientTransaction.
            return;
        }
        final Configuration preChangedConfig = mContextToPreChangedConfigMap.remove(context);
        if (preChangedConfig != null && shouldReportDisplayChange(context, preChangedConfig)) {
            onDisplayChanged(context.getDisplayId());
        }
    }

    /**
     * When {@link Configuration} is changed, we want to trigger display change callback as well,
     * because Display reads some fields from {@link Configuration}.
     */
    private void notifyDisplayManagerIfNeeded() {
        if (mContextToPreChangedConfigMap.isEmpty()) {
            return;
        }
        // Whether the configuration change should trigger DisplayListener#onDisplayChanged.
        try {
            // Calculate display ids that have config changed.
            final ArraySet<Integer> configUpdatedDisplayIds = new ArraySet<>();
            final int contextCount = mContextToPreChangedConfigMap.size();
            for (int i = 0; i < contextCount; i++) {
                final Context context = mContextToPreChangedConfigMap.keyAt(i);
                final Configuration preChangedConfig = mContextToPreChangedConfigMap.valueAt(i);
                if (shouldReportDisplayChange(context, preChangedConfig)) {
                    configUpdatedDisplayIds.add(context.getDisplayId());
                }
            }

            // Dispatch the display changed callbacks.
            final int displayCount = configUpdatedDisplayIds.size();
            for (int i = 0; i < displayCount; i++) {
                final int displayId = configUpdatedDisplayIds.valueAt(i);
                onDisplayChanged(displayId);
            }
        } finally {
            mContextToPreChangedConfigMap.clear();
        }
    }

    private boolean shouldReportDisplayChange(@NonNull Context context,
            @NonNull Configuration preChangedConfig) {
        final Configuration postChangedConfig = context.getResources().getConfiguration();
        return !areConfigurationsEqualForDisplay(postChangedConfig, preChangedConfig);
    }

    /**
     * Called when receives a {@link Configuration} changed event that is updating display-related
     * window configuration.
     */
    @VisibleForTesting
    public void onDisplayChanged(int displayId) {
        mDisplayManager.handleDisplayChangeFromWindowManager(displayId);
    }
}
+0 −57
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package android.app.servertransaction;

import static android.app.WindowConfiguration.areConfigurationsEqualForDisplay;
import static android.app.servertransaction.ActivityLifecycleItem.ON_CREATE;
import static android.app.servertransaction.ActivityLifecycleItem.ON_DESTROY;
import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
@@ -32,17 +31,12 @@ import static android.app.servertransaction.TransactionExecutorHelper.shouldExcl
import static android.app.servertransaction.TransactionExecutorHelper.tId;
import static android.app.servertransaction.TransactionExecutorHelper.transactionToString;

import static com.android.window.flags.Flags.bundleClientTransactionFlag;

import android.annotation.NonNull;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.content.Context;
import android.content.res.Configuration;
import android.os.IBinder;
import android.os.Trace;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IntArray;
import android.util.Slog;

@@ -63,12 +57,6 @@ public class TransactionExecutor {
    private final PendingTransactionActions mPendingActions = new PendingTransactionActions();
    private final TransactionExecutorHelper mHelper = new TransactionExecutorHelper();

    /**
     * Keeps track of the Context whose Configuration got updated within a transaction, mapping to
     * the config before the transaction.
     */
    private final ArrayMap<Context, Configuration> mContextToPreChangedConfigMap = new ArrayMap<>();

    /** Initialize an instance with transaction handler, that will execute all requested actions. */
    public TransactionExecutor(@NonNull ClientTransactionHandler clientTransactionHandler) {
        mTransactionHandler = clientTransactionHandler;
@@ -104,37 +92,6 @@ public class TransactionExecutor {
            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
        }

        if (!mContextToPreChangedConfigMap.isEmpty()) {
            // Whether this transaction should trigger DisplayListener#onDisplayChanged.
            try {
                // Calculate display ids that have config changed.
                final ArraySet<Integer> configUpdatedDisplayIds = new ArraySet<>();
                final int contextCount = mContextToPreChangedConfigMap.size();
                for (int i = 0; i < contextCount; i++) {
                    final Context context = mContextToPreChangedConfigMap.keyAt(i);
                    final Configuration preTransactionConfig =
                            mContextToPreChangedConfigMap.valueAt(i);
                    final Configuration postTransactionConfig = context.getResources()
                            .getConfiguration();
                    if (!areConfigurationsEqualForDisplay(
                            postTransactionConfig, preTransactionConfig)) {
                        configUpdatedDisplayIds.add(context.getDisplayId());
                    }
                }

                // Dispatch the display changed callbacks.
                final ClientTransactionListenerController controller =
                        ClientTransactionListenerController.getInstance();
                final int displayCount = configUpdatedDisplayIds.size();
                for (int i = 0; i < displayCount; i++) {
                    final int displayId = configUpdatedDisplayIds.valueAt(i);
                    controller.onDisplayChanged(displayId);
                }
            } finally {
                mContextToPreChangedConfigMap.clear();
            }
        }

        mPendingActions.clear();
        if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "End resolving transaction");
    }
@@ -214,20 +171,6 @@ public class TransactionExecutor {
            }
        }

        final boolean shouldTrackConfigUpdatedContext =
                // No configuration change for local transaction.
                !mTransactionHandler.isExecutingLocalTransaction()
                        && bundleClientTransactionFlag();
        final Context configUpdatedContext = shouldTrackConfigUpdatedContext
                ? item.getContextToUpdate(mTransactionHandler)
                : null;
        if (configUpdatedContext != null
                && !mContextToPreChangedConfigMap.containsKey(configUpdatedContext)) {
            // Keep track of the first pre-executed config of each changed Context.
            mContextToPreChangedConfigMap.put(configUpdatedContext,
                    new Configuration(configUpdatedContext.getResources().getConfiguration()));
        }

        item.execute(mTransactionHandler, mPendingActions);

        item.postExecute(mTransactionHandler, mPendingActions);
Loading