Merge ImeTargetChangeListener to InputMethodManagerInternal

This CL reworks how WMS tells IMMS about visibility changes on IME
targets windows.

With this CL we simply start using InputMethodManagerInternal.

Main motivations are:

 * We can see all the inter-component callbacks at one place in IMMS.
 * This removes one this-escape from the constructor.
 * No need to change WMS when we start relying on multiple instances
   of ImeVisibilityStateComputer.

At this momemt there must be no observable behavior change.

Fix: 356876005
Test: atest WmTests:WindowStateTests
Test: atest FrameworksInputMethodSystemServerTests
Test: presubmit
Flag: EXEMPT refactor
Change-Id: Ia711026604cd2e4588912fe2aa8bbd69808249aa
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index dba0465..4d06f50 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -238,6 +238,33 @@
     public abstract void removeImeSurface(int displayId);
 
     /**
+     * Called when a non-IME-focusable overlay window being the IME layering target (e.g. a
+     * window with {@link android.view.WindowManager.LayoutParams#FLAG_NOT_FOCUSABLE} and
+     * {@link android.view.WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM} flags)
+     * has changed its window visibility.
+     *
+     * @param hasVisibleOverlay  whether such an overlay window exists or not
+     * @param displayId          the display ID where the overlay window exists
+     */
+    public abstract void setHasVisibleImeLayeringOverlay(boolean hasVisibleOverlay, int displayId);
+
+    /**
+     * Called when the visibility of IME input target window has changed.
+     *
+     * @param imeInputTarget        the window token of the IME input target window
+     * @param visibleAndNotRemoved  {@code true} when the new window is made visible by
+     *                              {@code imeInputTarget} and the IME input target window has not
+     *                              been removed. The new window is considered to be visible when
+     *                              switching to the new visible IME input target window and
+     *                              starting input, or the existing input target becomes visible.
+     *                              In contrast, {@code false} when closing the input target, or the
+     *                              existing input target becomes invisible
+     * @param displayId             the display for which to update the IME window status
+     */
+    public abstract void onImeInputTargetVisibilityChanged(@NonNull IBinder imeInputTarget,
+            boolean visibleAndNotRemoved, int displayId);
+
+    /**
      * Updates the IME visibility, back disposition and show IME picker status for SystemUI.
      * TODO(b/189923292): Making SystemUI to be true IME icon controller vs. presenter that
      * controlled by IMMS.
@@ -389,6 +416,16 @@
                 public void removeImeSurface(int displayId) {
                 }
 
+                @Override
+                public void setHasVisibleImeLayeringOverlay(boolean hasVisibleOverlay,
+                        int displayId) {
+                }
+
+                @Override
+                public void onImeInputTargetVisibilityChanged(@NonNull IBinder imeInputTarget,
+                        boolean visibleAndNotRemoved, int displayId) {
+                }
+
                 @ImfLockFree
                 @Override
                 public void updateImeWindowStatus(boolean disableImeIcon, int displayId) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 3f666a2..1077c50 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -44,7 +44,6 @@
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.DISPLAY_IME_POLICY_HIDE;
 import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.inputmethod.ConnectionlessHandwritingCallback.CONNECTIONLESS_HANDWRITING_ERROR_OTHER;
 import static android.view.inputmethod.ConnectionlessHandwritingCallback.CONNECTIONLESS_HANDWRITING_ERROR_UNSUPPORTED;
 
@@ -188,7 +187,6 @@
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.utils.PriorityDump;
-import com.android.server.wm.ImeTargetChangeListener;
 import com.android.server.wm.WindowManagerInternal;
 
 import java.io.FileDescriptor;
@@ -968,37 +966,6 @@
             InputMethodDrawsNavBarResourceMonitor.registerCallback(context, mService.mIoHandler,
                     mService::onUpdateResourceOverlay);
 
-            // Also hook up ImeTargetChangeListener.
-            // TODO(b/356876005): Merge this into InputMethodManagerInternal.
-            final var windowManagerInternal = mService.mWindowManagerInternal;
-            windowManagerInternal.setInputMethodTargetChangeListener(new ImeTargetChangeListener() {
-                @Override
-                public void onImeTargetOverlayVisibilityChanged(@NonNull IBinder overlayWindowToken,
-                        @WindowManager.LayoutParams.WindowType int windowType, boolean visible,
-                        boolean removed, int displayId) {
-                    // Ignoring the starting window since it's ok to cover the IME target
-                    // window in temporary without affecting the IME visibility.
-                    final boolean hasOverlay = visible && !removed
-                            && windowType != TYPE_APPLICATION_STARTING;
-                    synchronized (ImfLock.class) {
-                        final var userId = mService.resolveImeUserIdFromDisplayIdLocked(displayId);
-                        mService.getUserData(userId).mVisibilityStateComputer
-                                .setHasVisibleImeLayeringOverlay(hasOverlay);
-                    }
-                }
-
-                @Override
-                public void onImeInputTargetVisibilityChanged(IBinder imeInputTarget,
-                        boolean visibleRequested, boolean removed, int displayId) {
-                    final boolean visibleAndNotRemoved = visibleRequested && !removed;
-                    synchronized (ImfLock.class) {
-                        final var userId = mService.resolveImeUserIdFromDisplayIdLocked(displayId);
-                        mService.getUserData(userId).mVisibilityStateComputer
-                                .onImeInputTargetVisibilityChanged(imeInputTarget,
-                                        visibleAndNotRemoved);
-                    }
-                }
-            });
             // Also schedule user init tasks onto an I/O thread.
             initializeUsersAsync(mService.mUserManagerInternal.getUserIds());
         }
@@ -6000,6 +5967,25 @@
             mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE).sendToTarget();
         }
 
+        @Override
+        public void setHasVisibleImeLayeringOverlay(boolean hasVisibleOverlay, int displayId) {
+            synchronized (ImfLock.class) {
+                final var userId = resolveImeUserIdFromDisplayIdLocked(displayId);
+                getUserData(userId).mVisibilityStateComputer.setHasVisibleImeLayeringOverlay(
+                        hasVisibleOverlay);
+            }
+        }
+
+        @Override
+        public void onImeInputTargetVisibilityChanged(@NonNull IBinder imeInputTarget,
+                boolean visibleAndNotRemoved, int displayId) {
+            synchronized (ImfLock.class) {
+                final var userId = resolveImeUserIdFromDisplayIdLocked(displayId);
+                getUserData(userId).mVisibilityStateComputer.onImeInputTargetVisibilityChanged(
+                        imeInputTarget, visibleAndNotRemoved);
+            }
+        }
+
         @ImfLockFree
         @Override
         public void updateImeWindowStatus(boolean disableImeIcon, int displayId) {
diff --git a/services/core/java/com/android/server/wm/ImeTargetChangeListener.java b/services/core/java/com/android/server/wm/ImeTargetChangeListener.java
deleted file mode 100644
index e94f17c..0000000
--- a/services/core/java/com/android/server/wm/ImeTargetChangeListener.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import android.annotation.NonNull;
-import android.os.IBinder;
-import android.view.WindowManager;
-
-/**
- * Callback the IME targeting window visibility change state for
- * {@link com.android.server.inputmethod.InputMethodManagerService} to manage the IME surface
- * visibility and z-ordering.
- */
-public interface ImeTargetChangeListener {
-    /**
-     * Called when a non-IME-focusable overlay window being the IME layering target (e.g. a
-     * window with {@link android.view.WindowManager.LayoutParams#FLAG_NOT_FOCUSABLE} and
-     * {@link android.view.WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM} flags)
-     * has changed its window visibility.
-     *
-     * @param overlayWindowToken the window token of the overlay window.
-     * @param windowType         the window type of the overlay window.
-     * @param visible            the visibility of the overlay window, {@code true} means visible
-     *                           and {@code false} otherwise.
-     * @param removed            Whether the IME target overlay window has being removed.
-     * @param displayId          display ID where the overlay window exists.
-     */
-    default void onImeTargetOverlayVisibilityChanged(@NonNull IBinder overlayWindowToken,
-            @WindowManager.LayoutParams.WindowType int windowType,
-            boolean visible, boolean removed, int displayId) {
-    }
-
-    /**
-     * Called when the visibility of IME input target window has changed.
-     *
-     * @param imeInputTarget   the window token of the IME input target window.
-     * @param visible          the new window visibility made by {@code imeInputTarget}. visible is
-     *                         {@code true} when switching to the new visible IME input target
-     *                         window and started input, or the same input target relayout to
-     *                         visible from invisible. In contrast, visible is {@code false} when
-     *                         closing the input target, or the same input target relayout to
-     *                         invisible from visible.
-     * @param removed          Whether the IME input target window has being removed.
-     * @param displayId        display ID where the overlay window exists.
-     */
-    default void onImeInputTargetVisibilityChanged(@NonNull IBinder imeInputTarget, boolean visible,
-            boolean removed, int displayId) {
-    }
-}
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index a574845..82d39a3 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -990,16 +990,6 @@
     }
 
     /**
-     * Sets by the {@link com.android.server.inputmethod.InputMethodManagerService} to monitor
-     * the visibility change of the IME targeted windows.
-     *
-     * @see ImeTargetChangeListener#onImeTargetOverlayVisibilityChanged
-     * @see ImeTargetChangeListener#onImeInputTargetVisibilityChanged
-     */
-    public abstract void setInputMethodTargetChangeListener(
-            @NonNull ImeTargetChangeListener listener);
-
-    /**
      * Moves the {@link WindowToken} {@code binder} to the display specified by {@code displayId}.
      */
     public abstract void moveWindowTokenToDisplay(IBinder binder, int displayId);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index cf92f1b..5749272 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -789,7 +789,6 @@
     boolean mHardKeyboardAvailable;
     WindowManagerInternal.OnHardKeyboardStatusChangeListener mHardKeyboardStatusChangeListener;
     WindowManagerInternal.OnImeRequestedChangedListener mOnImeRequestedChangedListener;
-    @Nullable ImeTargetChangeListener mImeTargetChangeListener;
 
     SettingsObserver mSettingsObserver;
     final EmbeddedWindowController mEmbeddedWindowController;
@@ -3517,29 +3516,28 @@
     void dispatchImeTargetOverlayVisibilityChanged(@NonNull IBinder token,
             @WindowManager.LayoutParams.WindowType int windowType, boolean visible,
             boolean removed, int displayId) {
-        if (mImeTargetChangeListener != null) {
-            if (DEBUG_INPUT_METHOD) {
-                Slog.d(TAG, "onImeTargetOverlayVisibilityChanged, win=" + mWindowMap.get(token)
-                        + ", type=" + ViewDebug.intToString(WindowManager.LayoutParams.class,
-                        "type", windowType) + "visible=" + visible + ", removed=" + removed
-                        + ", displayId=" + displayId);
-            }
-            mH.post(() -> mImeTargetChangeListener.onImeTargetOverlayVisibilityChanged(token,
-                    windowType, visible, removed, displayId));
+        if (DEBUG_INPUT_METHOD) {
+            Slog.d(TAG, "onImeTargetOverlayVisibilityChanged, win=" + mWindowMap.get(token)
+                    + ", type=" + ViewDebug.intToString(WindowManager.LayoutParams.class,
+                    "type", windowType) + "visible=" + visible + ", removed=" + removed
+                    + ", displayId=" + displayId);
         }
+        // Ignoring the starting window since it's ok to cover the IME target
+        // window in temporary without affecting the IME visibility.
+        final boolean hasOverlay = visible && !removed && windowType != TYPE_APPLICATION_STARTING;
+        mH.post(() -> InputMethodManagerInternal.get().setHasVisibleImeLayeringOverlay(hasOverlay,
+                displayId));
     }
 
     void dispatchImeInputTargetVisibilityChanged(@NonNull IBinder token, boolean visible,
             boolean removed, int displayId) {
-        if (mImeTargetChangeListener != null) {
-            if (DEBUG_INPUT_METHOD) {
-                Slog.d(TAG, "onImeInputTargetVisibilityChanged, win=" + mWindowMap.get(token)
-                        + "visible=" + visible + ", removed=" + removed
-                        + ", displayId" + displayId);
-            }
-            mH.post(() -> mImeTargetChangeListener.onImeInputTargetVisibilityChanged(token,
-                    visible, removed, displayId));
+        if (DEBUG_INPUT_METHOD) {
+            Slog.d(TAG, "onImeInputTargetVisibilityChanged, win=" + mWindowMap.get(token)
+                    + "visible=" + visible + ", removed=" + removed + ", displayId=" + displayId);
         }
+        final boolean visibleAndNotRemoved = visible && !removed;
+        mH.post(() -> InputMethodManagerInternal.get().onImeInputTargetVisibilityChanged(token,
+                visibleAndNotRemoved, displayId));
     }
 
     @Override
@@ -8675,13 +8673,6 @@
         }
 
         @Override
-        public void setInputMethodTargetChangeListener(@NonNull ImeTargetChangeListener listener) {
-            synchronized (mGlobalLock) {
-                mImeTargetChangeListener = listener;
-            }
-        }
-
-        @Override
         public void setOrientationRequestPolicy(boolean respected,
                 int[] fromOrientations, int[] toOrientations) {
             synchronized (mGlobalLock) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 11df331..39276a1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -77,8 +77,10 @@
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.atMost;
 import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.when;
 
 import android.content.res.CompatibilityInfo;
@@ -111,6 +113,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.testutils.StubTransaction;
 import com.android.server.wm.SensitiveContentPackages.PackageInfo;
 
@@ -1337,8 +1340,8 @@
 
     @Test
     public void testImeTargetChangeListener_OnImeInputTargetVisibilityChanged() {
-        final TestImeTargetChangeListener listener = new TestImeTargetChangeListener();
-        mWm.mImeTargetChangeListener = listener;
+        final InputMethodManagerInternal immi = InputMethodManagerInternal.get();
+        spyOn(immi);
 
         final WindowState imeTarget = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
                 createActivityRecord(mDisplayContent), "imeTarget");
@@ -1347,32 +1350,26 @@
         makeWindowVisible(imeTarget);
         mDisplayContent.setImeInputTarget(imeTarget);
         waitHandlerIdle(mWm.mH);
-
-        assertThat(listener.mImeTargetToken).isEqualTo(imeTarget.mClient.asBinder());
-        assertThat(listener.mIsRemoved).isFalse();
-        assertThat(listener.mIsVisibleForImeInputTarget).isTrue();
-        assertThat(listener.mDisplayId).isEqualTo(mDisplayContent.getDisplayId());
+        verify(immi).onImeInputTargetVisibilityChanged(imeTarget.mClient.asBinder(),
+                true /* visibleAndNotRemoved */, mDisplayContent.getDisplayId());
+        reset(immi);
 
         imeTarget.mActivityRecord.setVisibleRequested(false);
         waitHandlerIdle(mWm.mH);
-
-        assertThat(listener.mImeTargetToken).isEqualTo(imeTarget.mClient.asBinder());
-        assertThat(listener.mIsRemoved).isFalse();
-        assertThat(listener.mIsVisibleForImeInputTarget).isFalse();
-        assertThat(listener.mDisplayId).isEqualTo(mDisplayContent.getDisplayId());
+        verify(immi).onImeInputTargetVisibilityChanged(imeTarget.mClient.asBinder(),
+                false /* visibleAndNotRemoved */, mDisplayContent.getDisplayId());
+        reset(immi);
 
         imeTarget.removeImmediately();
-        assertThat(listener.mImeTargetToken).isEqualTo(imeTarget.mClient.asBinder());
-        assertThat(listener.mIsRemoved).isTrue();
-        assertThat(listener.mIsVisibleForImeInputTarget).isFalse();
-        assertThat(listener.mDisplayId).isEqualTo(mDisplayContent.getDisplayId());
+        verify(immi).onImeInputTargetVisibilityChanged(imeTarget.mClient.asBinder(),
+                false /* visibleAndNotRemoved */, mDisplayContent.getDisplayId());
     }
 
     @SetupWindows(addWindows = {W_INPUT_METHOD})
     @Test
     public void testImeTargetChangeListener_OnImeTargetOverlayVisibilityChanged() {
-        final TestImeTargetChangeListener listener = new TestImeTargetChangeListener();
-        mWm.mImeTargetChangeListener = listener;
+        final InputMethodManagerInternal immi = InputMethodManagerInternal.get();
+        spyOn(immi);
 
         // Scenario 1: test addWindow/relayoutWindow to add Ime layering overlay window as visible.
         final WindowToken windowToken = createTestWindowToken(TYPE_APPLICATION_OVERLAY,
@@ -1402,10 +1399,10 @@
         final WindowState imeLayeringTargetOverlay = mDisplayContent.getWindow(
                 w -> w.mClient.asBinder() == client.asBinder());
         assertThat(imeLayeringTargetOverlay.isVisible()).isTrue();
-        assertThat(listener.mImeTargetToken).isEqualTo(client.asBinder());
-        assertThat(listener.mIsRemoved).isFalse();
-        assertThat(listener.mIsVisibleForImeTargetOverlay).isTrue();
-        assertThat(listener.mDisplayId).isEqualTo(mDisplayContent.getDisplayId());
+        verify(immi, atLeast(1))
+                .setHasVisibleImeLayeringOverlay(true /* hasVisibleOverlay */,
+                        mDisplayContent.getDisplayId());
+        reset(immi);
 
         // Scenario 2: test relayoutWindow to let the Ime layering target overlay window invisible.
         mWm.relayoutWindow(session, client, params, 100, 200, View.GONE, 0, 0, 0,
@@ -1413,19 +1410,16 @@
         waitHandlerIdle(mWm.mH);
 
         assertThat(imeLayeringTargetOverlay.isVisible()).isFalse();
-        assertThat(listener.mImeTargetToken).isEqualTo(client.asBinder());
-        assertThat(listener.mIsRemoved).isFalse();
-        assertThat(listener.mIsVisibleForImeTargetOverlay).isFalse();
-        assertThat(listener.mDisplayId).isEqualTo(mDisplayContent.getDisplayId());
+        verify(immi).setHasVisibleImeLayeringOverlay(false /* hasVisibleOverlay */,
+                mDisplayContent.getDisplayId());
+        reset(immi);
 
         // Scenario 3: test removeWindow to remove the Ime layering target overlay window.
         mWm.removeClientToken(session, client.asBinder());
         waitHandlerIdle(mWm.mH);
 
-        assertThat(listener.mImeTargetToken).isEqualTo(client.asBinder());
-        assertThat(listener.mIsRemoved).isTrue();
-        assertThat(listener.mIsVisibleForImeTargetOverlay).isFalse();
-        assertThat(listener.mDisplayId).isEqualTo(mDisplayContent.getDisplayId());
+        verify(immi).setHasVisibleImeLayeringOverlay(false /* hasVisibleOverlay */,
+                mDisplayContent.getDisplayId());
     }
 
     @Test
@@ -1468,31 +1462,4 @@
         mWm.mSensitiveContentPackages.removeBlockScreenCaptureForApps(blockedPackages);
         assertFalse(window.isSecureLocked());
     }
-
-    private static class TestImeTargetChangeListener implements ImeTargetChangeListener {
-        private IBinder mImeTargetToken;
-        private boolean mIsRemoved;
-        private boolean mIsVisibleForImeTargetOverlay;
-        private boolean mIsVisibleForImeInputTarget;
-        private int mDisplayId;
-
-        @Override
-        public void onImeTargetOverlayVisibilityChanged(IBinder overlayWindowToken,
-                @WindowManager.LayoutParams.WindowType int windowType, boolean visible,
-                boolean removed, int displayId) {
-            mImeTargetToken = overlayWindowToken;
-            mIsVisibleForImeTargetOverlay = visible;
-            mIsRemoved = removed;
-            mDisplayId = displayId;
-        }
-
-        @Override
-        public void onImeInputTargetVisibilityChanged(IBinder imeInputTarget,
-                boolean visibleRequested, boolean removed, int displayId) {
-            mImeTargetToken = imeInputTarget;
-            mIsVisibleForImeInputTarget = visibleRequested;
-            mIsRemoved = removed;
-            mDisplayId = displayId;
-        }
-    }
 }