• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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