1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wm; 18 19 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_IME; 20 21 import android.view.InsetsSource; 22 import android.view.WindowInsets; 23 24 import com.android.internal.annotations.VisibleForTesting; 25 import com.android.server.protolog.common.ProtoLog; 26 27 import java.io.PrintWriter; 28 29 /** 30 * Controller for IME inset source on the server. It's called provider as it provides the 31 * {@link InsetsSource} to the client that uses it in {@link InsetsSourceConsumer}. 32 */ 33 class ImeInsetsSourceProvider extends InsetsSourceProvider { 34 35 private InsetsControlTarget mImeTargetFromIme; 36 private Runnable mShowImeRunner; 37 private boolean mIsImeLayoutDrawn; 38 private boolean mImeShowing; 39 ImeInsetsSourceProvider(InsetsSource source, InsetsStateController stateController, DisplayContent displayContent)40 ImeInsetsSourceProvider(InsetsSource source, 41 InsetsStateController stateController, DisplayContent displayContent) { 42 super(source, stateController, displayContent); 43 } 44 45 /** 46 * Called from {@link WindowManagerInternal#showImePostLayout} when {@link InputMethodService} 47 * requests to show IME on {@param imeTarget}. 48 * 49 * @param imeTarget imeTarget on which IME request is coming from. 50 */ scheduleShowImePostLayout(InsetsControlTarget imeTarget)51 void scheduleShowImePostLayout(InsetsControlTarget imeTarget) { 52 boolean targetChanged = mImeTargetFromIme != imeTarget 53 && mImeTargetFromIme != null && imeTarget != null && mShowImeRunner != null 54 && imeTarget.getWindow() != null && mImeTargetFromIme.getWindow() != null 55 && mImeTargetFromIme.getWindow().mActivityRecord 56 == imeTarget.getWindow().mActivityRecord; 57 mImeTargetFromIme = imeTarget; 58 if (targetChanged) { 59 // target changed, check if new target can show IME. 60 ProtoLog.d(WM_DEBUG_IME, "IME target changed within ActivityRecord"); 61 checkShowImePostLayout(); 62 // if IME cannot be shown at this time, it is scheduled to be shown. 63 // once window that called IMM.showSoftInput() and DisplayContent's ImeTarget match, 64 // it will be shown. 65 return; 66 } 67 68 ProtoLog.d(WM_DEBUG_IME, "Schedule IME show for %s", mImeTargetFromIme.getWindow() == null 69 ? mImeTargetFromIme : mImeTargetFromIme.getWindow().getName()); 70 mShowImeRunner = () -> { 71 ProtoLog.d(WM_DEBUG_IME, "Run showImeRunner"); 72 // Target should still be the same. 73 if (isImeTargetFromDisplayContentAndImeSame()) { 74 final InsetsControlTarget target = mDisplayContent.mInputMethodControlTarget; 75 76 ProtoLog.i(WM_DEBUG_IME, "call showInsets(ime) on %s", 77 target.getWindow() != null ? target.getWindow().getName() : ""); 78 setImeShowing(true); 79 target.showInsets(WindowInsets.Type.ime(), true /* fromIme */); 80 if (target != mImeTargetFromIme && mImeTargetFromIme != null) { 81 ProtoLog.w(WM_DEBUG_IME, 82 "showInsets(ime) was requested by different window: %s ", 83 (mImeTargetFromIme.getWindow() != null 84 ? mImeTargetFromIme.getWindow().getName() : "")); 85 } 86 } 87 abortShowImePostLayout(); 88 }; 89 mDisplayContent.mWmService.requestTraversal(); 90 } 91 checkShowImePostLayout()92 void checkShowImePostLayout() { 93 // check if IME is drawn 94 if (mIsImeLayoutDrawn 95 || (mImeTargetFromIme != null 96 && isImeTargetFromDisplayContentAndImeSame() 97 && mWin != null 98 && mWin.isDrawnLw() 99 && !mWin.mGivenInsetsPending)) { 100 mIsImeLayoutDrawn = true; 101 // show IME if InputMethodService requested it to be shown. 102 if (mShowImeRunner != null) { 103 mShowImeRunner.run(); 104 } 105 } 106 } 107 108 /** 109 * Abort any pending request to show IME post layout. 110 */ abortShowImePostLayout()111 void abortShowImePostLayout() { 112 ProtoLog.d(WM_DEBUG_IME, "abortShowImePostLayout"); 113 mImeTargetFromIme = null; 114 mIsImeLayoutDrawn = false; 115 mShowImeRunner = null; 116 } 117 118 @VisibleForTesting isImeTargetFromDisplayContentAndImeSame()119 boolean isImeTargetFromDisplayContentAndImeSame() { 120 // IMMS#mLastImeTargetWindow always considers focused window as 121 // IME target, however DisplayContent#computeImeTarget() can compute 122 // a different IME target. 123 // Refer to WindowManagerService#applyImeVisibility(token, false). 124 // If IMMS's imeTarget is child of DisplayContent's imeTarget and child window 125 // is above the parent, we will consider it as the same target for now. 126 // Also, if imeTarget is closing, it would be considered as outdated target. 127 // TODO(b/139861270): Remove the child & sublayer check once IMMS is aware of 128 // actual IME target. 129 final WindowState dcTarget = mDisplayContent.mInputMethodTarget; 130 final InsetsControlTarget controlTarget = mDisplayContent.mInputMethodControlTarget; 131 if (dcTarget == null || mImeTargetFromIme == null) { 132 return false; 133 } 134 ProtoLog.d(WM_DEBUG_IME, "dcTarget: %s mImeTargetFromIme: %s", 135 dcTarget.getName(), mImeTargetFromIme.getWindow() == null 136 ? mImeTargetFromIme : mImeTargetFromIme.getWindow().getName()); 137 138 return (!dcTarget.isClosing() && mImeTargetFromIme == dcTarget) 139 || (mImeTargetFromIme != null && mImeTargetFromIme.getWindow() != null 140 && dcTarget.getParentWindow() == mImeTargetFromIme 141 && dcTarget.mSubLayer > mImeTargetFromIme.getWindow().mSubLayer) 142 || mImeTargetFromIme == mDisplayContent.getImeFallback() 143 || mImeTargetFromIme == mDisplayContent.mInputMethodInputTarget 144 || controlTarget == mImeTargetFromIme 145 && (mImeTargetFromIme.getWindow() == null 146 || !mImeTargetFromIme.getWindow().isClosing()); 147 } 148 149 @Override dump(PrintWriter pw, String prefix)150 public void dump(PrintWriter pw, String prefix) { 151 super.dump(pw, prefix); 152 pw.print(prefix); 153 pw.print("mImeShowing="); 154 pw.print(mImeShowing); 155 if (mImeTargetFromIme != null) { 156 pw.print(" showImePostLayout pending for mImeTargetFromIme="); 157 pw.print(mImeTargetFromIme); 158 } 159 pw.println(); 160 } 161 162 /** 163 * Sets whether the IME is currently supposed to be showing according to 164 * InputMethodManagerService. 165 */ setImeShowing(boolean imeShowing)166 public void setImeShowing(boolean imeShowing) { 167 mImeShowing = imeShowing; 168 } 169 170 /** 171 * Returns whether the IME is currently supposed to be showing according to 172 * InputMethodManagerService. 173 */ isImeShowing()174 public boolean isImeShowing() { 175 return mImeShowing; 176 } 177 } 178