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

Commit 02170500 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Use singleton WindowManagerService for testing"

parents 3abc006f 5a108b83
Loading
Loading
Loading
Loading
+11 −0
Original line number Original line Diff line number Diff line
@@ -119,6 +119,10 @@ class ActivityTestsBase {
    @After
    @After
    public void tearDownBase() {
    public void tearDownBase() {
        mTestInjector.tearDown();
        mTestInjector.tearDown();
        if (mService != null) {
            mService.setWindowManager(null);
            mService = null;
        }
    }
    }


    ActivityTaskManagerService createActivityTaskManagerService() {
    ActivityTaskManagerService createActivityTaskManagerService() {
@@ -638,7 +642,13 @@ class ActivityTestsBase {
        }
        }
    }
    }


    private static WindowManagerService sMockWindowManagerService;

    private static WindowManagerService prepareMockWindowManager() {
    private static WindowManagerService prepareMockWindowManager() {
        if (sMockWindowManagerService != null) {
            return sMockWindowManagerService;
        }

        final WindowManagerService service = mock(WindowManagerService.class);
        final WindowManagerService service = mock(WindowManagerService.class);
        service.mRoot = mock(RootWindowContainer.class);
        service.mRoot = mock(RootWindowContainer.class);


@@ -650,6 +660,7 @@ class ActivityTestsBase {
            return null;
            return null;
        }).when(service).inSurfaceTransaction(any());
        }).when(service).inSurfaceTransaction(any());


        sMockWindowManagerService = service;
        return service;
        return service;
    }
    }


+11 −6
Original line number Original line Diff line number Diff line
@@ -63,6 +63,7 @@ import com.android.server.statusbar.StatusBarManagerInternal;


import org.junit.After;
import org.junit.After;
import org.junit.Before;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentCaptor;


@@ -86,7 +87,7 @@ public class DisplayRotationTests {


    private StatusBarManagerInternal mPreviousStatusBarManagerInternal;
    private StatusBarManagerInternal mPreviousStatusBarManagerInternal;


    private WindowManagerService mMockWm;
    private static WindowManagerService sMockWm;
    private DisplayContent mMockDisplayContent;
    private DisplayContent mMockDisplayContent;
    private DisplayPolicy mMockDisplayPolicy;
    private DisplayPolicy mMockDisplayPolicy;
    private Context mMockContext;
    private Context mMockContext;
@@ -108,13 +109,16 @@ public class DisplayRotationTests {


    private DisplayRotation mTarget;
    private DisplayRotation mTarget;


    @BeforeClass
    public static void setUpOnce() {
        sMockWm = mock(WindowManagerService.class);
        sMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class);
    }

    @Before
    @Before
    public void setUp() {
    public void setUp() {
        FakeSettingsProvider.clearSettingsProvider();
        FakeSettingsProvider.clearSettingsProvider();


        mMockWm = mock(WindowManagerService.class);
        mMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class);

        mPreviousStatusBarManagerInternal = LocalServices.getService(
        mPreviousStatusBarManagerInternal = LocalServices.getService(
                StatusBarManagerInternal.class);
                StatusBarManagerInternal.class);
        LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
        LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
@@ -452,7 +456,7 @@ public class DisplayRotationTests {
        mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
        mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
        assertTrue(waitForUiHandler());
        assertTrue(waitForUiHandler());


        verify(mMockWm).updateRotation(false, false);
        verify(sMockWm).updateRotation(false, false);
    }
    }


    @Test
    @Test
@@ -833,8 +837,9 @@ public class DisplayRotationTests {
                    .thenReturn(mFakeSettingsProvider.getIContentProvider());
                    .thenReturn(mFakeSettingsProvider.getIContentProvider());


            mMockDisplayWindowSettings = mock(DisplayWindowSettings.class);
            mMockDisplayWindowSettings = mock(DisplayWindowSettings.class);
            mTarget = new DisplayRotation(mMockWm, mMockDisplayContent, mMockDisplayPolicy,
            mTarget = new DisplayRotation(sMockWm, mMockDisplayContent, mMockDisplayPolicy,
                    mMockDisplayWindowSettings, mMockContext, new Object());
                    mMockDisplayWindowSettings, mMockContext, new Object());
            reset(sMockWm);


            captureObservers();
            captureObservers();
        }
        }
+25 −20
Original line number Original line Diff line number Diff line
@@ -34,7 +34,12 @@ import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;


import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;


import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.isNull;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
@@ -47,10 +52,6 @@ import static com.android.server.wm.LockTaskController.STATUS_BAR_MASK_PINNED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;


import android.app.StatusBarManager;
import android.app.StatusBarManager;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManager;
@@ -115,6 +116,7 @@ public class LockTaskControllerTest {


    private LockTaskController mLockTaskController;
    private LockTaskController mLockTaskController;
    private Context mContext;
    private Context mContext;
    private String mPackageName;
    private String mLockToAppSetting;
    private String mLockToAppSetting;


    @Before
    @Before
@@ -122,6 +124,7 @@ public class LockTaskControllerTest {
        MockitoAnnotations.initMocks(this);
        MockitoAnnotations.initMocks(this);


        mContext = getInstrumentation().getTargetContext();
        mContext = getInstrumentation().getTargetContext();
        mPackageName = mContext.getPackageName();
        mLockToAppSetting = Settings.Secure.getString(mContext.getContentResolver(),
        mLockToAppSetting = Settings.Secure.getString(mContext.getContentResolver(),
                Settings.Secure.LOCK_TO_APP_EXIT_LOCKED);
                Settings.Secure.LOCK_TO_APP_EXIT_LOCKED);


@@ -146,6 +149,7 @@ public class LockTaskControllerTest {


    @After
    @After
    public void tearDown() throws Exception {
    public void tearDown() throws Exception {
        mLockTaskController.setWindowManager(null);
        Settings.Secure.putString(mContext.getContentResolver(),
        Settings.Secure.putString(mContext.getContentResolver(),
                Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, mLockToAppSetting);
                Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, mLockToAppSetting);
    }
    }
@@ -223,7 +227,7 @@ public class LockTaskControllerTest {
        // THEN lock task mode should be started
        // THEN lock task mode should be started
        verifyLockTaskStarted(STATUS_BAR_MASK_PINNED, DISABLE2_NONE);
        verifyLockTaskStarted(STATUS_BAR_MASK_PINNED, DISABLE2_NONE);
        // THEN screen pinning toast should be shown
        // THEN screen pinning toast should be shown
        verify(mStatusBarService).showPinningEnterExitToast(true /* entering */);
        verify(mStatusBarService).showPinningEnterExitToast(eq(true /* entering */));
    }
    }


    @Test
    @Test
@@ -390,9 +394,9 @@ public class LockTaskControllerTest {
        // THEN lock task mode should have been finished
        // THEN lock task mode should have been finished
        verifyLockTaskStopped(times(1));
        verifyLockTaskStopped(times(1));
        // THEN the keyguard should be shown
        // THEN the keyguard should be shown
        verify(mLockPatternUtils).requireCredentialEntry(UserHandle.USER_ALL);
        verify(mLockPatternUtils).requireCredentialEntry(eq(UserHandle.USER_ALL));
        // THEN screen pinning toast should be shown
        // THEN screen pinning toast should be shown
        verify(mStatusBarService).showPinningEnterExitToast(false /* entering */);
        verify(mStatusBarService).showPinningEnterExitToast(eq(false /* entering */));
    }
    }


    @Test
    @Test
@@ -509,9 +513,9 @@ public class LockTaskControllerTest {
                & ~DISABLE_HOME;
                & ~DISABLE_HOME;
        int expectedFlags2 = DISABLE2_MASK;
        int expectedFlags2 = DISABLE2_MASK;
        verify(mStatusBarService).disable(eq(expectedFlags), any(IBinder.class),
        verify(mStatusBarService).disable(eq(expectedFlags), any(IBinder.class),
                eq(mContext.getPackageName()));
                eq(mPackageName));
        verify(mStatusBarService).disable2(eq(expectedFlags2), any(IBinder.class),
        verify(mStatusBarService).disable2(eq(expectedFlags2), any(IBinder.class),
                eq(mContext.getPackageName()));
                eq(mPackageName));


        // reset invocation counter
        // reset invocation counter
        reset(mStatusBarService);
        reset(mStatusBarService);
@@ -526,9 +530,9 @@ public class LockTaskControllerTest {
        expectedFlags2 = DISABLE2_MASK
        expectedFlags2 = DISABLE2_MASK
                & ~DISABLE2_NOTIFICATION_SHADE;
                & ~DISABLE2_NOTIFICATION_SHADE;
        verify(mStatusBarService).disable(eq(expectedFlags), any(IBinder.class),
        verify(mStatusBarService).disable(eq(expectedFlags), any(IBinder.class),
                eq(mContext.getPackageName()));
                eq(mPackageName));
        verify(mStatusBarService).disable2(eq(expectedFlags2), any(IBinder.class),
        verify(mStatusBarService).disable2(eq(expectedFlags2), any(IBinder.class),
                eq(mContext.getPackageName()));
                eq(mPackageName));
    }
    }


    @Test
    @Test
@@ -548,9 +552,9 @@ public class LockTaskControllerTest {


        // THEN status bar shouldn't change
        // THEN status bar shouldn't change
        verify(mStatusBarService, never()).disable(anyInt(), any(IBinder.class),
        verify(mStatusBarService, never()).disable(anyInt(), any(IBinder.class),
                eq(mContext.getPackageName()));
                eq(mPackageName));
        verify(mStatusBarService, never()).disable2(anyInt(), any(IBinder.class),
        verify(mStatusBarService, never()).disable2(anyInt(), any(IBinder.class),
                eq(mContext.getPackageName()));
                eq(mPackageName));
    }
    }


    @Test
    @Test
@@ -657,14 +661,14 @@ public class LockTaskControllerTest {
        verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString(), eq(TEST_USER_ID));
        verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString(), eq(TEST_USER_ID));
        // THEN the status bar should have been disabled
        // THEN the status bar should have been disabled
        verify(mStatusBarService).disable(eq(statusBarMask), any(IBinder.class),
        verify(mStatusBarService).disable(eq(statusBarMask), any(IBinder.class),
                eq(mContext.getPackageName()));
                eq(mPackageName));
        verify(mStatusBarService).disable2(eq(statusBarMask2), any(IBinder.class),
        verify(mStatusBarService).disable2(eq(statusBarMask2), any(IBinder.class),
                eq(mContext.getPackageName()));
                eq(mPackageName));
        // THEN recents should have been notified
        // THEN recents should have been notified
        verify(mRecentTasks).onLockTaskModeStateChanged(anyInt(), eq(TEST_USER_ID));
        verify(mRecentTasks).onLockTaskModeStateChanged(anyInt(), eq(TEST_USER_ID));
        // THEN the DO/PO should be informed about the operation
        // THEN the DO/PO should be informed about the operation
        verify(mDevicePolicyManager).notifyLockTaskModeChanged(true, TEST_PACKAGE_NAME,
        verify(mDevicePolicyManager).notifyLockTaskModeChanged(eq(true), eq(TEST_PACKAGE_NAME),
                TEST_USER_ID);
                eq(TEST_USER_ID));
    }
    }


    private void verifyLockTaskStopped(VerificationMode mode) throws Exception {
    private void verifyLockTaskStopped(VerificationMode mode) throws Exception {
@@ -672,11 +676,12 @@ public class LockTaskControllerTest {
        verify(mWindowManager, mode).reenableKeyguard(any(IBinder.class), eq(TEST_USER_ID));
        verify(mWindowManager, mode).reenableKeyguard(any(IBinder.class), eq(TEST_USER_ID));
        // THEN the status bar should have been disabled
        // THEN the status bar should have been disabled
        verify(mStatusBarService, mode).disable(eq(StatusBarManager.DISABLE_NONE),
        verify(mStatusBarService, mode).disable(eq(StatusBarManager.DISABLE_NONE),
                any(IBinder.class), eq(mContext.getPackageName()));
                any(IBinder.class), eq(mPackageName));
        verify(mStatusBarService, mode).disable2(eq(StatusBarManager.DISABLE2_NONE),
        verify(mStatusBarService, mode).disable2(eq(StatusBarManager.DISABLE2_NONE),
                any(IBinder.class), eq(mContext.getPackageName()));
                any(IBinder.class), eq(mPackageName));
        // THEN the DO/PO should be informed about the operation
        // THEN the DO/PO should be informed about the operation
        verify(mDevicePolicyManager, mode).notifyLockTaskModeChanged(false, null, TEST_USER_ID);
        verify(mDevicePolicyManager, mode).notifyLockTaskModeChanged(eq(false), isNull(),
                eq(TEST_USER_ID));
    }
    }


    /**
    /**
+4 −3
Original line number Original line Diff line number Diff line
@@ -33,6 +33,8 @@ import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;


import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;

import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertThat;


import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertEquals;
@@ -58,7 +60,6 @@ import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.TextView;


import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.filters.SmallTest;


import org.junit.After;
import org.junit.After;
@@ -80,8 +81,8 @@ import java.util.function.BooleanSupplier;
@Presubmit
@Presubmit
public class ScreenDecorWindowTests {
public class ScreenDecorWindowTests {


    private final Context mContext = InstrumentationRegistry.getTargetContext();
    private final Context mContext = getInstrumentation().getTargetContext();
    private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
    private final Instrumentation mInstrumentation = getInstrumentation();


    private WindowManager mWm;
    private WindowManager mWm;
    private ArrayList<View> mWindows = new ArrayList<>();
    private ArrayList<View> mWindows = new ArrayList<>();
+0 −242
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2018 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 com.android.server.wm;

import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
import static android.view.Display.DEFAULT_DISPLAY;

import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;

import android.app.ActivityManagerInternal;
import android.content.Context;
import android.hardware.display.DisplayManagerInternal;
import android.os.Handler;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.view.Display;
import android.view.InputChannel;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;

import com.android.server.LocalServices;
import com.android.server.input.InputManagerService;
import com.android.server.policy.WindowManagerPolicy;

import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.mockito.invocation.InvocationOnMock;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * A test rule that sets up a fresh WindowManagerService instance before each test and makes sure
 * to properly tear it down after.
 *
 * <p>
 * Usage:
 * <pre>
 * {@literal @}Rule
 *  public final WindowManagerServiceRule mWmRule = new WindowManagerServiceRule();
 * </pre>
 */
public class WindowManagerServiceRule implements TestRule {

    private WindowManagerService mService;
    private TestWindowManagerPolicy mPolicy;
    // Record all {@link SurfaceControl.Transaction} created while testing and releases native
    // resources when test finishes.
    private final List<WeakReference<Transaction>> mSurfaceTransactions = new ArrayList<>();
    // Record all {@link SurfaceControl} created while testing and releases native resources when
    // test finishes.
    private final List<WeakReference<SurfaceControl>> mSurfaceControls = new ArrayList<>();

    @Override
    public Statement apply(Statement base, Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                runWithDexmakerShareClassLoader(this::setUp);
                try {
                    base.evaluate();
                } finally {
                    tearDown();
                }
            }

            private void setUp() {
                final Context context = getInstrumentation().getTargetContext();

                removeServices();

                LocalServices.addService(DisplayManagerInternal.class,
                        mock(DisplayManagerInternal.class));

                LocalServices.addService(PowerManagerInternal.class,
                        mock(PowerManagerInternal.class));
                final PowerManagerInternal pm =
                        LocalServices.getService(PowerManagerInternal.class);
                doNothing().when(pm).registerLowPowerModeObserver(any());
                PowerSaveState state = new PowerSaveState.Builder().build();
                doReturn(state).when(pm).getLowPowerState(anyInt());

                LocalServices.addService(ActivityManagerInternal.class,
                        mock(ActivityManagerInternal.class));
                LocalServices.addService(ActivityTaskManagerInternal.class,
                        mock(ActivityTaskManagerInternal.class));
                final ActivityTaskManagerInternal atm =
                        LocalServices.getService(ActivityTaskManagerInternal.class);
                doAnswer((InvocationOnMock invocationOnMock) -> {
                    final Runnable runnable = invocationOnMock.<Runnable>getArgument(0);
                    if (runnable != null) {
                        runnable.run();
                    }
                    return null;
                }).when(atm).notifyKeyguardFlagsChanged(any(), anyInt());

                InputManagerService ims = mock(InputManagerService.class);
                // InputChannel is final and can't be mocked.
                InputChannel[] input = InputChannel.openInputChannelPair(TAG_WM);
                if (input != null && input.length > 1) {
                    doReturn(input[1]).when(ims).monitorInput(anyString(), anyInt());
                }
                ActivityTaskManagerService atms = mock(ActivityTaskManagerService.class);
                when(atms.getGlobalLock()).thenReturn(new WindowManagerGlobalLock());

                mService = WindowManagerService.main(context, ims, false, false,
                        mPolicy = new TestWindowManagerPolicy(
                                WindowManagerServiceRule.this::getWindowManagerService), atms);
                mService.mTransactionFactory = () -> {
                    final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
                    mSurfaceTransactions.add(new WeakReference<>(transaction));
                    return transaction;
                };
                mService.mSurfaceBuilderFactory = session -> new SurfaceControl.Builder(session) {
                    @Override
                    public SurfaceControl build() {
                        final SurfaceControl control = super.build();
                        mSurfaceControls.add(new WeakReference<>(control));
                        return control;
                    }
                };

                mService.onInitReady();

                final Display display = mService.mDisplayManager.getDisplay(DEFAULT_DISPLAY);
                // Display creation is driven by the ActivityManagerService via
                // ActivityStackSupervisor. We emulate those steps here.
                mService.mRoot.createDisplayContent(display, mock(ActivityDisplay.class));
            }

            private void removeServices() {
                LocalServices.removeServiceForTest(DisplayManagerInternal.class);
                LocalServices.removeServiceForTest(PowerManagerInternal.class);
                LocalServices.removeServiceForTest(ActivityManagerInternal.class);
                LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
                LocalServices.removeServiceForTest(WindowManagerInternal.class);
                LocalServices.removeServiceForTest(WindowManagerPolicy.class);
            }

            private void tearDown() {
                cancelAllPendingAnimations();
                waitUntilWindowManagerHandlersIdle();
                destroyAllSurfaceTransactions();
                destroyAllSurfaceControls();
                removeServices();
                mService = null;
                mPolicy = null;
            }
        };
    }

    WindowManagerService getWindowManagerService() {
        return mService;
    }

    private void cancelAllPendingAnimations() {
        for (final WeakReference<SurfaceControl> reference : mSurfaceControls) {
            final SurfaceControl sc = reference.get();
            if (sc != null) {
                mService.mSurfaceAnimationRunner.onAnimationCancelled(sc);
            }
        }
    }

    void waitUntilWindowManagerHandlersIdle() {
        final WindowManagerService wm = getWindowManagerService();
        if (wm == null) {
            return;
        }
        // Removing delayed FORCE_GC message decreases time for waiting idle.
        wm.mH.removeMessages(WindowManagerService.H.FORCE_GC);
        waitHandlerIdle(wm.mH);
        waitHandlerIdle(wm.mAnimationHandler);
        waitHandlerIdle(SurfaceAnimationThread.getHandler());
    }

    private static void waitHandlerIdle(Handler handler) {
        if (handler.hasMessagesOrCallbacks()) {
            final CountDownLatch latch = new CountDownLatch(1);
            // Wait for delayed messages are processed.
            handler.getLooper().getQueue().addIdleHandler(() -> {
                if (handler.hasMessagesOrCallbacks()) {
                    return true; // keep idle handler.
                }
                latch.countDown();
                return false; // remove idle handler.
            });
            try {
                latch.await();
            } catch (InterruptedException e) {
            }
        }
    }

    private void destroyAllSurfaceTransactions() {
        for (final WeakReference<Transaction> reference : mSurfaceTransactions) {
            final Transaction transaction = reference.get();
            if (transaction != null) {
                reference.clear();
                transaction.close();
            }
        }
    }

    private void destroyAllSurfaceControls() {
        for (final WeakReference<SurfaceControl> reference : mSurfaceControls) {
            final SurfaceControl control = reference.get();
            if (control != null) {
                reference.clear();
                control.destroy();
            }
        }
    }
}
Loading