From 44cab45149892f1438e077c38a51286783a1ced6 Mon Sep 17 00:00:00 2001 From: Wilson Wu Date: Fri, 3 Feb 2023 16:04:11 +0800 Subject: Fix wrong ime parent in embedded activity We have CL[1] to polish the ime transition. But in ActivityEmbedding, the ime layering target may higher than input target. And the ime parent didn't be updated since input target and layering target are unsync. Update ime parent for this case. [1]: I332c0e4fff62df5d7b793eda2767bb58fe85a938 Bug: 260387203 Test: Manual test with test apk in the bug Test: atest TaskFragmentTest#testUpdateImeParentForActivityEmbedding Change-Id: I78c82a0677d2ab7a1e247e91e5dfa1e5235e95bb --- .../java/com/android/server/wm/DisplayContent.java | 2 +- .../server/wm/ImeTargetVisibilityPolicy.java | 34 ++++++++++++++++++++-- .../com/android/server/wm/TaskFragmentTest.java | 31 ++++++++++++++++++++ 3 files changed, 63 insertions(+), 4 deletions(-) diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 8cd22fe71db5..ade2fe7152c0 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -4589,7 +4589,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ @VisibleForTesting SurfaceControl computeImeParent() { - if (!ImeTargetVisibilityPolicy.isReadyToComputeImeParent(mImeLayeringTarget, + if (!ImeTargetVisibilityPolicy.isValidToComputeImeParent(mImeLayeringTarget, mImeInputTarget)) { return null; } diff --git a/services/core/java/com/android/server/wm/ImeTargetVisibilityPolicy.java b/services/core/java/com/android/server/wm/ImeTargetVisibilityPolicy.java index 49218ad07dff..471bdf224b59 100644 --- a/services/core/java/com/android/server/wm/ImeTargetVisibilityPolicy.java +++ b/services/core/java/com/android/server/wm/ImeTargetVisibilityPolicy.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; +import android.annotation.Nullable; import android.os.IBinder; import android.view.WindowManager; @@ -50,10 +51,12 @@ public abstract class ImeTargetVisibilityPolicy { * Called when {@link DisplayContent#computeImeParent()} to check if it's valid to keep * computing the ime parent. * + * @param imeLayeringTarget The window which IME target to layer on top of it. + * @param imeInputTarget The window which start the input connection, receive input from IME. * @return {@code true} to keep computing the ime parent, {@code false} to defer this operation */ - public static boolean isReadyToComputeImeParent(WindowState imeLayeringTarget, - InputTarget imeInputTarget) { + public static boolean isValidToComputeImeParent(@Nullable WindowState imeLayeringTarget, + @Nullable InputTarget imeInputTarget) { if (imeLayeringTarget == null) { return false; } @@ -69,7 +72,9 @@ public abstract class ImeTargetVisibilityPolicy { boolean imeLayeringTargetMayUseIme = WindowManager.LayoutParams.mayUseInputMethod(imeLayeringTarget.mAttrs.flags) || imeLayeringTarget.mAttrs.type == TYPE_APPLICATION_STARTING; - + if (isImeTargetMismatchOnEmbedding(imeLayeringTarget, imeInputTarget)) { + return true; + } // Do not change parent if the window hasn't requested IME. var inputAndLayeringTargetsDisagree = (imeInputTarget == null || imeLayeringTarget.mActivityRecord != imeInputTarget.getActivityRecord()); @@ -77,4 +82,27 @@ public abstract class ImeTargetVisibilityPolicy { return !inputTargetStale; } + + private static boolean isImeTargetMismatchOnEmbedding( + @Nullable WindowState imeLayeringTarget, @Nullable InputTarget imeInputTarget) { + if (imeInputTarget == null || imeLayeringTarget == null) { + return false; + } + final ActivityRecord inputTargetRecord = imeInputTarget.getActivityRecord(); + final ActivityRecord layeringTargetRecord = imeLayeringTarget.getActivityRecord(); + final WindowState inputTargetWindow = imeInputTarget.getWindowState(); + if (inputTargetRecord == null || layeringTargetRecord == null + || inputTargetWindow == null) { + return false; + } + final boolean isImeTargetEmbedded = inputTargetRecord.isEmbedded() + && layeringTargetRecord.isEmbedded(); + // The IME layering target is calculated by the window hierarchy in DisplayContent. + // The layering target and input target may be different when the window hasn't started + // input connection, WMS hasn't received the target which reported from IMMS. We basically + // won't update IME parent for better IME transition. + // But in activity embedding, tapping a window won't update it to the top window so the IME + // layering target may higher than input target. Update IME parent for this case. + return isImeTargetEmbedded && imeLayeringTarget.compareTo(inputTargetWindow) > 0; + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java index dab842c4aa5a..df3af7d2f4fa 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java @@ -24,6 +24,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; @@ -613,4 +614,34 @@ public class TaskFragmentTest extends WindowTestsBase { assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, tf1.getOrientation(SCREEN_ORIENTATION_UNSET)); assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, task.getOrientation(SCREEN_ORIENTATION_UNSET)); } + + @Test + public void testUpdateImeParentForActivityEmbedding() { + // Setup two activities in ActivityEmbedding. + final Task task = createTask(mDisplayContent); + final TaskFragment tf0 = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .createActivityCount(1) + .setOrganizer(mOrganizer) + .setFragmentToken(new Binder()) + .build(); + final TaskFragment tf1 = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .createActivityCount(1) + .setOrganizer(mOrganizer) + .setFragmentToken(new Binder()) + .build(); + final ActivityRecord activity0 = tf0.getTopMostActivity(); + final ActivityRecord activity1 = tf1.getTopMostActivity(); + final WindowState win0 = createWindow(null, TYPE_BASE_APPLICATION, activity0, "win0"); + final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity1, "win1"); + doReturn(false).when(mDisplayContent).shouldImeAttachedToApp(); + + mDisplayContent.setImeInputTarget(win0); + mDisplayContent.setImeLayeringTarget(win1); + + // The ImeParent should be the display. + assertEquals(mDisplayContent.getImeContainer().getParent().getSurfaceControl(), + mDisplayContent.computeImeParent()); + } } -- cgit v1.2.3-59-g8ed1b