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

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

Fix ConcurrentModificationException in WindowContext

When shouldReportConfigChange is false, it is in process of initializing
WindowContext, which should not report config change nor trigger
onDisplayChanged.

Fix: 334285008
Test: atest FrameworksCoreTests:ClientTransactionListenerControllerTest
Change-Id: Ib66d688a4caf747db4e2f6c7097290e995918f0b
parent c3fd2835
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@ import static com.android.window.flags.Flags.bundleClientTransactionFlag;

import static java.util.Objects.requireNonNull;

import android.annotation.AnyThread;
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.app.Activity;
import android.app.ActivityThread;
@@ -94,6 +96,7 @@ public class ClientTransactionListenerController {
     * The listener will be invoked with two parameters: {@link Activity#getActivityToken()} and
     * {@link ActivityWindowInfo}.
     */
    @AnyThread
    public void registerActivityWindowInfoChangedListener(
            @NonNull BiConsumer<IBinder, ActivityWindowInfo> listener) {
        if (!activityWindowInfoFlag()) {
@@ -108,6 +111,7 @@ public class ClientTransactionListenerController {
     * Unregisters the listener that was previously registered via
     * {@link #registerActivityWindowInfoChangedListener(BiConsumer)}
     */
    @AnyThread
    public void unregisterActivityWindowInfoChangedListener(
            @NonNull BiConsumer<IBinder, ActivityWindowInfo> listener) {
        if (!activityWindowInfoFlag()) {
@@ -122,6 +126,7 @@ public class ClientTransactionListenerController {
     * Called when receives a {@link ClientTransaction} that is updating an activity's
     * {@link ActivityWindowInfo}.
     */
    @MainThread
    public void onActivityWindowInfoChanged(@NonNull IBinder activityToken,
            @NonNull ActivityWindowInfo activityWindowInfo) {
        if (!activityWindowInfoFlag()) {
@@ -141,17 +146,20 @@ public class ClientTransactionListenerController {
    }

    /** Called when starts executing a remote {@link ClientTransaction}. */
    @MainThread
    public void onClientTransactionStarted() {
        mIsClientTransactionExecuting = true;
    }

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

    /** Called before updating the Configuration of the given {@code context}. */
    @MainThread
    public void onContextConfigurationPreChanged(@NonNull Context context) {
        if (!bundleClientTransactionFlag() || ActivityThread.isSystem()) {
            // Not enable for system server.
@@ -166,6 +174,7 @@ public class ClientTransactionListenerController {
    }

    /** Called after updating the Configuration of the given {@code context}. */
    @MainThread
    public void onContextConfigurationPostChanged(@NonNull Context context) {
        if (!bundleClientTransactionFlag() || ActivityThread.isSystem()) {
            // Not enable for system server.
+23 −7
Original line number Diff line number Diff line
@@ -144,17 +144,26 @@ public class WindowTokenClient extends Binder {
        if (context == null) {
            return;
        }
        if (shouldReportConfigChange) {
            // Only report to ClientTransactionListenerController when shouldReportConfigChange,
            // which is on the MainThread.
            final ClientTransactionListenerController controller =
                ClientTransactionListenerController.getInstance();
                    getClientTransactionListenerController();
            controller.onContextConfigurationPreChanged(context);
            try {
            onConfigurationChangedInner(context, newConfig, newDisplayId, shouldReportConfigChange);
                onConfigurationChangedInner(context, newConfig, newDisplayId,
                        shouldReportConfigChange);
            } finally {
                controller.onContextConfigurationPostChanged(context);
            }
        } else {
            onConfigurationChangedInner(context, newConfig, newDisplayId, shouldReportConfigChange);
        }
    }

    private void onConfigurationChangedInner(@NonNull Context context,
    /** Handles onConfiguration changed. */
    @VisibleForTesting
    public void onConfigurationChangedInner(@NonNull Context context,
            @NonNull Configuration newConfig, int newDisplayId, boolean shouldReportConfigChange) {
        CompatibilityInfo.applyOverrideScaleIfNeeded(newConfig);
        final boolean displayChanged;
@@ -233,4 +242,11 @@ public class WindowTokenClient extends Binder {
            mContextRef.clear();
        }
    }

    /** Gets {@link ClientTransactionListenerController}. */
    @VisibleForTesting
    @NonNull
    public ClientTransactionListenerController getClientTransactionListenerController() {
        return ClientTransactionListenerController.getInstance();
    }
}
+38 −0
Original line number Diff line number Diff line
@@ -23,15 +23,19 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;

import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
@@ -45,6 +49,7 @@ import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.view.DisplayInfo;
import android.window.ActivityWindowInfo;
import android.window.WindowTokenClient;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -55,6 +60,7 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

@@ -180,4 +186,36 @@ public class ClientTransactionListenerControllerTest {

        verify(mActivityWindowInfoListener, never()).accept(any(), any());
    }

    @Test
    public void testWindowTokenClient_onConfigurationChanged() {
        doNothing().when(mController).onContextConfigurationPreChanged(any());
        doNothing().when(mController).onContextConfigurationPostChanged(any());

        final WindowTokenClient windowTokenClient = spy(new WindowTokenClient());
        final Context context = mock(Context.class);
        windowTokenClient.attachContext(context);

        doReturn(mController).when(windowTokenClient).getClientTransactionListenerController();
        doNothing().when(windowTokenClient).onConfigurationChangedInner(any(), any(), anyInt(),
                anyBoolean());

        // Not trigger when shouldReportConfigChange is false.
        windowTokenClient.onConfigurationChanged(mConfiguration, 123 /* newDisplayId */,
                false /* shouldReportConfigChange*/);

        verify(mController, never()).onContextConfigurationPreChanged(any());
        verify(mController, never()).onContextConfigurationPostChanged(any());

        // Trigger in order when shouldReportConfigChange is true.
        clearInvocations(windowTokenClient);
        final InOrder inOrder = inOrder(mController, windowTokenClient);
        windowTokenClient.onConfigurationChanged(mConfiguration, 123 /* newDisplayId */,
                true /* shouldReportConfigChange*/);

        inOrder.verify(mController).onContextConfigurationPreChanged(context);
        inOrder.verify(windowTokenClient).onConfigurationChangedInner(context, mConfiguration,
                123 /* newDisplayId */, true /* shouldReportConfigChange*/);
        inOrder.verify(mController).onContextConfigurationPostChanged(context);
    }
}