Merge "Add the `testOnly` flag to test_com.android.server"
diff --git a/core/proto/android/view/imeinsetssourceconsumer.proto b/core/proto/android/view/imeinsetssourceconsumer.proto
index 1b9aff9..b1ed365 100644
--- a/core/proto/android/view/imeinsetssourceconsumer.proto
+++ b/core/proto/android/view/imeinsetssourceconsumer.proto
@@ -16,7 +16,6 @@
 
 syntax = "proto2";
 
-import "frameworks/base/core/proto/android/view/inputmethod/editorinfo.proto";
 import "frameworks/base/core/proto/android/view/insetssourceconsumer.proto";
 
 package android.view;
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
deleted file mode 100644
index 384e02d..0000000
--- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright (C) 2014 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
-  -->
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:background="@android:color/transparent"
-    android:clipChildren="false"
-    android:clipToPadding="false">
-
-    <include
-        layout="@layout/keyguard_host_view"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content" />
-</FrameLayout>
-
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index b28cb2f..60860ba 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -101,7 +101,10 @@
         <FrameLayout android:id="@+id/keyguard_bouncer_container"
                      android:layout_height="0dp"
                      android:layout_width="match_parent"
-                     android:layout_weight="1" />
+                     android:layout_weight="1"
+                     android:background="@android:color/transparent"
+                     android:clipChildren="false"
+                     android:clipToPadding="false" />
     </LinearLayout>
 
     <com.android.systemui.biometrics.AuthRippleView
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardRootViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardRootViewController.java
deleted file mode 100644
index 4e375c2..0000000
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardRootViewController.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2020 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.keyguard;
-
-import android.view.ViewGroup;
-
-import com.android.keyguard.dagger.KeyguardBouncerScope;
-import com.android.systemui.dagger.qualifiers.RootView;
-import com.android.systemui.statusbar.phone.KeyguardBouncer;
-import com.android.systemui.util.ViewController;
-
-import javax.inject.Inject;
-/** Controller for a {@link KeyguardBouncer}'s Root view. */
-@KeyguardBouncerScope
-public class KeyguardRootViewController extends ViewController<ViewGroup> {
-    @Inject
-    public KeyguardRootViewController(@RootView ViewGroup view) {
-        super(view);
-    }
-
-    public ViewGroup getView() {
-        return mView;
-    }
-
-    @Override
-    protected void onViewAttached() {
-
-    }
-
-    @Override
-    protected void onViewDetached() {
-
-    }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java
index 5160b7e..0cbf8bc 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java
@@ -16,10 +16,13 @@
 
 package com.android.keyguard.dagger;
 
+import android.view.ViewGroup;
+
 import com.android.keyguard.KeyguardHostViewController;
-import com.android.keyguard.KeyguardRootViewController;
+import com.android.systemui.dagger.qualifiers.RootView;
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
 
+import dagger.BindsInstance;
 import dagger.Subcomponent;
 
 /**
@@ -31,12 +34,9 @@
     /** Simple factory for {@link KeyguardBouncerComponent}. */
     @Subcomponent.Factory
     interface Factory {
-        KeyguardBouncerComponent create();
+        KeyguardBouncerComponent create(@BindsInstance @RootView ViewGroup bouncerContainer);
     }
 
-    /** Returns a {@link KeyguardRootViewController}. */
-    KeyguardRootViewController getKeyguardRootViewController();
-
     /** Returns a {@link KeyguardHostViewController}. */
     KeyguardHostViewController getKeyguardHostViewController();
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
index 4fad9a9..b3c1158 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java
@@ -20,7 +20,6 @@
 import android.view.ViewGroup;
 
 import com.android.keyguard.KeyguardHostView;
-import com.android.keyguard.KeyguardMessageArea;
 import com.android.keyguard.KeyguardSecurityContainer;
 import com.android.keyguard.KeyguardSecurityViewFlipper;
 import com.android.systemui.R;
@@ -35,26 +34,16 @@
  */
 @Module
 public interface KeyguardBouncerModule {
-    /** */
-    @Provides
-    @KeyguardBouncerScope
-    @RootView
-    static ViewGroup providesRootView(LayoutInflater layoutInflater) {
-        return (ViewGroup) layoutInflater.inflate(R.layout.keyguard_bouncer, null);
-    }
 
     /** */
     @Provides
     @KeyguardBouncerScope
-    static KeyguardMessageArea providesKeyguardMessageArea(@RootView ViewGroup viewGroup) {
-        return viewGroup.findViewById(R.id.keyguard_message_area);
-    }
-
-    /** */
-    @Provides
-    @KeyguardBouncerScope
-    static KeyguardHostView providesKeyguardHostView(@RootView ViewGroup rootView) {
-        return rootView.findViewById(R.id.keyguard_host_view);
+    static KeyguardHostView providesKeyguardHostView(@RootView ViewGroup rootView,
+            LayoutInflater layoutInflater) {
+        KeyguardHostView hostView = (KeyguardHostView) layoutInflater.inflate(
+                R.layout.keyguard_host_view, rootView, false);
+        rootView.addView(hostView);
+        return hostView;
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 98b5dcc..bfa4a24 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -634,20 +634,22 @@
         Optional.ofNullable(BiometricUiEvent.FAILURE_EVENT_BY_SOURCE_TYPE.get(biometricSourceType))
                 .ifPresent(UI_EVENT_LOGGER::log);
 
-        long currUptimeMillis = SystemClock.uptimeMillis();
-        if (currUptimeMillis - mLastFpFailureUptimeMillis < 2000) { // attempt within 2 seconds
-            mNumConsecutiveFpFailures += 1;
-        } else {
-            mNumConsecutiveFpFailures = 1;
-        }
-        mLastFpFailureUptimeMillis = currUptimeMillis;
+        if (biometricSourceType == BiometricSourceType.FINGERPRINT
+                && mUpdateMonitor.isUdfpsSupported()) {
+            long currUptimeMillis = SystemClock.uptimeMillis();
+            if (currUptimeMillis - mLastFpFailureUptimeMillis
+                    < (mStatusBarStateController.isDozing() ? 3500 : 2000)) {
+                mNumConsecutiveFpFailures += 1;
+            } else {
+                mNumConsecutiveFpFailures = 1;
+            }
+            mLastFpFailureUptimeMillis = currUptimeMillis;
 
-        if (biometricSourceType.equals(BiometricSourceType.FINGERPRINT)
-                && mUpdateMonitor.isUdfpsSupported()
-                && mNumConsecutiveFpFailures >= FP_ATTEMPTS_BEFORE_SHOW_BOUNCER) {
-            startWakeAndUnlock(MODE_SHOW_BOUNCER);
-            UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN);
-            mNumConsecutiveFpFailures = 0;
+            if (mNumConsecutiveFpFailures >= FP_ATTEMPTS_BEFORE_SHOW_BOUNCER) {
+                startWakeAndUnlock(MODE_SHOW_BOUNCER);
+                UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN);
+                mNumConsecutiveFpFailures = 0;
+            }
         }
         cleanup();
     }
@@ -736,6 +738,11 @@
         pw.println(" BiometricUnlockController:");
         pw.print("   mMode="); pw.println(mMode);
         pw.print("   mWakeLock="); pw.println(mWakeLock);
+        if (mUpdateMonitor.isUdfpsSupported()) {
+            pw.print("   mNumConsecutiveFpFailures="); pw.println(mNumConsecutiveFpFailures);
+            pw.print("   time since last failure=");
+            pw.println(SystemClock.uptimeMillis() - mLastFpFailureUptimeMillis);
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 9647486..565b2d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -33,7 +33,6 @@
 
 import com.android.internal.policy.SystemBarUtils;
 import com.android.keyguard.KeyguardHostViewController;
-import com.android.keyguard.KeyguardRootViewController;
 import com.android.keyguard.KeyguardSecurityModel;
 import com.android.keyguard.KeyguardSecurityView;
 import com.android.keyguard.KeyguardUpdateMonitor;
@@ -42,7 +41,6 @@
 import com.android.keyguard.dagger.KeyguardBouncerComponent;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.classifier.FalsingCollector;
-import com.android.systemui.dagger.qualifiers.RootView;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -105,12 +103,11 @@
 
     private int mStatusBarHeight;
     private float mExpansion = EXPANSION_HIDDEN;
-    protected ViewGroup mRoot;
-    private KeyguardRootViewController mRootViewController;
     private boolean mShowingSoon;
     private int mBouncerPromptReason;
     private boolean mIsAnimatingAway;
     private boolean mIsScrimmed;
+    private boolean mInitialized;
 
     private KeyguardBouncer(Context context, ViewMediatorCallback callback,
             ViewGroup container,
@@ -184,7 +181,7 @@
             showPrimarySecurityScreen();
         }
 
-        if (mRoot.getVisibility() == View.VISIBLE || mShowingSoon) {
+        if (mContainer.getVisibility() == View.VISIBLE || mShowingSoon) {
             return;
         }
 
@@ -235,10 +232,8 @@
             Log.wtf(TAG, "onFullyShown when view was null");
         } else {
             mKeyguardViewController.onResume();
-            if (mRoot != null) {
-                mRoot.announceForAccessibility(
-                        mKeyguardViewController.getAccessibilityTitleForCurrentMode());
-            }
+            mContainer.announceForAccessibility(
+                    mKeyguardViewController.getAccessibilityTitleForCurrentMode());
         }
     }
 
@@ -253,10 +248,8 @@
     }
 
     private void setVisibility(@View.Visibility int visibility) {
-        if (mRoot != null) {
-            mRoot.setVisibility(visibility);
-            dispatchVisibilityChanged();
-        }
+        mContainer.setVisibility(visibility);
+        dispatchVisibilityChanged();
     }
 
     private final Runnable mShowRunnable = new Runnable() {
@@ -337,14 +330,12 @@
             mKeyguardViewController.cleanUp();
         }
         mIsAnimatingAway = false;
-        if (mRoot != null) {
-            setVisibility(View.INVISIBLE);
-            if (destroyView) {
+        setVisibility(View.INVISIBLE);
+        if (destroyView) {
 
-                // We have a ViewFlipper that unregisters a broadcast when being detached, which may
-                // be slow because of AM lock contention during unlocking. We can delay it a bit.
-                mHandler.postDelayed(mRemoveViewRunnable, 50);
-            }
+            // We have a ViewFlipper that unregisters a broadcast when being detached, which may
+            // be slow because of AM lock contention during unlocking. We can delay it a bit.
+            mHandler.postDelayed(mRemoveViewRunnable, 50);
         }
     }
 
@@ -370,14 +361,13 @@
     }
 
     public void onScreenTurnedOff() {
-        if (mKeyguardViewController != null
-                && mRoot != null && mRoot.getVisibility() == View.VISIBLE) {
+        if (mKeyguardViewController != null && mContainer.getVisibility() == View.VISIBLE) {
             mKeyguardViewController.onPause();
         }
     }
 
     public boolean isShowing() {
-        return (mShowingSoon || (mRoot != null && mRoot.getVisibility() == View.VISIBLE))
+        return (mShowingSoon || mContainer.getVisibility() == View.VISIBLE)
                 && mExpansion == EXPANSION_VISIBLE && !isAnimatingAway();
     }
 
@@ -401,7 +391,7 @@
     }
 
     public void prepare() {
-        boolean wasInitialized = mRoot != null;
+        boolean wasInitialized = mInitialized;
         ensureView();
         if (wasInitialized) {
             showPrimarySecurityScreen();
@@ -461,7 +451,7 @@
         // in this case we need to force the removal, otherwise we'll
         // end up in an unpredictable state.
         boolean forceRemoval = mHandler.hasCallbacks(mRemoveViewRunnable);
-        if (mRoot == null || forceRemoval) {
+        if (!mInitialized || forceRemoval) {
             inflateView();
         }
     }
@@ -469,28 +459,24 @@
     protected void inflateView() {
         removeView();
         mHandler.removeCallbacks(mRemoveViewRunnable);
-        KeyguardBouncerComponent component = mKeyguardBouncerComponentFactory.create();
-        mRootViewController = component.getKeyguardRootViewController();
-        mRootViewController.init();
-        mRoot = mRootViewController.getView();  // TODO(b/166448040): Don't access root view here.
+
+        KeyguardBouncerComponent component = mKeyguardBouncerComponentFactory.create(mContainer);
         mKeyguardViewController = component.getKeyguardHostViewController();
         mKeyguardViewController.init();
 
-        mContainer.addView(mRoot, mContainer.getChildCount());
         mStatusBarHeight = SystemBarUtils.getStatusBarHeight(mContext);
         setVisibility(View.INVISIBLE);
 
-        final WindowInsets rootInsets = mRoot.getRootWindowInsets();
+        final WindowInsets rootInsets = mContainer.getRootWindowInsets();
         if (rootInsets != null) {
-            mRoot.dispatchApplyWindowInsets(rootInsets);
+            mContainer.dispatchApplyWindowInsets(rootInsets);
         }
+        mInitialized = true;
     }
 
     protected void removeView() {
-        if (mRoot != null && mRoot.getParent() == mContainer) {
-            mContainer.removeView(mRoot);
-            mRoot = null;
-        }
+        mContainer.removeAllViews();
+        mInitialized = false;
     }
 
     /**
@@ -577,7 +563,7 @@
 
     private void dispatchVisibilityChanged() {
         for (BouncerExpansionCallback callback : mExpansionCallbacks) {
-            callback.onVisibilityChanged(mRoot.getVisibility() == View.VISIBLE);
+            callback.onVisibilityChanged(mContainer.getVisibility() == View.VISIBLE);
         }
     }
 
@@ -601,6 +587,7 @@
         pw.println("  mShowingSoon: " + mShowingSoon);
         pw.println("  mBouncerPromptReason: " + mBouncerPromptReason);
         pw.println("  mIsAnimatingAway: " + mIsAnimatingAway);
+        pw.println("  mInitialized: " + mInitialized);
     }
 
     /** Update keyguard position based on a tapped X coordinate. */
@@ -675,7 +662,10 @@
             mKeyguardBouncerComponentFactory = keyguardBouncerComponentFactory;
         }
 
-        public KeyguardBouncer create(@RootView ViewGroup container,
+        /**
+         * Construct a KeyguardBouncer that will exist in the given container.
+         */
+        public KeyguardBouncer create(ViewGroup container,
                 BouncerExpansionCallback expansionCallback) {
             return new KeyguardBouncer(mContext, mCallback, container,
                     mDismissCallbackRegistry, mFalsingCollector, expansionCallback,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 875b7e5..316e682 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -51,7 +51,6 @@
 import com.android.systemui.DejankUtils;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dock.DockManager;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.navigationbar.NavigationBarView;
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -87,7 +86,7 @@
 public class StatusBarKeyguardViewManager implements RemoteInputController.Callback,
         StatusBarStateController.StateListener, ConfigurationController.ConfigurationListener,
         PanelExpansionListener, NavigationModeController.ModeChangedListener,
-        KeyguardViewController, WakefulnessLifecycle.Observer {
+        KeyguardViewController {
 
     // When hiding the Keyguard with timing supplied from WindowManager, better be early than late.
     private static final long HIDE_TIMING_CORRECTION_MS = - 16 * 3;
@@ -392,17 +391,13 @@
         } else {
             mStatusBar.showKeyguard();
             if (hideBouncerWhenShowing) {
-                hideBouncer(shouldDestroyViewOnReset() /* destroyView */);
+                hideBouncer(false /* destroyView */);
                 mBouncer.prepare();
             }
         }
         updateStates();
     }
 
-    protected boolean shouldDestroyViewOnReset() {
-        return false;
-    }
-
     /**
      * If applicable, shows the alternate authentication bouncer. Else, shows the input
      * (pin/password/pattern) bouncer.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index 4f3266d..40632a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -44,8 +44,10 @@
 import com.android.systemui.statusbar.SmartReplyController;
 
 import org.junit.After;
+import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.Rule;
+import org.mockito.Mockito;
 
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -120,11 +122,15 @@
             TestableLooper.get(this).processAllMessages();
         }
         disallowTestableLooperAsMainThread();
-        SystemUIFactory.cleanup();
         mContext.cleanUpReceivers(this.getClass().getSimpleName());
         mFakeBroadcastDispatcher.cleanUpReceivers(this.getClass().getSimpleName());
     }
 
+    @AfterClass
+    public static void mockitoTearDown() {
+        Mockito.framework().clearInlineMocks();
+    }
+
     /**
      * Tests are run on the TestableLooper; however, there are parts of SystemUI that assert that
      * the code is run from the main looper. Therefore, we allow the TestableLooper to pass these
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index 9bf8775..8a38847 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -45,6 +45,7 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -86,6 +87,7 @@
             super(mock(HeadsUpManagerLogger.class));
             mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME;
             mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME;
+            mHandler.removeCallbacksAndMessages(null);
             mHandler = mTestHandler;
         }
 
@@ -145,6 +147,11 @@
         mAlertingNotificationManager = createAlertingNotificationManager();
     }
 
+    @After
+    public void tearDown() {
+        mTestHandler.removeCallbacksAndMessages(null);
+    }
+
     @Test
     public void testShowNotification_addsEntry() {
         mAlertingNotificationManager.showNotification(mEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerLegacyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerLegacyTest.java
index 2dfb9fc..429d2ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerLegacyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerLegacyTest.java
@@ -60,6 +60,7 @@
 
 import com.google.android.collect.Lists;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -131,6 +132,11 @@
         verify(mNotifPipeline, never()).addCollectionListener(any());
     }
 
+    @After
+    public void tearDown() {
+        mLogger.mHandler.removeCallbacksAndMessages(null);
+    }
+
     @Test
     public void testOnChildLocationsChangedReportsVisibilityChanged() throws Exception {
         NotificationVisibility[] newlyVisibleKeys = {
@@ -281,6 +287,7 @@
                     mNotificationPanelLoggerFake
             );
             mBarService = barService;
+            mHandler.removeCallbacksAndMessages(null);
             // Make this on the current thread so we can wait for it during tests.
             mHandler = Handler.createAsync(Looper.myLooper());
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index 4d861f9..b69bd8d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -60,6 +60,7 @@
 
 import com.google.android.collect.Lists;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -132,6 +133,11 @@
         verify(mNotifPipeline).addCollectionListener(any());
     }
 
+    @After
+    public void tearDown() {
+        mLogger.mHandler.removeCallbacksAndMessages(null);
+    }
+
     @Test
     public void testOnChildLocationsChangedReportsVisibilityChanged() throws Exception {
         NotificationVisibility[] newlyVisibleKeys = {
@@ -282,6 +288,7 @@
                     mNotificationPanelLoggerFake
             );
             mBarService = barService;
+            mHandler.removeCallbacksAndMessages(null);
             // Make this on the current thread so we can wait for it during tests.
             mHandler = Handler.createAsync(Looper.myLooper());
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index f3eece8..e4721b1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -36,6 +36,7 @@
 import android.content.Intent;
 import android.content.pm.LauncherApps;
 import android.graphics.drawable.Icon;
+import android.os.Handler;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 import android.testing.TestableLooper;
@@ -139,6 +140,8 @@
                 mock(NotificationGroupManagerLegacy.class),
                 mock(ConfigurationControllerImpl.class)
         );
+        mHeadsUpManager.mHandler.removeCallbacksAndMessages(null);
+        mHeadsUpManager.mHandler = new Handler(mTestLooper.getLooper());
         mGroupMembershipManager.setHeadsUpManager(mHeadsUpManager);
         mIconManager = new IconManager(
                 mock(CommonNotifCollection.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 0f419c7..e8b9c7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -41,6 +41,7 @@
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Rule;
@@ -115,9 +116,15 @@
                 mConfigurationController
         );
         super.setUp();
+        mHeadsUpManager.mHandler.removeCallbacksAndMessages(null);
         mHeadsUpManager.mHandler = mTestHandler;
     }
 
+    @After
+    public void tearDown() {
+        mTestHandler.removeCallbacksAndMessages(null);
+    }
+
     @Test
     public void testSnooze() {
         mHeadsUpManager.showNotification(mEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index e5f2aa7..f391eff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -26,6 +26,7 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.verifyZeroInteractions;
@@ -43,7 +44,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.KeyguardHostViewController;
-import com.android.keyguard.KeyguardRootViewController;
 import com.android.keyguard.KeyguardSecurityModel;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.ViewMediatorCallback;
@@ -64,7 +64,6 @@
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
-import org.mockito.stubbing.Answer;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -92,13 +91,10 @@
     @Mock
     private KeyguardSecurityModel mKeyguardSecurityModel;
     @Mock
-    private KeyguardRootViewController mRootViewController;
-    @Mock
-    private ViewGroup mRootView;
-    @Mock
     private KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
     @Mock
     private KeyguardBouncerComponent mKeyguardBouncerComponent;
+    private ViewGroup mContainer;
     @Rule
     public MockitoRule mRule = MockitoJUnit.rule();
     private Integer mRootVisibility = View.INVISIBLE;
@@ -107,32 +103,22 @@
     @Before
     public void setup() {
         allowTestableLooperAsMainThread();
-        mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mKeyguardUpdateMonitor);
-        mDependency.injectMockDependency(KeyguardStateController.class);
-        when(mRootView.getVisibility()).thenAnswer((Answer<Integer>) invocation -> mRootVisibility);
-        doAnswer(invocation -> {
-            mRootVisibility = invocation.getArgument(0);
-            return null;
-        }).when(mRootView).setVisibility(anyInt());
         when(mKeyguardSecurityModel.getSecurityMode(anyInt()))
                 .thenReturn(KeyguardSecurityModel.SecurityMode.None);
         DejankUtils.setImmediate(true);
-        when(mKeyguardBouncerComponentFactory.create()).thenReturn(mKeyguardBouncerComponent);
+
+        mContainer = spy(new FrameLayout(getContext()));
+        when(mKeyguardBouncerComponentFactory.create(mContainer)).thenReturn(
+                mKeyguardBouncerComponent);
         when(mKeyguardBouncerComponent.getKeyguardHostViewController())
                 .thenReturn(mKeyguardHostViewController);
-        when(mKeyguardBouncerComponent.getKeyguardRootViewController())
-                .thenReturn(mRootViewController);
 
-        when(mRootViewController.getView()).thenReturn(mRootView);
-        when(mRootView.getResources()).thenReturn(mContext.getResources());
-
-        final ViewGroup container = new FrameLayout(getContext());
         mBouncer = new KeyguardBouncer.Factory(getContext(), mViewMediatorCallback,
                 mDismissCallbackRegistry, mFalsingCollector,
                 mKeyguardStateController, mKeyguardUpdateMonitor,
                 mKeyguardBypassController, mHandler, mKeyguardSecurityModel,
                 mKeyguardBouncerComponentFactory)
-                .create(container, mExpansionCallback);
+                .create(mContainer, mExpansionCallback);
     }
 
     @Test
@@ -233,7 +219,7 @@
 
         mBouncer.setExpansion(0);
         verify(mKeyguardHostViewController).onResume();
-        verify(mRootView).announceForAccessibility(any());
+        verify(mContainer).announceForAccessibility(any());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index b717d28..9898b4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -51,6 +51,7 @@
 import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
 import com.android.wm.shell.bubbles.Bubbles;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -111,6 +112,11 @@
         mHeadsUpManager.addListener(mGroupAlertTransferHelper);
     }
 
+    @After
+    public void tearDown() {
+        mHeadsUpManager.mHandler.removeCallbacksAndMessages(null);
+    }
+
     private void mockHasHeadsUpContentView(NotificationEntry entry,
             boolean hasHeadsUpContentView) {
         RowContentBindParams params = new RowContentBindParams();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index 1cd9b9e..3b72f00 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -148,6 +148,7 @@
 import com.android.systemui.wallet.controller.QuickAccessWalletController;
 import com.android.wm.shell.animation.FlingAnimationUtils;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -366,6 +367,7 @@
     private List<View.OnAttachStateChangeListener> mOnAttachStateChangeListeners;
     private FalsingManagerFake mFalsingManager = new FalsingManagerFake();
     private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+    private Handler mMainHandler;
 
     @Before
     public void setup() {
@@ -483,9 +485,11 @@
                 .thenReturn(true);
         reset(mView);
 
+        mMainHandler = new Handler(Looper.getMainLooper());
+
         mNotificationPanelViewController = new NotificationPanelViewController(mView,
                 mResources,
-                new Handler(Looper.getMainLooper()),
+                mMainHandler,
                 mLayoutInflater,
                 mFeatureFlags,
                 coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController,
@@ -560,6 +564,12 @@
                 .setHeadsUpAppearanceController(mock(HeadsUpAppearanceController.class));
     }
 
+    @After
+    public void tearDown() {
+        mNotificationPanelViewController.cancelHeightAnimator();
+        mMainHandler.removeCallbacksAndMessages(null);
+    }
+
     @Test
     public void testSetPanelScrimMinFraction() {
         mNotificationPanelViewController.setPanelScrimMinFraction(0.5f);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
index 5e852e3..d15ba26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
@@ -42,6 +42,7 @@
 import com.android.systemui.statusbar.AlertingNotificationManagerTest;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -77,9 +78,15 @@
 
         mHeadsUpManager = new TestableHeadsUpManager(mContext);
         super.setUp();
+        mHeadsUpManager.mHandler.removeCallbacksAndMessages(null);
         mHeadsUpManager.mHandler = mTestHandler;
     }
 
+    @After
+    public void tearDown() {
+        mTestHandler.removeCallbacksAndMessages(null);
+    }
+
     @Test
     public void testShowNotification_autoDismissesWithAccessibilityTimeout() {
         doReturn(TEST_A11Y_AUTO_DISMISS_TIME).when(mAccessibilityMgr)
diff --git a/services/core/java/com/android/server/inputmethod/ImfLock.java b/services/core/java/com/android/server/inputmethod/ImfLock.java
new file mode 100644
index 0000000..612c14f
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/ImfLock.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 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.inputmethod;
+
+/**
+ * The implicit lock of this class serves as the global lock for
+ * the {@link InputMethodManagerService} and its controllers,
+ * which contain the main logic of the input method framework (IMF).
+ *
+ * <p>
+ * This lock can be used as follows in code:
+ * <pre>
+ * synchronized (ImfLock.class) {
+ *   ...
+ * }
+ * </pre>
+ *
+ * <p>
+ * For annotations, you can use a similar syntax:
+ * <pre>
+ * &#64;GuardedBy("ImfLock.class")
+ * myMethodDeclaration() {
+ *   ...
+ * }
+ * </pre>
+ */
+final class ImfLock {
+    private ImfLock() {
+        // no instances
+    }
+}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index 47e0e69..220d790 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -72,16 +72,16 @@
     @NonNull private final WindowManagerInternal mWindowManagerInternal;
     @NonNull private final Resources mRes;
 
-    @GuardedBy("mMethodMap") private long mLastBindTime;
-    @GuardedBy("mMethodMap") private boolean mHasConnection;
-    @GuardedBy("mMethodMap") @Nullable private String mCurId;
-    @GuardedBy("mMethodMap") @Nullable private String mSelectedMethodId;
-    @GuardedBy("mMethodMap") @Nullable private Intent mCurIntent;
-    @GuardedBy("mMethodMap") @Nullable private IInputMethod mCurMethod;
-    @GuardedBy("mMethodMap") private int mCurMethodUid = Process.INVALID_UID;
-    @GuardedBy("mMethodMap") private IBinder mCurToken;
-    @GuardedBy("mMethodMap") private int mCurSeq;
-    @GuardedBy("mMethodMap") private boolean mVisibleBound;
+    @GuardedBy("ImfLock.class") private long mLastBindTime;
+    @GuardedBy("ImfLock.class") private boolean mHasConnection;
+    @GuardedBy("ImfLock.class") @Nullable private String mCurId;
+    @GuardedBy("ImfLock.class") @Nullable private String mSelectedMethodId;
+    @GuardedBy("ImfLock.class") @Nullable private Intent mCurIntent;
+    @GuardedBy("ImfLock.class") @Nullable private IInputMethod mCurMethod;
+    @GuardedBy("ImfLock.class") private int mCurMethodUid = Process.INVALID_UID;
+    @GuardedBy("ImfLock.class") private IBinder mCurToken;
+    @GuardedBy("ImfLock.class") private int mCurSeq;
+    @GuardedBy("ImfLock.class") private boolean mVisibleBound;
     private boolean mSupportsStylusHw;
 
     /**
@@ -146,7 +146,7 @@
      * Time that we last initiated a bind to the input method, to determine
      * if we should try to disconnect and reconnect to it.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     long getLastBindTime() {
         return mLastBindTime;
     }
@@ -155,7 +155,7 @@
      * Set to true if our ServiceConnection is currently actively bound to
      * a service (whether or not we have gotten its IBinder back yet).
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     boolean hasConnection() {
         return mHasConnection;
     }
@@ -168,7 +168,7 @@
      *
      * @see #getSelectedMethodId()
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     @Nullable
     String getCurId() {
         return mCurId;
@@ -187,13 +187,13 @@
      *
      * @see #getCurId()
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     @Nullable
     String getSelectedMethodId() {
         return mSelectedMethodId;
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     void setSelectedMethodId(@Nullable String selectedMethodId) {
         mSelectedMethodId = selectedMethodId;
     }
@@ -202,7 +202,7 @@
      * The token we have made for the currently active input method, to
      * identify it in the future.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     IBinder getCurToken() {
         return mCurToken;
     }
@@ -210,7 +210,7 @@
     /**
      * The Intent used to connect to the current input method.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     @Nullable
     Intent getCurIntent() {
         return mCurIntent;
@@ -220,7 +220,7 @@
      * The current binding sequence number, incremented every time there is
      * a new bind performed.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     int getSequenceNumber() {
         return mCurSeq;
     }
@@ -229,7 +229,7 @@
      * Increase the current binding sequence number by one.
      * Reset to 1 on overflow.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     void advanceSequenceNumber() {
         mCurSeq += 1;
         if (mCurSeq <= 0) {
@@ -241,7 +241,7 @@
      * If non-null, this is the input method service we are currently connected
      * to.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     @Nullable
     IInputMethod getCurMethod() {
         return mCurMethod;
@@ -250,7 +250,7 @@
     /**
      * If not {@link Process#INVALID_UID}, then the UID of {@link #getCurIntent()}.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     int getCurMethodUid() {
         return mCurMethodUid;
     }
@@ -258,7 +258,7 @@
     /**
      * Indicates whether {@link #mVisibleConnection} is currently in use.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     boolean isVisibleBound() {
         return mVisibleBound;
     }
@@ -266,10 +266,10 @@
     /**
      * Used to bring IME service up to visible adjustment while it is being shown.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private final ServiceConnection mVisibleConnection = new ServiceConnection() {
         @Override public void onBindingDied(ComponentName name) {
-            synchronized (mMethodMap) {
+            synchronized (ImfLock.class) {
                 if (mVisibleBound) {
                     unbindVisibleConnection();
                 }
@@ -286,12 +286,12 @@
     /**
      * Used to bind the IME while it is not currently being shown.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private final ServiceConnection mMainConnection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.onServiceConnected");
-            synchronized (mMethodMap) {
+            synchronized (ImfLock.class) {
                 if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {
                     mCurMethod = IInputMethod.Stub.asInterface(service);
                     updateCurrentMethodUid();
@@ -318,7 +318,7 @@
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
 
-        @GuardedBy("mMethodMap")
+        @GuardedBy("ImfLock.class")
         private void updateCurrentMethodUid() {
             final String curMethodPackage = mCurIntent.getComponent().getPackageName();
             final int curMethodUid = mPackageManagerInternal.getPackageUid(
@@ -344,7 +344,7 @@
             // refreshed when this method is called back.  Running
             //    adb install -r <APK that implements the current IME>
             // would be a good way to trigger such a situation.
-            synchronized (mMethodMap) {
+            synchronized (ImfLock.class) {
                 if (DEBUG) {
                     Slog.v(TAG, "Service disconnected: " + name + " mCurIntent=" + mCurIntent);
                 }
@@ -361,7 +361,7 @@
         }
     };
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     void unbindCurrentMethod() {
         if (mVisibleBound) {
             unbindVisibleConnection();
@@ -380,14 +380,14 @@
         clearCurMethodAndSessions();
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private void clearCurMethodAndSessions() {
         mService.clearClientSessionsLocked();
         mCurMethod = null;
         mCurMethodUid = Process.INVALID_UID;
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private void removeCurrentToken() {
         int curTokenDisplayId = mService.getCurTokenDisplayIdLocked();
 
@@ -400,7 +400,7 @@
         mCurToken = null;
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     @NonNull
     InputBindResult bindCurrentMethod() {
         InputMethodInfo info = mMethodMap.get(mSelectedMethodId);
@@ -438,7 +438,7 @@
         return intent;
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private void addFreshWindowToken() {
         int displayIdToShowIme = mService.getDisplayIdToShowImeLocked();
         mCurToken = new Binder();
@@ -458,19 +458,19 @@
         }
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private void unbindMainConnection() {
         mContext.unbindService(mMainConnection);
         mHasConnection = false;
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     void unbindVisibleConnection() {
         mContext.unbindService(mVisibleConnection);
         mVisibleBound = false;
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private boolean bindCurrentInputMethodService(ServiceConnection conn, int flags) {
         if (mCurIntent == null || conn == null) {
             Slog.e(TAG, "--- bind failed: service = " + mCurIntent + ", conn = " + conn);
@@ -480,14 +480,14 @@
                 new UserHandle(mSettings.getCurrentUserId()));
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private boolean bindCurrentInputMethodServiceVisibleConnection() {
         mVisibleBound = bindCurrentInputMethodService(mVisibleConnection,
                 IME_VISIBLE_BIND_FLAGS);
         return mVisibleBound;
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private boolean bindCurrentInputMethodServiceMainConnection() {
         mHasConnection = bindCurrentInputMethodService(mMainConnection,
                 mImeConnectionBindFlags);
@@ -500,7 +500,7 @@
      * <p>
      * Performs a rebind if no binding is achieved in {@link #TIME_TO_RECONNECT} milliseconds.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     void setCurrentMethodVisible() {
         if (mCurMethod != null) {
             if (DEBUG) Slog.d(TAG, "setCurrentMethodVisible: mCurToken=" + mCurToken);
@@ -541,7 +541,7 @@
     /**
      * Remove the binding needed for the IME to be shown.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     void setCurrentMethodNotVisible() {
         if (mVisibleBound) {
             unbindVisibleConnection();
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 41d8332..c87dc89 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -303,9 +303,7 @@
     @Nullable
     private AudioManagerInternal mAudioManagerInternal = null;
 
-
-    // All known input methods.  mMethodMap also serves as the global
-    // lock for this class.
+    // All known input methods.
     final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
     final ArrayMap<String, InputMethodInfo> mMethodMap = new ArrayMap<>();
     final InputMethodSubtypeSwitchingController mSwitchingController;
@@ -313,18 +311,18 @@
     /**
      * Tracks how many times {@link #mMethodMap} was updated.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private int mMethodMapUpdateCount = 0;
 
     /**
      * The display id for which the latest startInput was called.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     int getDisplayIdToShowImeLocked() {
         return mDisplayIdToShowIme;
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private int mDisplayIdToShowIme = INVALID_DISPLAY;
 
     // Ongoing notification
@@ -410,7 +408,7 @@
         }
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>();
 
     /**
@@ -431,13 +429,13 @@
      *
      * @see InputMethodBindingController#getCurId()
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     @Nullable
-    private String getSelectedMethodIdLocked() {
+    String getSelectedMethodIdLocked() {
         return mBindingController.getSelectedMethodId();
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private void setSelectedMethodIdLocked(@Nullable String selectedMethodId) {
         mBindingController.setSelectedMethodId(selectedMethodId);
     }
@@ -446,7 +444,7 @@
      * The current binding sequence number, incremented every time there is
      * a new bind performed.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private int getSequenceNumberLocked() {
         return mBindingController.getSequenceNumber();
     }
@@ -455,7 +453,7 @@
      * Increase the current binding sequence number by one.
      * Reset to 1 on overflow.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private void advanceSequenceNumberLocked() {
         mBindingController.advanceSequenceNumber();
     }
@@ -517,7 +515,7 @@
      *
      * @see #getSelectedMethodIdLocked()
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     @Nullable
     private String getCurIdLocked() {
         return mBindingController.getCurId();
@@ -537,7 +535,7 @@
      * Set to true if our ServiceConnection is currently actively bound to
      * a service (whether or not we have gotten its IBinder back yet).
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private boolean hasConnectionLocked() {
         return mBindingController.hasConnection();
     }
@@ -570,7 +568,7 @@
     /**
      * The Intent used to connect to the current input method.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     @Nullable
     private Intent getCurIntentLocked() {
         return mBindingController.getCurIntent();
@@ -580,7 +578,7 @@
      * The token we have made for the currently active input method, to
      * identify it in the future.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private IBinder getCurTokenLocked() {
         return mBindingController.getCurToken();
     }
@@ -588,23 +586,23 @@
     /**
      * The displayId of current active input method.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     int getCurTokenDisplayIdLocked() {
         return mCurTokenDisplayId;
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     void setCurTokenDisplayIdLocked(int curTokenDisplayId) {
         mCurTokenDisplayId = curTokenDisplayId;
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private int mCurTokenDisplayId = INVALID_DISPLAY;
 
     /**
      * The host input token of the current active input method.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     @Nullable
     private IBinder mCurHostInputToken;
 
@@ -620,7 +618,7 @@
      * If non-null, this is the input method service we are currently connected
      * to.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     @Nullable
     private IInputMethod getCurMethodLocked() {
         return mBindingController.getCurMethod();
@@ -629,7 +627,7 @@
     /**
      * If not {@link Process#INVALID_UID}, then the UID of {@link #getCurIntentLocked()}.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private int getCurMethodUidLocked() {
         return mBindingController.getCurMethodUid();
     }
@@ -638,7 +636,7 @@
      * Time that we last initiated a bind to the input method, to determine
      * if we should try to disconnect and reconnect to it.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private long getLastBindTimeLocked() {
         return mBindingController.getLastBindTime();
     }
@@ -765,7 +763,7 @@
         }
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private final WeakHashMap<IBinder, IBinder> mImeTargetWindowMap = new WeakHashMap<>();
 
     private static final class SoftInputShowHideHistory {
@@ -873,7 +871,7 @@
      * {@link InputMethodManager#showSoftInput(View, int)}.
      * This map tracks origin of showSoftInput requests.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private final WeakHashMap<IBinder, IBinder> mShowRequestWindowMap = new WeakHashMap<>();
 
     /**
@@ -881,7 +879,7 @@
      * {@link InputMethodManager#hideSoftInputFromWindow(IBinder, int)}.
      * This map tracks origin of hideSoftInput requests.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private final WeakHashMap<IBinder, IBinder> mHideRequestWindowMap = new WeakHashMap<>();
 
     /**
@@ -1038,11 +1036,11 @@
         }
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     @NonNull
     private final StartInputHistory mStartInputHistory = new StartInputHistory();
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     @NonNull
     private final SoftInputShowHideHistory mSoftInputShowHideHistory =
             new SoftInputShowHideHistory();
@@ -1060,7 +1058,7 @@
             super(handler);
         }
 
-        @GuardedBy("mMethodMap")
+        @GuardedBy("ImfLock.class")
         public void registerContentObserverLocked(@UserIdInt int userId) {
             if (mRegistered && mUserId == userId) {
                 return;
@@ -1092,7 +1090,7 @@
                     Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
             final Uri accessibilityRequestingNoImeUri = Settings.Secure.getUriFor(
                     Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
-            synchronized (mMethodMap) {
+            synchronized (ImfLock.class) {
                 if (showImeUri.equals(uri)) {
                     mMenuController.updateKeyboardFromSettingsLocked();
                 } else if (accessibilityRequestingNoImeUri.equals(uri)) {
@@ -1198,7 +1196,7 @@
      * <p>Caution: This method must not be called when system is not ready.</p>
      */
     void onActionLocaleChanged() {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             final LocaleList possibleNewLocale = mRes.getConfiguration().getLocales();
             if (possibleNewLocale != null && possibleNewLocale.equals(mLastSystemLocales)) {
                 return;
@@ -1220,7 +1218,7 @@
          * dynamically unless the entire package is updated, which also always triggers package
          * rescanning.</p>
          */
-        @GuardedBy("mMethodMap")
+        @GuardedBy("ImfLock.class")
         final private ArraySet<String> mKnownImePackageNames = new ArraySet<>();
 
         /**
@@ -1243,17 +1241,17 @@
          */
         private boolean mImePackageAppeared = false;
 
-        @GuardedBy("mMethodMap")
+        @GuardedBy("ImfLock.class")
         void clearKnownImePackageNamesLocked() {
             mKnownImePackageNames.clear();
         }
 
-        @GuardedBy("mMethodMap")
+        @GuardedBy("ImfLock.class")
         final void addKnownImePackageNameLocked(@NonNull String packageName) {
             mKnownImePackageNames.add(packageName);
         }
 
-        @GuardedBy("mMethodMap")
+        @GuardedBy("ImfLock.class")
         private boolean isChangingPackagesOfCurrentUserLocked() {
             final int userId = getChangingUserId();
             final boolean retval = userId == mSettings.getCurrentUserId();
@@ -1267,7 +1265,7 @@
 
         @Override
         public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
-            synchronized (mMethodMap) {
+            synchronized (ImfLock.class) {
                 if (!isChangingPackagesOfCurrentUserLocked()) {
                     return false;
                 }
@@ -1355,7 +1353,7 @@
             mImePackageAppeared = false;
         }
 
-        @GuardedBy("mMethodMap")
+        @GuardedBy("ImfLock.class")
         private boolean shouldRebuildInputMethodListLocked() {
             // This method is guaranteed to be called only by getRegisteredHandler().
 
@@ -1379,7 +1377,7 @@
         }
 
         private void onFinishPackageChangesInternal() {
-            synchronized (mMethodMap) {
+            synchronized (ImfLock.class) {
                 if (!isChangingPackagesOfCurrentUserLocked()) {
                     return;
                 }
@@ -1502,7 +1500,7 @@
 
         @Override
         public void run() {
-            synchronized (mService.mMethodMap) {
+            synchronized (ImfLock.class) {
                 if (mService.mUserSwitchHandlerTask != this) {
                     // This task was already canceled before it is handled here. So do nothing.
                     return;
@@ -1519,7 +1517,7 @@
      * a handler callback.  This needs to be set and unset only within the lock.
      */
     @Nullable
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private UserSwitchHandlerTask mUserSwitchHandlerTask;
 
     public static final class Lifecycle extends SystemService {
@@ -1541,7 +1539,7 @@
         @Override
         public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
             // Called on ActivityManager thread.
-            synchronized (mService.mMethodMap) {
+            synchronized (ImfLock.class) {
                 mService.scheduleSwitchUserTaskLocked(to.getUserIdentifier(),
                         /* clientToBeReset= */ null);
             }
@@ -1567,7 +1565,7 @@
     }
 
     void onUnlockUser(@UserIdInt int userId) {
-        synchronized(mMethodMap) {
+        synchronized (ImfLock.class) {
             final int currentUserId = mSettings.getCurrentUserId();
             if (DEBUG) {
                 Slog.d(TAG, "onUnlockUser: userId=" + userId + " curUserId=" + currentUserId);
@@ -1584,7 +1582,7 @@
         }
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     void scheduleSwitchUserTaskLocked(@UserIdInt int userId,
             @Nullable IInputMethodClient clientToBeReset) {
         if (mUserSwitchHandlerTask != null) {
@@ -1677,7 +1675,7 @@
                 com.android.internal.R.bool.config_preventImeStartupUnlessTextEditor);
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private void resetDefaultImeLocked(Context context) {
         // Do not reset the default (current) IME when it is a 3rd-party IME
         String selectedMethodId = getSelectedMethodIdLocked();
@@ -1697,7 +1695,7 @@
         setSelectedInputMethodAndSubtypeLocked(defIm, NOT_A_SUBTYPE_ID, false);
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private void switchUserOnHandlerLocked(@UserIdInt int newUserId,
             IInputMethodClient clientToBeReset) {
         if (DEBUG) Slog.d(TAG, "Switching user stage 1/3. newUserId=" + newUserId
@@ -1783,7 +1781,7 @@
     }
 
     public void systemRunning(StatusBarManagerService statusBar) {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (DEBUG) {
                 Slog.d(TAG, "--- systemReady");
             }
@@ -1839,7 +1837,7 @@
     // Check whether or not this is a valid IPC. Assumes an IPC is valid when either
     // 1) it comes from the system process
     // 2) the calling process' user id is identical to the current user id IMMS thinks.
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private boolean calledFromValidUserLocked() {
         final int uid = Binder.getCallingUid();
         final int userId = UserHandle.getUserId(uid);
@@ -1883,7 +1881,7 @@
      * @param token The window token given to the input method when it was started.
      * @return true if and only if non-null valid token is specified.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private boolean calledWithValidTokenLocked(@NonNull IBinder token) {
         if (token == null) {
             throw new InvalidParameterException("token must not be null.");
@@ -1902,7 +1900,7 @@
             mContext.enforceCallingPermission(
                     Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         }
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             final int[] resolvedUserIds = InputMethodUtils.resolveUserId(userId,
                     mSettings.getCurrentUserId(), null);
             if (resolvedUserIds.length != 1) {
@@ -1934,7 +1932,7 @@
             mContext.enforceCallingPermission(
                     Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         }
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             final int[] resolvedUserIds = InputMethodUtils.resolveUserId(userId,
                     mSettings.getCurrentUserId(), null);
             if (resolvedUserIds.length != 1) {
@@ -1949,7 +1947,7 @@
         }
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private List<InputMethodInfo> getInputMethodListLocked(@UserIdInt int userId,
             @DirectBootAwareness int directBootAwareness) {
         final ArrayList<InputMethodInfo> methodList;
@@ -1969,7 +1967,7 @@
         return methodList;
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private List<InputMethodInfo> getEnabledInputMethodListLocked(@UserIdInt int userId) {
         if (userId == mSettings.getCurrentUserId()) {
             return mSettings.getEnabledInputMethodListLocked();
@@ -1980,7 +1978,7 @@
         return settings.getEnabledInputMethodListLocked();
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private void onCreateInlineSuggestionsRequestLocked(@UserIdInt int userId,
             InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback callback) {
         final InputMethodInfo imi = mMethodMap.get(getSelectedMethodIdLocked());
@@ -2088,7 +2086,7 @@
      * @param hostInputToken the host input token of the current active input method
      */
     void setCurHostInputToken(@NonNull IBinder callerImeToken, @Nullable IBinder hostInputToken) {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (!calledWithValidTokenLocked(callerImeToken)) {
                 return;
             }
@@ -2107,7 +2105,7 @@
     public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
             boolean allowsImplicitlySelectedSubtypes) {
         final int callingUserId = UserHandle.getCallingUserId();
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             final int[] resolvedUserIds = InputMethodUtils.resolveUserId(callingUserId,
                     mSettings.getCurrentUserId(), null);
             if (resolvedUserIds.length != 1) {
@@ -2123,7 +2121,7 @@
         }
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked(String imiId,
             boolean allowsImplicitlySelectedSubtypes, @UserIdInt int userId) {
         if (userId == mSettings.getCurrentUserId()) {
@@ -2178,7 +2176,7 @@
         // actually running.
         final int callerUid = Binder.getCallingUid();
         final int callerPid = Binder.getCallingPid();
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             // TODO: Optimize this linear search.
             final int numClients = mClients.size();
             for (int i = 0; i < numClients; ++i) {
@@ -2211,7 +2209,7 @@
     }
 
     void removeClient(IInputMethodClient client) {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             ClientState cs = mClients.remove(client.asBinder());
             if (cs != null) {
                 client.asBinder().unlinkToDeath(cs.clientDeathRecipient, 0);
@@ -2245,7 +2243,7 @@
          }
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     void unbindCurrentClientLocked(@UnbindReason int unbindClientReason) {
         if (mCurClient != null) {
             if (DEBUG) Slog.v(TAG, "unbindCurrentInputLocked: client="
@@ -2271,13 +2269,13 @@
         }
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     void clearInputShowRequestLocked() {
         mShowRequested = mInputShown;
         mInputShown = false;
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private int getImeShowFlagsLocked() {
         int flags = 0;
         if (mShowForced) {
@@ -2289,7 +2287,7 @@
         return flags;
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private int getAppShowFlagsLocked() {
         int flags = 0;
         if (mShowForced) {
@@ -2300,7 +2298,7 @@
         return flags;
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     @NonNull
     InputBindResult attachNewInputLocked(@StartInputReason int startInputReason, boolean initial) {
         if (!mBoundToMethod) {
@@ -2350,7 +2348,7 @@
                 curId, getSequenceNumberLocked(), suppressesSpellChecker);
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     @NonNull
     InputBindResult startInputUncheckedLocked(@NonNull ClientState cs, IInputContext inputContext,
             @NonNull EditorInfo attribute, @StartInputFlags int startInputFlags,
@@ -2437,14 +2435,14 @@
         return mBindingController.bindCurrentMethod();
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private boolean isSelectedMethodBoundLocked() {
         String curId = getCurIdLocked();
         return curId != null && curId.equals(getSelectedMethodIdLocked())
                 && mDisplayIdToShowIme == mCurTokenDisplayId;
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private void prepareClientSwitchLocked(ClientState cs) {
         // If the client is changing, we need to switch over to the new
         // one.
@@ -2456,7 +2454,7 @@
         }
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     @Nullable
     private InputBindResult tryReuseConnectionLocked(@NonNull ClientState cs) {
         if (hasConnectionLocked()) {
@@ -2529,7 +2527,7 @@
 
     void onSessionCreated(IInputMethod method, IInputMethodSession session,
             InputChannel channel) {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (mUserSwitchHandlerTask != null) {
                 // We have a pending user-switching task so it's better to just ignore this session.
                 channel.dispose();
@@ -2557,7 +2555,7 @@
         channel.dispose();
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     void resetSystemUiLocked() {
         // Set IME window status as invisible when unbinding current method.
         mImeWindowVis = 0;
@@ -2567,14 +2565,14 @@
         mCurHostInputToken = null;
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     void resetCurrentMethodAndClientLocked(@UnbindReason int unbindClientReason) {
         setSelectedMethodIdLocked(null);
         mBindingController.unbindCurrentMethod();
         unbindCurrentClientLocked(unbindClientReason);
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     void reRequestCurrentClientSessionLocked() {
         if (mCurClient != null) {
             clearClientSessionLocked(mCurClient);
@@ -2582,7 +2580,7 @@
         }
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     void requestClientSessionLocked(ClientState cs) {
         if (!cs.sessionRequested) {
             if (DEBUG) Slog.v(TAG, "Creating new session for client " + cs);
@@ -2595,14 +2593,14 @@
         }
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     void clearClientSessionLocked(ClientState cs) {
         finishSessionLocked(cs.curSession);
         cs.curSession = null;
         cs.sessionRequested = false;
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private void finishSessionLocked(SessionState sessionState) {
         if (sessionState != null) {
             if (sessionState.session != null) {
@@ -2621,7 +2619,7 @@
         }
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     void clearClientSessionsLocked() {
         if (getCurMethodLocked() != null) {
             final int numClients = mClients.size();
@@ -2640,7 +2638,7 @@
     @BinderThread
     private void updateStatusIcon(@NonNull IBinder token, String packageName,
             @DrawableRes int iconId) {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (!calledWithValidTokenLocked(token)) {
                 return;
             }
@@ -2674,14 +2672,14 @@
         }
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private void hideStatusBarIconLocked() {
         if (mStatusBar != null) {
             mStatusBar.setIconVisibility(mSlotIme, false);
         }
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private boolean shouldShowImeSwitcherLocked(int visibility) {
         if (!mShowOngoingImeSwitcherForPhones) return false;
         if (mMenuController.getSwitchingDialogLocked() != null) return false;
@@ -2750,7 +2748,7 @@
     private void setImeWindowStatus(@NonNull IBinder token, int vis, int backDisposition) {
         final int topFocusedDisplayId = mWindowManagerInternal.getTopFocusedDisplayId();
 
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (!calledWithValidTokenLocked(token)) {
                 return;
             }
@@ -2785,7 +2783,7 @@
 
     @BinderThread
     private void reportStartInput(@NonNull IBinder token, IBinder startInputToken) {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (!calledWithValidTokenLocked(token)) {
                 return;
             }
@@ -2798,7 +2796,7 @@
     }
 
     private void updateImeWindowStatus(boolean disableImeIcon) {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (disableImeIcon) {
                 updateSystemUiLocked(0, mBackDisposition);
             } else {
@@ -2807,13 +2805,13 @@
         }
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     void updateSystemUiLocked() {
         updateSystemUiLocked(mImeWindowVis, mBackDisposition);
     }
 
     // Caution! This method is called in this class. Handle multi-user carefully
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private void updateSystemUiLocked(int vis, int backDisposition) {
         if (getCurTokenLocked() == null) {
             return;
@@ -2882,13 +2880,13 @@
         }
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     void updateFromSettingsLocked(boolean enabledMayChange) {
         updateInputMethodsFromSettingsLocked(enabledMayChange);
         mMenuController.updateKeyboardFromSettingsLocked();
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     void updateInputMethodsFromSettingsLocked(boolean enabledMayChange) {
         if (enabledMayChange) {
             List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodListLocked();
@@ -2943,7 +2941,7 @@
 
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     void setInputMethodLocked(String id, int subtypeId) {
         InputMethodInfo info = mMethodMap.get(id);
         if (info == null) {
@@ -3015,7 +3013,7 @@
         int uid = Binder.getCallingUid();
         ImeTracing.getInstance().triggerManagerServiceDump(
                 "InputMethodManagerService#showSoftInput");
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (!calledFromValidUserLocked()) {
                 return false;
             }
@@ -3051,7 +3049,7 @@
     public void reportPerceptibleAsync(IBinder windowToken, boolean perceptible) {
         Objects.requireNonNull(windowToken, "windowToken must not be null");
         int uid = Binder.getCallingUid();
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (!calledFromValidUserLocked()) {
                 return;
             }
@@ -3068,7 +3066,7 @@
         }
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     boolean showCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver,
             @SoftInputShowHideReason int reason) {
         mShowRequested = true;
@@ -3109,7 +3107,7 @@
         int uid = Binder.getCallingUid();
         ImeTracing.getInstance().triggerManagerServiceDump(
                 "InputMethodManagerService#hideSoftInput");
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (!InputMethodManagerService.this.calledFromValidUserLocked()) {
                 return false;
             }
@@ -3146,7 +3144,7 @@
         }
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     boolean hideCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver,
             @SoftInputShowHideReason int reason) {
         if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
@@ -3240,7 +3238,7 @@
                 userId = callingUserId;
             }
             final InputBindResult result;
-            synchronized (mMethodMap) {
+            synchronized (ImfLock.class) {
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     result = startInputOrWindowGainedFocusInternalLocked(startInputReason,
@@ -3265,7 +3263,7 @@
         }
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     @NonNull
     private InputBindResult startInputOrWindowGainedFocusInternalLocked(
             @StartInputReason int startInputReason, IInputMethodClient client,
@@ -3538,7 +3536,7 @@
         return mWindowManagerInternal.shouldRestoreImeVisibility(windowToken);
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private boolean canShowInputMethodPickerLocked(IInputMethodClient client) {
         // TODO(yukawa): multi-display support.
         final int uid = Binder.getCallingUid();
@@ -3557,7 +3555,7 @@
     @Override
     public void showInputMethodPickerFromClient(IInputMethodClient client,
             int auxiliarySubtypeMode) {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (!calledFromValidUserLocked()) {
                 return;
             }
@@ -3594,14 +3592,14 @@
      * A test API for CTS to make sure that the input method menu is showing.
      */
     public boolean isInputMethodPickerShownForTest() {
-        synchronized(mMethodMap) {
+        synchronized (ImfLock.class) {
             return mMenuController.isisInputMethodPickerShownForTestLocked();
         }
     }
 
     @BinderThread
     private void setInputMethod(@NonNull IBinder token, String id) {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (!calledWithValidTokenLocked(token)) {
                 return;
             }
@@ -3612,7 +3610,7 @@
     @BinderThread
     private void setInputMethodAndSubtype(@NonNull IBinder token, String id,
             InputMethodSubtype subtype) {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (!calledWithValidTokenLocked(token)) {
                 return;
             }
@@ -3629,7 +3627,7 @@
     @Override
     public void showInputMethodAndSubtypeEnablerFromClient(
             IInputMethodClient client, String inputMethodId) {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             // TODO(yukawa): Should we verify the display ID?
             if (!calledFromValidUserLocked()) {
                 return;
@@ -3641,7 +3639,7 @@
 
     @BinderThread
     private boolean switchToPreviousInputMethod(@NonNull IBinder token) {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (!calledWithValidTokenLocked(token)) {
                 return false;
             }
@@ -3713,7 +3711,7 @@
 
     @BinderThread
     private boolean switchToNextInputMethod(@NonNull IBinder token, boolean onlyCurrentIme) {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (!calledWithValidTokenLocked(token)) {
                 return false;
             }
@@ -3730,7 +3728,7 @@
 
     @BinderThread
     private boolean shouldOfferSwitchingToNextInputMethod(@NonNull IBinder token) {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (!calledWithValidTokenLocked(token)) {
                 return false;
             }
@@ -3746,7 +3744,7 @@
 
     @Override
     public InputMethodSubtype getLastInputMethodSubtype() {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (!calledFromValidUserLocked()) {
                 return null;
             }
@@ -3784,7 +3782,7 @@
                         + subtype.getLocale() + ", " + subtype.getMode());
             }
         }
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (!calledFromValidUserLocked()) {
                 return;
             }
@@ -3835,7 +3833,11 @@
     @Override
     public int getInputMethodWindowVisibleHeight() {
         // TODO(yukawa): Should we verify the display ID?
-        return mWindowManagerInternal.getInputMethodWindowVisibleHeight(mCurTokenDisplayId);
+        final int curTokenDisplayId;
+        synchronized (ImfLock.class) {
+            curTokenDisplayId = mCurTokenDisplayId;
+        }
+        return mWindowManagerInternal.getInputMethodWindowVisibleHeight(curTokenDisplayId);
     }
 
     @Override
@@ -3914,7 +3916,7 @@
     public void startImeTrace() {
         ImeTracing.getInstance().startTrace(null /* printwriter */);
         ArrayMap<IBinder, ClientState> clients;
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             clients = new ArrayMap<>(mClients);
         }
         for (ClientState state : clients.values()) {
@@ -3933,7 +3935,7 @@
     public void stopImeTrace() {
         ImeTracing.getInstance().stopTrace(null /* printwriter */);
         ArrayMap<IBinder, ClientState> clients;
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             clients = new ArrayMap<>(mClients);
         }
         for (ClientState state : clients.values()) {
@@ -3948,7 +3950,7 @@
     }
 
     private void dumpDebug(ProtoOutputStream proto, long fieldId) {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             final long token = proto.start(fieldId);
             proto.write(CUR_METHOD_ID, getSelectedMethodIdLocked());
             proto.write(CUR_SEQ, getSequenceNumberLocked());
@@ -3989,7 +3991,7 @@
         if (DEBUG) {
             Slog.d(TAG, "Got the notification of a user action.");
         }
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (getCurTokenLocked() != token) {
                 if (DEBUG) {
                     Slog.d(TAG, "Ignoring the user action notification from IMEs that are no longer"
@@ -4007,7 +4009,7 @@
     @BinderThread
     private void applyImeVisibility(IBinder token, IBinder windowToken, boolean setVisible) {
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.applyImeVisibility");
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (!calledWithValidTokenLocked(token)) {
                 return;
             }
@@ -4031,7 +4033,7 @@
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private void setInputMethodWithSubtypeIdLocked(IBinder token, String id, int subtypeId) {
         if (token == null) {
             if (mContext.checkCallingOrSelfPermission(
@@ -4056,6 +4058,7 @@
     }
 
     /** Called right after {@link IInputMethod#showSoftInput}. */
+    @GuardedBy("ImfLock.class")
     private void onShowHideSoftInputRequested(boolean show, IBinder requestToken,
             @SoftInputShowHideReason int reason) {
         final WindowManagerInternal.ImeTargetInfo info =
@@ -4070,7 +4073,7 @@
     @BinderThread
     private void hideMySoftInput(@NonNull IBinder token, int flags) {
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideMySoftInput");
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (!calledWithValidTokenLocked(token)) {
                 return;
             }
@@ -4090,7 +4093,7 @@
     @BinderThread
     private void showMySoftInput(@NonNull IBinder token, int flags) {
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showMySoftInput");
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (!calledWithValidTokenLocked(token)) {
                 return;
             }
@@ -4188,8 +4191,11 @@
                     final IBinder token = (IBinder) args.arg3;
                     ((IInputMethod) args.arg1).showSoftInput(
                             token, msg.arg1 /* flags */, (ResultReceiver) args.arg2);
-                    final IBinder requestToken = mShowRequestWindowMap.get(token);
-                    onShowHideSoftInputRequested(true /* show */, requestToken, reason);
+                    final IBinder requestToken;
+                    synchronized (ImfLock.class) {
+                        requestToken = mShowRequestWindowMap.get(token);
+                        onShowHideSoftInputRequested(true /* show */, requestToken, reason);
+                    }
                 } catch (RemoteException e) {
                 }
                 args.recycle();
@@ -4204,14 +4210,17 @@
                     final IBinder token = (IBinder) args.arg3;
                     ((IInputMethod)args.arg1).hideSoftInput(
                             token, 0 /* flags */, (ResultReceiver) args.arg2);
-                    final IBinder requestToken = mHideRequestWindowMap.get(token);
-                    onShowHideSoftInputRequested(false /* show */, requestToken, reason);
+                    final IBinder requestToken;
+                    synchronized (ImfLock.class) {
+                        requestToken = mHideRequestWindowMap.get(token);
+                        onShowHideSoftInputRequested(false /* show */, requestToken, reason);
+                    }
                 } catch (RemoteException e) {
                 }
                 args.recycle();
                 return true;
             case MSG_HIDE_CURRENT_INPUT_METHOD:
-                synchronized (mMethodMap) {
+                synchronized (ImfLock.class) {
                     final @SoftInputShowHideReason int reason = (int) msg.obj;
                     hideCurrentInputLocked(mCurFocusedWindow, 0, null, reason);
 
@@ -4221,8 +4230,10 @@
                 args = (SomeArgs)msg.obj;
                 try {
                     if (DEBUG) {
-                        Slog.v(TAG, "Sending attach of token: " + args.arg2 + " for display: "
-                                + mCurTokenDisplayId);
+                        synchronized (ImfLock.class) {
+                            Slog.v(TAG, "Sending attach of token: " + args.arg2 + " for display: "
+                                    + mCurTokenDisplayId);
+                        }
                     }
                     final IBinder token = (IBinder) args.arg2;
                     ((IInputMethod) args.arg1).initializeInternal(token,
@@ -4249,7 +4260,7 @@
                 return true;
             }
             case MSG_REMOVE_IME_SURFACE: {
-                synchronized (mMethodMap) {
+                synchronized (ImfLock.class) {
                     try {
                         if (mEnabledSession != null && mEnabledSession.session != null
                                 && !mShowRequested) {
@@ -4262,7 +4273,7 @@
             }
             case MSG_REMOVE_IME_SURFACE_FROM_WINDOW: {
                 IBinder windowToken = (IBinder) msg.obj;
-                synchronized (mMethodMap) {
+                synchronized (ImfLock.class) {
                     try {
                         if (windowToken == mCurFocusedWindow
                                 && mEnabledSession != null && mEnabledSession.session != null) {
@@ -4404,7 +4415,7 @@
     }
 
     private void handleSetInteractive(final boolean interactive) {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             mIsInteractive = interactive;
             updateSystemUiLocked(interactive ? mImeWindowVis : 0, mBackDisposition);
 
@@ -4428,7 +4439,7 @@
                 active ? 1 : 0, fullscreen ? 1 : 0, reportToImeController ? 1 : 0, 0, state));
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private boolean chooseNewDefaultIMELocked() {
         final InputMethodInfo imi = InputMethodUtils.getMostApplicableDefaultIME(
                 mSettings.getEnabledInputMethodListLocked());
@@ -4504,7 +4515,7 @@
         }
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     void buildInputMethodListLocked(boolean resetDefaultEnabledIme) {
         if (DEBUG) {
             Slog.d(TAG, "--- re-buildInputMethodList reset = " + resetDefaultEnabledIme
@@ -4612,7 +4623,7 @@
                 mSettings.getCurrentUserId(), 0 /* unused */, inputMethodList).sendToTarget();
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private void updateDefaultVoiceImeIfNeededLocked() {
         final String systemSpeechRecognizer =
                 mContext.getString(com.android.internal.R.string.config_systemSpeechRecognizer);
@@ -4654,7 +4665,7 @@
             intent.putExtra(Settings.EXTRA_INPUT_METHOD_ID, inputMethodId);
         }
         final int userId;
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             userId = mSettings.getCurrentUserId();
         }
         mContext.startActivityAsUser(intent, null, UserHandle.of(userId));
@@ -4678,7 +4689,7 @@
      * @param enabled {@code true} if {@code id} needs to be enabled.
      * @return {@code true} if the IME was previously enabled. {@code false} otherwise.
      */
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private boolean setInputMethodEnabledLocked(String id, boolean enabled) {
         List<Pair<String, ArrayList<String>>> enabledInputMethodsList = mSettings
                 .getEnabledInputMethodsAndSubtypeListLocked();
@@ -4714,7 +4725,7 @@
         }
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private void setSelectedInputMethodAndSubtypeLocked(InputMethodInfo imi, int subtypeId,
             boolean setSubtypeOnly) {
         mSettings.saveCurrentInputMethodAndSubtypeToHistory(getSelectedMethodIdLocked(),
@@ -4742,7 +4753,7 @@
         }
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private void resetSelectedInputMethodAndSubtypeLocked(String newDefaultIme) {
         InputMethodInfo imi = mMethodMap.get(newDefaultIme);
         int lastSubtypeId = NOT_A_SUBTYPE_ID;
@@ -4766,7 +4777,7 @@
      */
     @Override
     public InputMethodSubtype getCurrentInputMethodSubtype() {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             // TODO: Make this work even for non-current users?
             if (!calledFromValidUserLocked()) {
                 return null;
@@ -4775,7 +4786,7 @@
         }
     }
 
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     InputMethodSubtype getCurrentInputMethodSubtypeLocked() {
         String selectedMethodId = getSelectedMethodIdLocked();
         if (selectedMethodId == null) {
@@ -4816,19 +4827,14 @@
         return mCurrentSubtype;
     }
 
-    @Nullable
-    String getCurrentMethodId() {
-        return getSelectedMethodIdLocked();
-    }
-
     private List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             return getInputMethodListLocked(userId, DirectBootAwareness.AUTO);
         }
     }
 
     private List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId) {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             return getEnabledInputMethodListLocked(userId);
         }
     }
@@ -4836,7 +4842,7 @@
     private void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
             InlineSuggestionsRequestInfo requestInfo,
             IInlineSuggestionsRequestCallback callback) {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             onCreateInlineSuggestionsRequestLocked(userId, requestInfo, callback);
         }
     }
@@ -4853,7 +4859,7 @@
     }
 
     private boolean switchToInputMethod(String imeId, @UserIdInt int userId) {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (userId == mSettings.getCurrentUserId()) {
                 if (!mMethodMap.containsKey(imeId)
                         || !mSettings.getEnabledInputMethodListLocked()
@@ -4879,7 +4885,7 @@
     }
 
     private boolean setInputMethodEnabled(String imeId, boolean enabled, @UserIdInt int userId) {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (userId == mSettings.getCurrentUserId()) {
                 if (!mMethodMap.containsKey(imeId)) {
                     return false; // IME is not found.
@@ -4911,7 +4917,7 @@
             int displayId) {
         //TODO(b/150843766): Check if Input Token is valid.
         final IBinder curHostInputToken;
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (displayId != mCurTokenDisplayId || mCurHostInputToken == null) {
                 return false;
             }
@@ -4921,7 +4927,7 @@
     }
 
     private void reportImeControl(@Nullable IBinder windowToken, boolean imeParentChanged) {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (mCurFocusedWindow != windowToken) {
                 // mCurPerceptible was set by the focused window, but it is no longer in control,
                 // so we reset mCurPerceptible.
@@ -5029,7 +5035,7 @@
             throw new InvalidParameterException("contentUri must have content scheme");
         }
 
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             final int uid = Binder.getCallingUid();
             if (getSelectedMethodIdLocked() == null) {
                 return null;
@@ -5068,7 +5074,7 @@
 
     @BinderThread
     private void reportFullscreenMode(@NonNull IBinder token, boolean fullscreen) {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             if (!calledWithValidTokenLocked(token)) {
                 return;
             }
@@ -5151,7 +5157,7 @@
 
         final Printer p = new PrintWriterPrinter(pw);
 
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             p.println("Current Input Method Manager state:");
             int N = mMethodList.size();
             p.println("  Input Methods: mMethodMapUpdateCount=" + mMethodMapUpdateCount);
@@ -5436,7 +5442,7 @@
     @BinderThread
     @ShellCommandResult
     private int getLastSwitchUserId(@NonNull ShellCommand shellCommand) {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             shellCommand.getOutPrintWriter().println(mLastSwitchUserId);
             return ShellCommandResult.SUCCESS;
         }
@@ -5471,7 +5477,7 @@
                     break;
             }
         }
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             final PrintWriter pr = shellCommand.getOutPrintWriter();
             final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
                     mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
@@ -5513,7 +5519,7 @@
         final PrintWriter out = shellCommand.getOutPrintWriter();
         final PrintWriter error = shellCommand.getErrPrintWriter();
         boolean hasFailed = false;
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
                     mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
             for (int userId : userIds) {
@@ -5567,7 +5573,7 @@
      * @return {@code false} if it fails to enable the IME.  {@code false} otherwise.
      */
     @BinderThread
-    @GuardedBy("mMethodMap")
+    @GuardedBy("ImfLock.class")
     private boolean handleShellCommandEnableDisableInputMethodInternalLocked(
             @UserIdInt int userId, String imeId, boolean enabled, PrintWriter out,
             PrintWriter error) {
@@ -5637,7 +5643,7 @@
         final PrintWriter out = shellCommand.getOutPrintWriter();
         final PrintWriter error = shellCommand.getErrPrintWriter();
         boolean hasFailed = false;
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
                     mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
             for (int userId : userIds) {
@@ -5675,7 +5681,7 @@
     private int handleShellCommandResetInputMethod(@NonNull ShellCommand shellCommand) {
         final PrintWriter out = shellCommand.getOutPrintWriter();
         final int userIdToBeResolved = handleOptionsForCommandsThatOnlyHaveUserOption(shellCommand);
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
                     mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
             for (int userId : userIds) {
@@ -5693,11 +5699,15 @@
                     // Also reset the settings of the current IME
                     mSettings.putSelectedInputMethod(null);
                     // Disable all enabled IMEs.
-                    mSettings.getEnabledInputMethodListLocked().forEach(
-                            imi -> setInputMethodEnabledLocked(imi.getId(), false));
+                    for (InputMethodInfo inputMethodInfo :
+                            mSettings.getEnabledInputMethodListLocked()) {
+                        setInputMethodEnabledLocked(inputMethodInfo.getId(), false);
+                    }
                     // Re-enable with default enabled IMEs.
-                    InputMethodUtils.getDefaultEnabledImes(mContext, mMethodList).forEach(
-                            imi -> setInputMethodEnabledLocked(imi.getId(), true));
+                    for (InputMethodInfo imi :
+                            InputMethodUtils.getDefaultEnabledImes(mContext, mMethodList)) {
+                        setInputMethodEnabledLocked(imi.getId(), true);
+                    }
                     updateInputMethodsFromSettingsLocked(true /* enabledMayChange */);
                     InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mIPackageManager,
                             mSettings.getEnabledInputMethodListLocked(),
@@ -5766,7 +5776,7 @@
         }
         boolean isImeTraceEnabled = ImeTracing.getInstance().isEnabled();
         ArrayMap<IBinder, ClientState> clients;
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             clients = new ArrayMap<>(mClients);
         }
         for (ClientState state : clients.values()) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
index f70ad05..132be7d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java
@@ -46,6 +46,7 @@
 import android.widget.Switch;
 import android.widget.TextView;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
 import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
@@ -98,7 +99,7 @@
         int lastInputMethodSubtypeId = mSettings.getSelectedInputMethodSubtypeId(lastInputMethodId);
         if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId);
 
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             final List<ImeSubtypeListItem> imList = mSwitchingController
                     .getSortedInputMethodAndSubtypeListForImeMenuLocked(
                             showAuxSubtypes, isScreenLocked);
@@ -112,7 +113,7 @@
                 final InputMethodSubtype currentSubtype =
                         mService.getCurrentInputMethodSubtypeLocked();
                 if (currentSubtype != null) {
-                    final String curMethodId = mService.getCurrentMethodId();
+                    final String curMethodId = mService.getSelectedMethodIdLocked();
                     final InputMethodInfo currentImi = mMethodMap.get(curMethodId);
                     lastInputMethodSubtypeId = InputMethodUtils.getSubtypeIdFromHashCode(
                             currentImi, currentSubtype.hashCode());
@@ -175,7 +176,7 @@
             final ImeSubtypeListAdapter adapter = new ImeSubtypeListAdapter(dialogContext,
                     com.android.internal.R.layout.input_method_switch_item, imList, checkedItem);
             final DialogInterface.OnClickListener choiceListener = (dialog, which) -> {
-                synchronized (mMethodMap) {
+                synchronized (ImfLock.class) {
                     if (mIms == null || mIms.length <= which || mSubtypeIds == null
                             || mSubtypeIds.length <= which) {
                         return;
@@ -250,11 +251,12 @@
     }
 
     void hideInputMethodMenu() {
-        synchronized (mMethodMap) {
+        synchronized (ImfLock.class) {
             hideInputMethodMenuLocked();
         }
     }
 
+    @GuardedBy("ImfLock.class")
     void hideInputMethodMenuLocked() {
         if (DEBUG) Slog.v(TAG, "Hide switching menu");
 
@@ -299,7 +301,7 @@
             if (DEBUG) {
                 Slog.w(TAG, "HardKeyboardStatusChanged: available=" + available);
             }
-            synchronized (mMethodMap) {
+            synchronized (ImfLock.class) {
                 if (mSwitchingDialog != null && mSwitchingDialogTitleView != null
                         && mSwitchingDialog.isShowing()) {
                     mSwitchingDialogTitleView.findViewById(
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
index c340a2b..f8894c6 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -39,7 +39,7 @@
  * InputMethodSubtypeSwitchingController controls the switching behavior of the subtypes.
  *
  * <p>This class is designed to be used from and only from {@link InputMethodManagerService} by
- * using {@link InputMethodManagerService#mMethodMap} as a global lock.</p>
+ * using {@link ImfLock ImfLock.class} as a global lock.</p>
  */
 final class InputMethodSubtypeSwitchingController {
     private static final String TAG = InputMethodSubtypeSwitchingController.class.getSimpleName();
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 771bf19..1ba32ac 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -245,38 +245,19 @@
                 intent.putExtra(KEY_LOCATIONS, locationResult.asList().toArray(new Location[0]));
             }
 
-            PendingIntent.OnFinished onFinished = null;
-
-            // send() SHOULD only run the completion callback if it completes successfully. however,
-            // b/201299281 (which could not be fixed in the S timeframe) means that it's possible
-            // for send() to throw an exception AND run the completion callback. if this happens, we
-            // would over-release the wakelock... we take matters into our own hands to ensure that
-            // the completion callback can only be run if send() completes successfully. this means
-            // the completion callback may be run inline - but as we've never specified what thread
-            // the callback is run on, this is fine.
-            GatedCallback gatedCallback;
+            Runnable callback = null;
             if (onCompleteCallback != null) {
-                gatedCallback = new GatedCallback(() -> {
+                callback = () -> {
                     try {
                         onCompleteCallback.sendResult(null);
                     } catch (RemoteException e) {
                         throw e.rethrowFromSystemServer();
                     }
-                });
-                onFinished = (pI, i, rC, rD, rE) -> gatedCallback.run();
-            } else {
-                gatedCallback = new GatedCallback(null);
+                };
             }
 
-            mPendingIntent.send(
-                    mContext,
-                    0,
-                    intent,
-                    onFinished,
-                    null,
-                    null,
+            PendingIntentSender.send(mPendingIntent, mContext, intent, callback,
                     options.toBundle());
-            gatedCallback.allow();
         }
 
         @Override
@@ -1783,12 +1764,26 @@
 
         ICancellationSignal cancelTransport = CancellationSignal.createTransport();
         CancellationSignal.fromTransport(cancelTransport)
-                .setOnCancelListener(SingleUseCallback.wrap(
+                .setOnCancelListener(
                         () -> {
-                            synchronized (mLock) {
-                                removeRegistration(callback.asBinder(), registration);
+                            final long ident = Binder.clearCallingIdentity();
+                            try {
+                                synchronized (mLock) {
+                                    removeRegistration(callback.asBinder(), registration);
+                                }
+                            } catch (RuntimeException e) {
+                                // since this is within a oneway binder transaction there is nowhere
+                                // for exceptions to go - move onto another thread to crash system
+                                // server so we find out about it
+                                FgThread.getExecutor().execute(() -> {
+                                    throw new AssertionError(e);
+                                });
+                                throw e;
+                            } finally {
+                                Binder.restoreCallingIdentity(ident);
                             }
-                        }));
+
+                        });
         return cancelTransport;
     }
 
@@ -2733,103 +2728,84 @@
         }
     }
 
-    private static class SingleUseCallback extends IRemoteCallback.Stub implements Runnable,
-            CancellationSignal.OnCancelListener {
+    private static class PendingIntentSender {
 
-        public static @Nullable SingleUseCallback wrap(@Nullable Runnable callback) {
-            return callback == null ? null : new SingleUseCallback(callback);
-        }
-
-        @GuardedBy("this")
-        private @Nullable Runnable mCallback;
-
-        private SingleUseCallback(Runnable callback) {
-            mCallback = Objects.requireNonNull(callback);
-        }
-
-        @Override
-        public void sendResult(Bundle data) {
-            run();
-        }
-
-        @Override
-        public void onCancel() {
-            run();
-        }
-
-        @Override
-        public void run() {
-            Runnable callback;
-            synchronized (this) {
-                callback = mCallback;
-                mCallback = null;
+        // send() SHOULD only run the OnFinished callback if it completes successfully. however,
+        // b/201299281 (which could not be fixed in the S timeframe) means that it's possible
+        // for send() to throw an exception AND run the completion callback which breaks the
+        // guarantee we rely on. we take matters into our own hands to ensure that the OnFinished
+        // callback can only be run if send() completes successfully. this means the OnFinished
+        // callback may be run inline, so there is no longer any guarantee about what thread the
+        // callback will be run on.
+        public static void send(PendingIntent pendingIntent, Context context, Intent intent,
+                @Nullable final Runnable callback, Bundle options)
+                throws PendingIntent.CanceledException {
+            GatedCallback gatedCallback;
+            PendingIntent.OnFinished onFinished;
+            if (callback != null) {
+                gatedCallback = new GatedCallback(callback);
+                onFinished = (pI, i, rC, rD, rE) -> gatedCallback.run();
+            } else {
+                gatedCallback = null;
+                onFinished = null;
             }
 
-            // prevent this callback from being run more than once - otherwise this could provide an
-            // attack vector for a malicious app to break assumptions on how many times a callback
-            // may be invoked, and thus crash system server.
-            if (callback == null) {
-                return;
-            }
-
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                callback.run();
-            } catch (RuntimeException e) {
-                // since this is within a oneway binder transaction there is nowhere
-                // for exceptions to go - move onto another thread to crash system
-                // server so we find out about it
-                FgThread.getExecutor().execute(() -> {
-                    throw new AssertionError(e);
-                });
-                throw e;
-            } finally {
-                Binder.restoreCallingIdentity(identity);
+            pendingIntent.send(
+                    context,
+                    0,
+                    intent,
+                    onFinished,
+                    null,
+                    null,
+                    options);
+            if (gatedCallback != null) {
+                gatedCallback.allow();
             }
         }
-    }
 
-    private static class GatedCallback implements Runnable {
+        private static class GatedCallback implements Runnable {
 
-        private @Nullable Runnable mCallback;
+            @GuardedBy("this")
+            private @Nullable Runnable mCallback;
 
-        @GuardedBy("this")
-        private boolean mGate;
-        @GuardedBy("this")
-        private boolean mRun;
+            @GuardedBy("this")
+            private boolean mGate;
+            @GuardedBy("this")
+            private boolean mRun;
 
-        GatedCallback(@Nullable Runnable callback) {
-            mCallback = callback;
-        }
+            private GatedCallback(@Nullable Runnable callback) {
+                mCallback = callback;
+            }
 
-        public void allow() {
-            Runnable callback = null;
-            synchronized (this) {
-                mGate = true;
-                if (mRun && mCallback != null) {
-                    callback = mCallback;
-                    mCallback = null;
+            public void allow() {
+                Runnable callback = null;
+                synchronized (this) {
+                    mGate = true;
+                    if (mRun && mCallback != null) {
+                        callback = mCallback;
+                        mCallback = null;
+                    }
+                }
+
+                if (callback != null) {
+                    callback.run();
                 }
             }
 
-            if (callback != null) {
-                callback.run();
-            }
-        }
-
-        @Override
-        public void run() {
-            Runnable callback = null;
-            synchronized (this) {
-                mRun = true;
-                if (mGate && mCallback != null) {
-                    callback = mCallback;
-                    mCallback = null;
+            @Override
+            public void run() {
+                Runnable callback = null;
+                synchronized (this) {
+                    mRun = true;
+                    if (mGate && mCallback != null) {
+                        callback = mCallback;
+                        mCallback = null;
+                    }
                 }
-            }
 
-            if (callback != null) {
-                callback.run();
+                if (callback != null) {
+                    callback.run();
+                }
             }
         }
     }
@@ -2850,7 +2826,19 @@
             try {
                 mWakeLock.release();
             } catch (RuntimeException e) {
-                Log.e(TAG, "wakelock over-released by " + mIdentity, e);
+                // wakelock throws a RuntimeException instead of some more specific exception, so
+                // attempt to capture only actual RuntimeExceptions
+                if (e.getClass() == RuntimeException.class) {
+                    Log.e(TAG, "wakelock over-released by " + mIdentity, e);
+                } else {
+                    // since this is within a oneway binder transaction there is nowhere for
+                    // exceptions to go - move onto another thread to crash system server so we find
+                    // out about it
+                    FgThread.getExecutor().execute(() -> {
+                        throw new AssertionError(e);
+                    });
+                    throw e;
+                }
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }