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

Commit e2ff33ed authored by Felix Stern's avatar Felix Stern Committed by Android (Google) Code Review
Browse files

Merge "Remove ImeTargetVisibilityPolicy" into main

parents 126a2abe 3d7e6c22
Loading
Loading
Loading
Loading
+2 −6
Original line number Diff line number Diff line
@@ -192,7 +192,6 @@ import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeS
import com.android.server.pm.UserManagerInternal;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.utils.PriorityDump;
import com.android.server.wm.ImeTargetVisibilityPolicy;
import com.android.server.wm.WindowManagerInternal;

import java.io.FileDescriptor;
@@ -409,8 +408,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
    @MultiUserUnawareField
    private final InputMethodMenuController mMenuController;
    private final InputMethodMenuControllerNew mMenuControllerNew;
    @NonNull
    private final ImeTargetVisibilityPolicy mImeTargetVisibilityPolicy;

    /**
     * Cache the result of {@code LocalServices.getService(AudioManagerInternal.class)}.
@@ -1217,7 +1214,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
            mImePlatformCompatUtils = new ImePlatformCompatUtils();
            mInputMethodDeviceConfigs = new InputMethodDeviceConfigs();
            mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
            mImeTargetVisibilityPolicy = LocalServices.getService(ImeTargetVisibilityPolicy.class);

            mSlotIme = mContext.getString(com.android.internal.R.string.status_bar_ime);

@@ -5395,7 +5391,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
    @VisibleForTesting
    @GuardedBy("ImfLock.class")
    void showImeScreenshot(IBinder imeTarget, int displayId, @UserIdInt int userId) {
        if (mImeTargetVisibilityPolicy.showImeScreenshot(imeTarget, displayId)) {
        if (mWindowManagerInternal.showImeScreenshot(imeTarget, displayId)) {
            onShowHideSoftInputRequested(false /* show */, imeTarget,
                    SoftInputShowHideReason.SHOW_IME_SCREENSHOT_FROM_IMMS, null /* statsToken */,
                    userId);
@@ -5412,7 +5408,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
    @VisibleForTesting
    @GuardedBy("ImfLock.class")
    void removeImeScreenshot(IBinder imeTarget, int displayId, @UserIdInt int userId) {
        if (mImeTargetVisibilityPolicy.removeImeScreenshot(displayId)) {
        if (mWindowManagerInternal.removeImeScreenshot(displayId)) {
            onShowHideSoftInputRequested(false /* show */, imeTarget,
                    SoftInputShowHideReason.REMOVE_IME_SCREENSHOT_FROM_IMMS, null /* statsToken */,
                    userId);
+81 −1
Original line number Diff line number Diff line
@@ -4902,7 +4902,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
    @VisibleForTesting
    @Nullable
    WindowContainer computeImeParent() {
        if (!ImeTargetVisibilityPolicy.canComputeImeParent(mImeLayeringTarget, mImeInputTarget)) {
        if (!canComputeImeParent(mImeLayeringTarget, mImeInputTarget)) {
            return null;
        }
        // Attach it to app if the IME layering target is part of an app that is covering the entire
@@ -4915,6 +4915,86 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
        return mImeWindowsContainer.getParent();
    }

    /**
     * Called from {@link #computeImeParent()} to check if we can compute the new IME parent
     * based on the given IME layering and IME input target.
     *
     * @param imeLayeringTarget The window the IME is on top of.
     * @param imeInputTarget    The target which receives input from the IME.
     * @return {@code true} to keep computing the IME parent, {@code false} to defer this operation.
     */
    private static boolean canComputeImeParent(@Nullable WindowState imeLayeringTarget,
            @Nullable InputTarget imeInputTarget) {
        if (imeLayeringTarget == null) {
            return false;
        }
        if (shouldComputeImeParentForEmbeddedActivity(imeLayeringTarget, imeInputTarget)) {
            return true;
        }
        // Ensure changing the IME parent when the layering target that may use IME has
        // became to the input target for preventing IME flickers.
        // Note that:
        // 1) For the imeLayeringTarget that may not use IME but requires IME on top
        // of it (e.g. an overlay window with NOT_FOCUSABLE|ALT_FOCUSABLE_IM flags), we allow
        // it to re-parent the IME on top the display to keep the legacy behavior.
        // 2) Even though the starting window won't use IME, the associated activity
        // behind the starting window may request the input. If so, then we should still hold
        // the IME parent change until the activity started the input.
        boolean imeLayeringTargetMayUseIme =
                WindowManager.LayoutParams.mayUseInputMethod(imeLayeringTarget.mAttrs.flags)
                        || imeLayeringTarget.mAttrs.type == TYPE_APPLICATION_STARTING;
        // Do not change parent if the window hasn't requested IME.
        boolean inputAndLayeringTargetsDisagree = (imeInputTarget == null
                || imeLayeringTarget.mActivityRecord != imeInputTarget.getActivityRecord());
        boolean inputTargetStale = imeLayeringTargetMayUseIme && inputAndLayeringTargetsDisagree;

        return !inputTargetStale;
    }

    /**
     * Called from {@link #computeImeParent()} to check if the IME surface parent should be
     * updated in ActivityEmbeddings, based on the given IME layering and IME input target.
     *
     * <p>As the IME layering target is calculated according to the window hierarchy by
     * {@link #computeImeLayeringTarget}, the layering target and input target may be different
     * when the window hasn't started input connection, WindowManagerService hasn't yet received
     * the input target which reported from InputMethodManagerService. To make sure the IME
     * surface will be shown on the best fit IME layering target, we basically won't update IME
     * parent until both IME layering and input target are updated, for better IME transition.
     *
     * <p>However, in activity embedding, tapping a window won't update it to the top window so the
     * IME layering target may be higher than input target. Update IME parent in this case.
     *
     * @param imeLayeringTarget The window the IME is on top of.
     * @param imeInputTarget    The target which receives input from the IME.
     *
     * @return {@code true} means the layer of IME layering target is higher than the input target
     * and {@link #computeImeParent()} should keep progressing to update the IME surface parent
     * on the display in case the IME surface was left behind.
     */
    private static boolean shouldComputeImeParentForEmbeddedActivity(
            @Nullable WindowState imeLayeringTarget, @Nullable InputTarget imeInputTarget) {
        if (imeInputTarget == null || imeLayeringTarget == null) {
            return false;
        }
        final WindowState inputTargetWindow = imeInputTarget.getWindowState();
        if (inputTargetWindow == null || !imeLayeringTarget.isAttached()
                || !inputTargetWindow.isAttached()) {
            return false;
        }

        final ActivityRecord inputTargetRecord = imeInputTarget.getActivityRecord();
        final ActivityRecord layeringTargetRecord = imeLayeringTarget.getActivityRecord();
        if (inputTargetRecord == null || layeringTargetRecord == null
                || inputTargetRecord == layeringTargetRecord
                || (inputTargetRecord.getTask() != layeringTargetRecord.getTask())
                || !inputTargetRecord.isEmbedded() || !layeringTargetRecord.isEmbedded()) {
            // Check whether the input target and layering target are embedded in the same Task.
            return false;
        }
        return imeLayeringTarget.compareTo(inputTargetWindow) > 0;
    }

    void setLayoutNeeded() {
        if (DEBUG_LAYOUT) Slog.w(TAG_WM, "setLayoutNeeded: callers=" + Debug.getCallers(3));
        mLayoutNeeded = true;
+0 −131
Original line number Diff line number Diff line
/*
 * 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 static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IBinder;
import android.view.WindowManager;

/**
 * A class for {@link com.android.server.inputmethod.InputMethodManagerService} to
 * control IME visibility operations in {@link WindowManagerService}.
 */
public abstract class ImeTargetVisibilityPolicy {

    /**
     * Shows the IME screenshot and attaches it to the given IME target window.
     *
     * @param imeTarget the token of the IME target window.
     * @param displayId the ID of the display to show the screenshot on.
     * @return {@code true} if successful, {@code false} otherwise.
     */
    public abstract boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId);

    /**
     * Removes the IME screenshot from the given display.
     *
     * @param displayId The target display of showing IME screenshot.
     * @return {@code true} if successful, {@code false} otherwise.
     */
    public abstract boolean removeImeScreenshot(int displayId);

    /**
     * Called from {@link DisplayContent#computeImeParent()} to check if we can compute the new IME
     * parent based on the given IME layering and IME input target.
     *
     * @param imeLayeringTarget The window the IME is on top of.
     * @param imeInputTarget    The target which receives input from the IME.
     * @return {@code true} to keep computing the IME parent, {@code false} to defer this operation.
     */
    public static boolean canComputeImeParent(@Nullable WindowState imeLayeringTarget,
            @Nullable InputTarget imeInputTarget) {
        if (imeLayeringTarget == null) {
            return false;
        }
        if (shouldComputeImeParentForEmbeddedActivity(imeLayeringTarget, imeInputTarget)) {
            return true;
        }
        // Ensure changing the IME parent when the layering target that may use IME has
        // became to the input target for preventing IME flickers.
        // Note that:
        // 1) For the imeLayeringTarget that may not use IME but requires IME on top
        // of it (e.g. an overlay window with NOT_FOCUSABLE|ALT_FOCUSABLE_IM flags), we allow
        // it to re-parent the IME on top the display to keep the legacy behavior.
        // 2) Even though the starting window won't use IME, the associated activity
        // behind the starting window may request the input. If so, then we should still hold
        // the IME parent change until the activity started the input.
        boolean imeLayeringTargetMayUseIme =
                WindowManager.LayoutParams.mayUseInputMethod(imeLayeringTarget.mAttrs.flags)
                        || imeLayeringTarget.mAttrs.type == TYPE_APPLICATION_STARTING;
        // Do not change parent if the window hasn't requested IME.
        boolean inputAndLayeringTargetsDisagree = (imeInputTarget == null
                || imeLayeringTarget.mActivityRecord != imeInputTarget.getActivityRecord());
        boolean inputTargetStale = imeLayeringTargetMayUseIme && inputAndLayeringTargetsDisagree;

        return !inputTargetStale;
    }


    /**
     * Called from {@link DisplayContent#computeImeParent()} to check if the IME surface parent
     * should be updated in ActivityEmbeddings, based on the given IME layering and IME input
     * target.
     *
     * <p>As the IME layering target is calculated according to the window hierarchy by
     * {@link DisplayContent#computeImeLayeringTarget}, the layering target and input target may be
     * different when the window hasn't started input connection, WindowManagerService hasn't yet
     * received the input target which reported from InputMethodManagerService. To make sure the IME
     * surface will be shown on the best fit IME layering target, we basically won't update IME
     * parent until both IME layering and input target are updated, for better IME transition.
     *
     * <p>However, in activity embedding, tapping a window won't update it to the top window so the
     * IME layering target may be higher than input target. Update IME parent in this case.
     *
     * @param imeLayeringTarget The window the IME is on top of.
     * @param imeInputTarget    The target which receives input from the IME.
     *
     * @return {@code true} means the layer of IME layering target is higher than the input target
     * and {@link DisplayContent#computeImeParent()} should keep progressing to update the IME
     * surface parent on the display in case the IME surface was left behind.
     */
    private static boolean shouldComputeImeParentForEmbeddedActivity(
            @Nullable WindowState imeLayeringTarget, @Nullable InputTarget imeInputTarget) {
        if (imeInputTarget == null || imeLayeringTarget == null) {
            return false;
        }
        final WindowState inputTargetWindow = imeInputTarget.getWindowState();
        if (inputTargetWindow == null || !imeLayeringTarget.isAttached()
                || !inputTargetWindow.isAttached()) {
            return false;
        }

        final ActivityRecord inputTargetRecord = imeInputTarget.getActivityRecord();
        final ActivityRecord layeringTargetRecord = imeLayeringTarget.getActivityRecord();
        if (inputTargetRecord == null || layeringTargetRecord == null
                || inputTargetRecord == layeringTargetRecord
                || (inputTargetRecord.getTask() != layeringTargetRecord.getTask())
                || !inputTargetRecord.isEmbedded() || !layeringTargetRecord.isEmbedded()) {
            // Check whether the input target and layering target are embedded in the same Task.
            return false;
        }
        return imeLayeringTarget.compareTo(inputTargetWindow) > 0;
    }
}
+17 −0
Original line number Diff line number Diff line
@@ -713,6 +713,23 @@ public abstract class WindowManagerInternal {
     */
    public abstract void updateImeTargetWindow(@NonNull IBinder windowToken);

    /**
     * Shows the IME screenshot and attaches it to the given IME target window.
     *
     * @param imeTarget the token of the IME target window.
     * @param displayId the ID of the display to show the screenshot on.
     * @return {@code true} if successful, {@code false} otherwise.
     */
    public abstract boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId);

    /**
     * Removes the IME screenshot from the given display.
     *
     * @param displayId The target display of showing IME screenshot.
     * @return {@code true} if successful, {@code false} otherwise.
     */
    public abstract boolean removeImeScreenshot(int displayId);

    /**
      * Returns true when the hardware keyboard is available.
      */
+32 −36
Original line number Diff line number Diff line
@@ -1514,8 +1514,6 @@ public class WindowManagerService extends IWindowManager.Stub
        mConstants.start(new HandlerExecutor(mH));

        LocalServices.addService(WindowManagerInternal.class, new LocalService());
        LocalServices.addService(
                ImeTargetVisibilityPolicy.class, new ImeTargetVisibilityPolicyImpl());
        mEmbeddedWindowController = new EmbeddedWindowController(mAtmService, inputManager);

        mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources(
@@ -8345,6 +8343,38 @@ public class WindowManagerService extends IWindowManager.Stub
            }
        }

        @Override
        public boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId) {
            synchronized (mGlobalLock) {
                final WindowState imeTargetWindow = mWindowMap.get(imeTarget);
                if (imeTargetWindow == null) {
                    return false;
                }
                final DisplayContent dc = mRoot.getDisplayContent(displayId);
                if (dc == null) {
                    Slog.w(TAG, "Invalid displayId:" + displayId + ", fail to show IME screenshot");
                    return false;
                }

                dc.showImeScreenshot(imeTargetWindow);
                return true;
            }
        }

        @Override
        public boolean removeImeScreenshot(int displayId) {
            synchronized (mGlobalLock) {
                final DisplayContent dc = mRoot.getDisplayContent(displayId);
                if (dc == null) {
                    Slog.w(TAG, "Invalid displayId:" + displayId
                            + ", fail to remove IME screenshot");
                    return false;
                }
                dc.removeImeScreenshotImmediately();
                return true;
            }
        }

        @Override
        public boolean isHardKeyboardAvailable() {
            synchronized (mGlobalLock) {
@@ -9018,40 +9048,6 @@ public class WindowManagerService extends IWindowManager.Stub
        }
    }

    private final class ImeTargetVisibilityPolicyImpl extends ImeTargetVisibilityPolicy {

        @Override
        public boolean showImeScreenshot(@NonNull IBinder imeTarget, int displayId) {
            synchronized (mGlobalLock) {
                final WindowState imeTargetWindow = mWindowMap.get(imeTarget);
                if (imeTargetWindow == null) {
                    return false;
                }
                final DisplayContent dc = mRoot.getDisplayContent(displayId);
                if (dc == null) {
                    Slog.w(TAG, "Invalid displayId:" + displayId + ", fail to show IME screenshot");
                    return false;
                }

                dc.showImeScreenshot(imeTargetWindow);
                return true;
            }
        }
        @Override
        public boolean removeImeScreenshot(int displayId) {
            synchronized (mGlobalLock) {
                final DisplayContent dc = mRoot.getDisplayContent(displayId);
                if (dc == null) {
                    Slog.w(TAG, "Invalid displayId:" + displayId
                            + ", fail to remove IME screenshot");
                    return false;
                }
                dc.removeImeScreenshotImmediately();
                return true;
            }
        }
    }

    /** Called to inform window manager if non-Vr UI shoul be disabled or not. */
    public void disableNonVrUi(boolean disable) {
        synchronized (mGlobalLock) {
Loading