• 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 android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
20 import static android.view.InsetsSource.ID_IME;
21 
22 import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_IME;
23 import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL;
24 import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
25 import static com.android.server.wm.ImeInsetsSourceProviderProto.IME_TARGET_FROM_IME_IDENTIFIER;
26 import static com.android.server.wm.ImeInsetsSourceProviderProto.INSETS_SOURCE_PROVIDER;
27 import static com.android.server.wm.WindowManagerService.H.UPDATE_MULTI_WINDOW_STACKS;
28 
29 import android.annotation.NonNull;
30 import android.annotation.Nullable;
31 import android.graphics.Rect;
32 import android.os.Trace;
33 import android.util.Slog;
34 import android.util.proto.ProtoOutputStream;
35 import android.view.InsetsSource;
36 import android.view.InsetsSourceConsumer;
37 import android.view.InsetsSourceControl;
38 import android.view.WindowInsets;
39 import android.view.inputmethod.Flags;
40 import android.view.inputmethod.ImeTracker;
41 
42 import com.android.internal.annotations.VisibleForTesting;
43 import com.android.internal.inputmethod.SoftInputShowHideReason;
44 import com.android.internal.protolog.ProtoLog;
45 
46 import java.io.PrintWriter;
47 
48 /**
49  * Controller for IME inset source on the server. It's called provider as it provides the
50  * {@link InsetsSource} to the client that uses it in {@link InsetsSourceConsumer}.
51  */
52 final class ImeInsetsSourceProvider extends InsetsSourceProvider {
53 
54     private static final String TAG = ImeInsetsSourceProvider.class.getSimpleName();
55 
56     /** The token tracking the show IME request, non-null only while a show request is pending. */
57     @Nullable
58     private ImeTracker.Token mStatsToken;
59     /** The target that requested to show the IME, non-null only while a show request is pending. */
60     @Nullable
61     private InsetsControlTarget mImeRequester;
62     /** @see #isImeShowing() */
63     private boolean mImeShowing;
64     /** The latest received insets source. */
65     private final InsetsSource mLastSource = new InsetsSource(ID_IME, WindowInsets.Type.ime());
66 
67     /** @see #setFrozen(boolean) */
68     private boolean mFrozen;
69 
70     /**
71      * The server visibility of the source provider's window container. This is out of sync with
72      * {@link InsetsSourceProvider#mServerVisible} while {@link #mFrozen} is {@code true}.
73      *
74      * @see #setServerVisible
75      */
76     private boolean mServerVisible;
77 
78     /**
79      * When the IME is not ready, it has givenInsetsPending. However, this could happen again,
80      * after it became serverVisible. This flag indicates is used to determine if it is
81      * readyForDispatching
82      */
83     private boolean mGivenInsetsReady = false;
84 
85     /**
86      * The last state of the windowContainer. This is used to reset server visibility, in case of
87      * the IME (temporarily) redrawing  (e.g. during a rotation), to dispatch the control with
88      * leash again after it has finished drawing.
89      */
90     private boolean mLastDrawn = false;
91 
ImeInsetsSourceProvider(@onNull InsetsSource source, @NonNull InsetsStateController stateController, @NonNull DisplayContent displayContent)92     ImeInsetsSourceProvider(@NonNull InsetsSource source,
93             @NonNull InsetsStateController stateController,
94             @NonNull DisplayContent displayContent) {
95         super(source, stateController, displayContent);
96     }
97 
98     @Override
onPostLayout()99     void onPostLayout() {
100         boolean wasServerVisible = mServerVisible;
101         super.onPostLayout();
102 
103         if (android.view.inputmethod.Flags.refactorInsetsController()) {
104             final WindowState ws =
105                     mWindowContainer != null ? mWindowContainer.asWindowState() : null;
106             final boolean givenInsetsPending = ws != null && ws.mGivenInsetsPending;
107             mLastDrawn = ws != null && ws.isDrawn();
108 
109             // isLeashReadyForDispatching (used to dispatch the leash of the control) is
110             // depending on mGivenInsetsReady. Therefore, triggering notifyControlChanged here
111             // again, so that the control with leash can be eventually dispatched
112             if (!mGivenInsetsReady && isServerVisible() && !givenInsetsPending
113                     && mControlTarget != null) {
114                 ProtoLog.d(WM_DEBUG_IME,
115                         "onPostLayout: IME control ready to be dispatched, controlTarget=%s",
116                         mControlTarget);
117                 mGivenInsetsReady = true;
118                 ImeTracker.forLogging().onProgress(mStatsToken,
119                         ImeTracker.PHASE_WM_POST_LAYOUT_NOTIFY_CONTROLS_CHANGED);
120                 mStateController.notifyControlChanged(mControlTarget, this);
121                 setImeShowing(true);
122             } else if (wasServerVisible && isServerVisible() && mGivenInsetsReady
123                     && givenInsetsPending) {
124                 // If the server visibility didn't change (still visible), and mGivenInsetsReady
125                 // is set, we won't call into notifyControlChanged. Therefore, we can reset the
126                 // statsToken, if available.
127                 ProtoLog.w(WM_DEBUG_IME, "onPostLayout cancel statsToken, controlTarget=%s",
128                         mControlTarget);
129                 ImeTracker.forLogging().onCancelled(mStatsToken,
130                         ImeTracker.PHASE_WM_POST_LAYOUT_NOTIFY_CONTROLS_CHANGED);
131                 mStatsToken = null;
132             } else if (wasServerVisible && !isServerVisible()) {
133                 ProtoLog.d(WM_DEBUG_IME,
134                         "onPostLayout: setImeShowing(false) was: %s, controlTarget=%s",
135                         isImeShowing(), mControlTarget);
136                 setImeShowing(false);
137             }
138         }
139     }
140 
141     @Nullable
getAndClearStatsToken()142     ImeTracker.Token getAndClearStatsToken() {
143         if (android.view.inputmethod.Flags.refactorInsetsController()) {
144             ImeTracker.Token statsToken = mStatsToken;
145             mStatsToken = null;
146             return statsToken;
147         } else {
148             return null;
149         }
150     }
151 
152     @Override
isLeashReadyForDispatching()153     protected boolean isLeashReadyForDispatching() {
154         if (android.view.inputmethod.Flags.refactorInsetsController()) {
155             // We should only dispatch the leash, if the following conditions are fulfilled:
156             // 1. parent isLeashReadyForDispatching, 2. mGivenInsetsReady (means there are no
157             // givenInsetsPending), 3. the IME surface is drawn, 4. either the IME is
158             // serverVisible (the unfrozen state)
159             final WindowState ws =
160                     mWindowContainer != null ? mWindowContainer.asWindowState() : null;
161             final boolean isDrawn = ws != null && ws.isDrawn();
162             return super.isLeashReadyForDispatching()
163                     && isServerVisible() && isDrawn && mGivenInsetsReady;
164         } else {
165             return super.isLeashReadyForDispatching();
166         }
167     }
168 
169     /**
170      * This is used to determine the desired serverVisibility state. For the IME, just having a
171      * window state that would be visible by policy is not enough.
172      */
173     @Override
isSurfaceVisible()174     protected boolean isSurfaceVisible() {
175         final boolean isSurfaceVisible = super.isSurfaceVisible();
176         if (android.view.inputmethod.Flags.refactorInsetsController()) {
177             final WindowState windowState = mWindowContainer.asWindowState();
178             if (mControl != null && windowState != null) {
179                 final boolean isDrawn = windowState.isDrawn();
180                 if (!isServerVisible() && isSurfaceVisible) {
181                     // In case the IME becomes visible, we need to check if it is already drawn and
182                     // does not have given insets pending. If it's not yet drawn, we do not set
183                     // server visibility
184                     return isDrawn && !windowState.mGivenInsetsPending;
185                 } else if (mLastDrawn && !isDrawn) {
186                     // If the IME was drawn before, but is not drawn anymore, we need to reset
187                     // server visibility, which will also reset {@link
188                     // ImeInsetsSourceProvider#mGivenInsetsReady}. Otherwise, the new control
189                     // with leash won't be dispatched after the surface has redrawn.
190                     return false;
191                 }
192             }
193         }
194         return isSurfaceVisible;
195     }
196 
197 
198     @Nullable
199     @Override
getControl(InsetsControlTarget target)200     InsetsSourceControl getControl(InsetsControlTarget target) {
201         final InsetsSourceControl control = super.getControl(target);
202         if (control != null && target != null && target.getWindow() != null) {
203             final WindowState targetWin = target.getWindow();
204             final Task task = targetWin.getTask();
205             // If the control target changes during the app transition with the task snapshot
206             // starting window and the IME snapshot is visible, in case not have duplicated IME
207             // showing animation during transitioning, use a flag to inform IME source control to
208             // skip showing animation once.
209             StartingData startingData = null;
210             if (task != null) {
211                 startingData = targetWin.mActivityRecord.mStartingData;
212                 if (startingData == null) {
213                     final WindowState startingWin = task.topStartingWindow();
214                     if (startingWin != null) {
215                         startingData = startingWin.mStartingData;
216                     }
217                 }
218             }
219             control.setSkipAnimationOnce(startingData != null && startingData.hasImeSurface());
220         }
221         if (android.view.inputmethod.Flags.refactorInsetsController()) {
222             if (control != null && control.getLeash() != null) {
223                 ImeTracker.Token statsToken = getAndClearStatsToken();
224                 if (statsToken == null) {
225                     ProtoLog.w(WM_DEBUG_IME,
226                             "IME getControl without statsToken (check previous request!). "
227                                     + "Start new request");
228                     // TODO(b/353463205) remove this later after fixing the race of two requests
229                     //  that cancel each other (cf. b/383466954#comment19).
230                     statsToken = ImeTracker.forLogging().onStart(ImeTracker.TYPE_SHOW,
231                             ImeTracker.ORIGIN_SERVER, SoftInputShowHideReason.CONTROLS_CHANGED,
232                             false /* fromUser */);
233                 }
234                 ImeTracker.forLogging().onProgress(statsToken,
235                         ImeTracker.PHASE_WM_GET_CONTROL_WITH_LEASH);
236                 control.setImeStatsToken(statsToken);
237             }
238         }
239         return control;
240     }
241 
242     @Override
setClientVisible(boolean clientVisible)243     void setClientVisible(boolean clientVisible) {
244         final boolean wasClientVisible = isClientVisible();
245         super.setClientVisible(clientVisible);
246         // The layer of ImePlaceholder needs to be updated on a higher z-order for
247         // non-activity window (For activity window, IME is already on top of it).
248         if (!wasClientVisible && isClientVisible()) {
249             final InsetsControlTarget imeControlTarget = getControlTarget();
250             if (imeControlTarget != null && imeControlTarget.getWindow() != null
251                     && imeControlTarget.getWindow().mActivityRecord == null) {
252                 mDisplayContent.assignWindowLayers(false /* setLayoutNeeded */);
253             }
254         }
255     }
256 
257     @Override
setServerVisible(boolean serverVisible)258     void setServerVisible(boolean serverVisible) {
259         if (mServerVisible != serverVisible) {
260             mServerVisible = serverVisible;
261             // reset the leash if the server visibility becomes hidden
262             if (android.view.inputmethod.Flags.refactorInsetsController()) {
263                 if (!serverVisible && !mFrozen) {
264                     mGivenInsetsReady = false;
265                     updateControlForTarget(mControlTarget, true /* force */, null /* statsToken */);
266                 }
267             }
268         }
269         if (!mFrozen) {
270             super.setServerVisible(serverVisible);
271         }
272     }
273 
274     /**
275      * Freeze IME insets source state when required.
276      *
277      * <p>When setting {@param frozen} as {@code true}, the IME insets provider will freeze the
278      * current IME insets state and pending the IME insets state update until setting
279      * {@param frozen} as {@code false}.</p>
280      */
setFrozen(boolean frozen)281     void setFrozen(boolean frozen) {
282         if (mFrozen == frozen) {
283             return;
284         }
285         mFrozen = frozen;
286         if (!frozen) {
287             // Unfreeze and process the pending IME insets states.
288             super.setServerVisible(mServerVisible);
289         }
290     }
291 
292     @Override
updateSourceFrame(Rect frame)293     void updateSourceFrame(Rect frame) {
294         super.updateSourceFrame(frame);
295         onSourceChanged();
296     }
297 
298     @Override
updateVisibility()299     protected void updateVisibility() {
300         boolean oldVisibility = mSource.isVisible();
301         super.updateVisibility();
302         if (Flags.refactorInsetsController()) {
303             if (mSource.isVisible() && !oldVisibility && mControlTarget != null) {
304                 reportImeDrawnForOrganizerIfNeeded(mControlTarget);
305             }
306         }
307         onSourceChanged();
308     }
309 
310     @Override
updateControlForTarget(@ullable InsetsControlTarget target, boolean force, @NonNull ImeTracker.Token statsToken)311     void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force,
312             @NonNull ImeTracker.Token statsToken) {
313         if (target != null && target.getWindow() != null) {
314             // ime control target could be a different window.
315             // Refer WindowState#getImeControlTarget().
316             target = target.getWindow().getImeControlTarget();
317         }
318         // TODO(b/353463205) make sure that the statsToken of all callers is non-null (currently
319         //  not the case)
320         super.updateControlForTarget(target, force, statsToken);
321         if (Flags.refactorInsetsController()) {
322             // TODO(b/353463205) investigate if we should fail the statsToken, or if it's only
323             //  temporary null.
324             if (target != null && target == mControlTarget) {
325                 // If insets target is not available (e.g. RemoteInsetsControlTarget), use current
326                 // IME input target to update IME request state. For example, switch from a task
327                 // with showing IME to a split-screen task without showing IME.
328                 InputTarget imeInputTarget = mDisplayContent.getImeInputTarget();
329                 if (imeInputTarget != target && imeInputTarget != null
330                         && imeInputTarget.isRequestedVisible(WindowInsets.Type.ime())
331                         != target.isRequestedVisible(WindowInsets.Type.ime())) {
332                     // Only update the controlTarget, if it has a different requested visibility
333                     // than the imeInputTarget. Otherwise, updateClientVisibility won't invoke
334                     // the listener, as nothing changed.
335                     reportImeInputTargetStateToControlTarget(imeInputTarget, target,
336                             statsToken);
337                 } else {
338                     invokeOnImeRequestedChangedListener(target, statsToken);
339                 }
340             } else {
341                 ProtoLog.w(WM_DEBUG_IME,
342                         "Not invoking onImeRequestedChangedListener, target=%s, current "
343                                 + "controlTarget=%s",
344                         target, mControlTarget);
345             }
346         }
347     }
348 
349     // TODO(b/353463205) change statsToken to be NonNull, after the flag is permanently enabled
350     @Override
updateClientVisibility(InsetsTarget caller, @Nullable ImeTracker.Token statsToken)351     protected boolean updateClientVisibility(InsetsTarget caller,
352             @Nullable ImeTracker.Token statsToken) {
353         InsetsControlTarget controlTarget = getControlTarget();
354         if (caller != controlTarget) {
355             if (Flags.refactorInsetsController()) {
356                 if (isImeInputTarget(caller)) {
357                     reportImeInputTargetStateToControlTarget(caller, controlTarget, statsToken);
358                 } else {
359                     ProtoLog.w(WM_DEBUG_IME,
360                             "Tried to update client visibility for non-IME input target %s "
361                                     + "(current target: %s, IME requested: %s)", caller,
362                             mDisplayContent.getImeInputTarget(),
363                             caller.isRequestedVisible(WindowInsets.Type.ime()));
364                     ImeTracker.forLogging().onFailed(statsToken,
365                             ImeTracker.PHASE_SERVER_UPDATE_CLIENT_VISIBILITY);
366                 }
367             }
368             return false;
369         }
370         boolean changed = super.updateClientVisibility(caller, statsToken);
371         if (!Flags.refactorInsetsController() && caller instanceof InsetsControlTarget) {
372             if (changed && caller.isRequestedVisible(mSource.getType())) {
373                 reportImeDrawnForOrganizerIfNeeded((InsetsControlTarget) caller);
374             }
375         }
376         if (Flags.refactorInsetsController()) {
377             if (changed) {
378                 ImeTracker.forLogging().onProgress(statsToken,
379                         ImeTracker.PHASE_SERVER_UPDATE_CLIENT_VISIBILITY);
380                 invokeOnImeRequestedChangedListener(controlTarget, statsToken);
381             } else {
382                 // TODO(b/353463205) check cancelled / failed
383                 ImeTracker.forLogging().onCancelled(statsToken,
384                         ImeTracker.PHASE_SERVER_UPDATE_CLIENT_VISIBILITY);
385             }
386         }
387         return changed;
388     }
389 
onInputTargetChanged(InputTarget target)390     void onInputTargetChanged(InputTarget target) {
391         if (Flags.refactorInsetsController() && target != null) {
392             InsetsControlTarget imeControlTarget = getControlTarget();
393             if (target != imeControlTarget) {
394                 // TODO(b/353463205): check if fromUser=false is correct here
395                 boolean imeVisible = target.isRequestedVisible(WindowInsets.Type.ime());
396                 ImeTracker.Token statsToken = ImeTracker.forLogging().onStart(
397                         imeVisible ? ImeTracker.TYPE_SHOW : ImeTracker.TYPE_HIDE,
398                         ImeTracker.ORIGIN_SERVER,
399                         imeVisible ? SoftInputShowHideReason.SHOW_INPUT_TARGET_CHANGED
400                                 : SoftInputShowHideReason.HIDE_INPUT_TARGET_CHANGED,
401                         false /* fromUser */);
402                 reportImeInputTargetStateToControlTarget(target, imeControlTarget,
403                         statsToken);
404             }
405         }
406     }
407 
reportImeInputTargetStateToControlTarget(@onNull InsetsTarget imeInsetsTarget, InsetsControlTarget controlTarget, @NonNull ImeTracker.Token statsToken)408     private void reportImeInputTargetStateToControlTarget(@NonNull InsetsTarget imeInsetsTarget,
409             InsetsControlTarget controlTarget, @NonNull ImeTracker.Token statsToken) {
410         // In case of the multi window mode, update the requestedVisibleTypes from
411         // the controlTarget (=RemoteInsetsControlTarget) via DisplayImeController.
412         // Then, trigger onRequestedVisibleTypesChanged for the controlTarget with
413         // its new requested visibility for the IME
414         boolean imeVisible = imeInsetsTarget.isRequestedVisible(WindowInsets.Type.ime());
415         if (controlTarget != null) {
416             ImeTracker.forLogging().onProgress(statsToken,
417                     ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
418             controlTarget.setImeInputTargetRequestedVisibility(imeVisible, statsToken);
419         } else if (imeInsetsTarget instanceof InsetsControlTarget) {
420             // In case of a virtual display that cannot show the IME, the
421             // controlTarget will be null here, as no controlTarget was set yet. In
422             // that case, proceed similar to the multi window mode (fallback =
423             // RemoteInsetsControlTarget of the default display)
424             controlTarget = mDisplayContent.getImeHostOrFallback(
425                     ((InsetsControlTarget) imeInsetsTarget).getWindow());
426 
427             if (controlTarget != null && controlTarget != imeInsetsTarget) {
428                 ImeTracker.forLogging().onProgress(statsToken,
429                         ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
430                 controlTarget.setImeInputTargetRequestedVisibility(imeVisible, statsToken);
431                 // not all virtual displays have an ImeInsetsSourceProvider, so it is not
432                 // guaranteed that the IME will be started when the control target reports its
433                 // requested visibility back. Thus, invoking the listener here.
434                 invokeOnImeRequestedChangedListener((InsetsControlTarget) imeInsetsTarget,
435                         statsToken);
436             } else {
437                 ImeTracker.forLogging().onFailed(statsToken,
438                         ImeTracker.PHASE_WM_SET_REMOTE_TARGET_IME_VISIBILITY);
439             }
440         }
441     }
442 
443     // TODO(b/353463205) check callers to see if we can make statsToken @NonNull
invokeOnImeRequestedChangedListener(InsetsControlTarget controlTarget, @Nullable ImeTracker.Token statsToken)444     private void invokeOnImeRequestedChangedListener(InsetsControlTarget controlTarget,
445             @Nullable ImeTracker.Token statsToken) {
446         final var imeListener = mDisplayContent.mWmService.mOnImeRequestedChangedListener;
447         if (imeListener != null) {
448             if (controlTarget != null) {
449                 final boolean imeAnimating = Flags.reportAnimatingInsetsTypes()
450                         && (controlTarget.getAnimatingTypes() & WindowInsets.Type.ime()) != 0;
451                 final boolean imeVisible =
452                         controlTarget.isRequestedVisible(WindowInsets.Type.ime()) || imeAnimating;
453                 final var finalStatsToken = statsToken != null ? statsToken
454                         : ImeTracker.forLogging().onStart(
455                                 imeVisible ? ImeTracker.TYPE_SHOW : ImeTracker.TYPE_HIDE,
456                                 ImeTracker.ORIGIN_SERVER,
457                                 SoftInputShowHideReason.IME_REQUESTED_CHANGED_LISTENER,
458                                 false /* fromUser */);
459                 ImeTracker.forLogging().onProgress(finalStatsToken,
460                         ImeTracker.PHASE_WM_POSTING_CHANGED_IME_VISIBILITY);
461                 mDisplayContent.mWmService.mH.post(() -> {
462                     ImeTracker.forLogging().onProgress(finalStatsToken,
463                             ImeTracker.PHASE_WM_INVOKING_IME_REQUESTED_LISTENER);
464                     imeListener.onImeRequestedChanged(controlTarget.getWindowToken(), imeVisible,
465                             finalStatsToken);
466                 });
467             } else {
468                 ImeTracker.forLogging().onFailed(statsToken,
469                         ImeTracker.PHASE_WM_POSTING_CHANGED_IME_VISIBILITY);
470             }
471         } else {
472             // TODO(b/353463205) We could combine the upper if's and remove the additional phase.
473             ImeTracker.forLogging().onFailed(statsToken,
474                     ImeTracker.PHASE_WM_DISPATCH_IME_REQUESTED_CHANGED);
475         }
476     }
477 
478     @Override
onAnimatingTypesChanged(InsetsControlTarget caller, @Nullable ImeTracker.Token statsToken)479     void onAnimatingTypesChanged(InsetsControlTarget caller,
480             @Nullable ImeTracker.Token statsToken) {
481         if (Flags.reportAnimatingInsetsTypes()) {
482             final InsetsControlTarget controlTarget = getControlTarget();
483             // If the IME is not being requested anymore and the animation is finished, we need to
484             // invoke the listener, to let IMS eventually know
485             if (caller != null && caller == controlTarget && !caller.isRequestedVisible(
486                     WindowInsets.Type.ime())
487                     && (caller.getAnimatingTypes() & WindowInsets.Type.ime()) == 0) {
488                 ImeTracker.forLogging().onFailed(statsToken,
489                         ImeTracker.PHASE_WM_NOTIFY_HIDE_ANIMATION_FINISHED);
490                 invokeOnImeRequestedChangedListener(caller, statsToken);
491             } else {
492                 ImeTracker.forLogging().onCancelled(statsToken,
493                         ImeTracker.PHASE_WM_NOTIFY_HIDE_ANIMATION_FINISHED);
494             }
495         }
496     }
497 
reportImeDrawnForOrganizerIfNeeded(@onNull InsetsControlTarget caller)498     private void reportImeDrawnForOrganizerIfNeeded(@NonNull InsetsControlTarget caller) {
499         final WindowState callerWindow = caller.getWindow();
500         if (callerWindow == null) {
501             return;
502         }
503         WindowToken imeToken = mWindowContainer.asWindowState() != null
504                 ? mWindowContainer.asWindowState().mToken : null;
505         final var rotationController = mDisplayContent.getAsyncRotationController();
506         if ((rotationController != null && rotationController.isTargetToken(imeToken)) || (
507                 imeToken != null && imeToken.isSelfAnimating(0 /* flags */,
508                         SurfaceAnimator.ANIMATION_TYPE_TOKEN_TRANSFORM))) {
509             // Skip reporting IME drawn state when the control target is in fixed
510             // rotation, AsyncRotationController will report after the animation finished.
511             return;
512         }
513         reportImeDrawnForOrganizer(caller);
514     }
515 
reportImeDrawnForOrganizer(@onNull InsetsControlTarget caller)516     private void reportImeDrawnForOrganizer(@NonNull InsetsControlTarget caller) {
517         final WindowState callerWindow = caller.getWindow();
518         if (callerWindow == null || callerWindow.getTask() == null) {
519             return;
520         }
521         if (callerWindow.getTask().isOrganized()) {
522             mWindowContainer.mWmService.mAtmService.mTaskOrganizerController
523                     .reportImeDrawnOnTask(caller.getWindow().getTask());
524         }
525     }
526 
527     /** Report the IME has drawn on the current IME control target for its task organizer */
reportImeDrawnForOrganizer()528     void reportImeDrawnForOrganizer() {
529         final InsetsControlTarget imeControlTarget = getControlTarget();
530         if (imeControlTarget != null) {
531             reportImeDrawnForOrganizer(imeControlTarget);
532         }
533     }
534 
onSourceChanged()535     private void onSourceChanged() {
536         if (mLastSource.equals(mSource)) {
537             return;
538         }
539         mLastSource.set(mSource);
540         mDisplayContent.mWmService.mH.obtainMessage(
541                 UPDATE_MULTI_WINDOW_STACKS, mDisplayContent).sendToTarget();
542     }
543 
544     /**
545      * Called from {@link WindowManagerInternal#showImePostLayout}
546      * when {@link android.inputmethodservice.InputMethodService} requests to show IME
547      * on the given control target.
548      *
549      * @param imeTarget  the control target on which the IME request is coming from.
550      * @param statsToken the token tracking the current IME request.
551      */
scheduleShowImePostLayout(@onNull InsetsControlTarget imeTarget, @NonNull ImeTracker.Token statsToken)552     void scheduleShowImePostLayout(@NonNull InsetsControlTarget imeTarget,
553             @NonNull ImeTracker.Token statsToken) {
554         if (mImeRequester == null) {
555             // Start tracing only on initial scheduled show IME request, to record end-to-end time.
556             Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0);
557         } else {
558             // We already have a scheduled show IME request, cancel the previous statsToken and
559             // continue with the new one.
560             logIsScheduledAndReadyToShowIme(false /* aborted */);
561             ImeTracker.forLogging().onCancelled(mStatsToken, ImeTracker.PHASE_WM_SHOW_IME_RUNNER);
562         }
563         final boolean targetChanged = isTargetChangedWithinActivity(imeTarget);
564         mImeRequester = imeTarget;
565         mStatsToken = statsToken;
566         if (targetChanged) {
567             // target changed, check if new target can show IME.
568             ProtoLog.d(WM_DEBUG_IME, "IME target changed within ActivityRecord");
569             checkAndStartShowImePostLayout();
570             // if IME cannot be shown at this time, it is scheduled to be shown.
571             // once window that called IMM.showSoftInput() and DisplayContent's ImeTarget match,
572             // it will be shown.
573             return;
574         }
575 
576         ProtoLog.d(WM_DEBUG_IME, "Schedule IME show for %s", mImeRequester.getWindow() == null
577                 ? mImeRequester : mImeRequester.getWindow().getName());
578         mDisplayContent.mWmService.requestTraversal();
579     }
580 
581     /**
582      * Checks whether there is a previously scheduled show IME request and we are ready to show,
583      * in which case also start handling the request.
584      */
checkAndStartShowImePostLayout()585     void checkAndStartShowImePostLayout() {
586         if (!isScheduledAndReadyToShowIme()) {
587             // This can later become ready, so we don't want to cancel the pending request here.
588             return;
589         }
590         // TODO(b/353463205) check if this is still triggered, as we don't go into STATE_SHOW_IME
591         //  (DefaultImeVisibilityApplier)
592         if (android.view.inputmethod.Flags.refactorInsetsController()) {
593             // The IME is drawn, so call into {@link WindowState#notifyInsetsControlChanged}
594             // if we have a leash
595             if (mControl != null && mControl.getLeash() != null
596                     && mControlTarget.getWindow() != null
597                     && !mControlTarget.getWindow().mGivenInsetsPending) {
598                 int displayId = mDisplayContent.getDisplayId();
599                 mControlTarget.notifyInsetsControlChanged(displayId);
600             }
601             return;
602         }
603 
604         ImeTracker.forLogging().onProgress(mStatsToken, ImeTracker.PHASE_WM_SHOW_IME_RUNNER);
605         ProtoLog.d(WM_DEBUG_IME, "Run showImeRunner");
606 
607         final InsetsControlTarget target = getControlTarget();
608 
609         ProtoLog.i(WM_DEBUG_IME, "call showInsets(ime) on %s",
610                 target.getWindow() != null ? target.getWindow().getName() : "");
611         setImeShowing(true);
612         target.showInsets(WindowInsets.Type.ime(), true /* fromIme */, mStatsToken);
613         Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0);
614         if (target != mImeRequester) {
615             ProtoLog.w(WM_DEBUG_IME, "showInsets(ime) was requested by different window: %s ",
616                     (mImeRequester.getWindow() != null ? mImeRequester.getWindow().getName() : ""));
617         }
618         resetShowImePostLayout();
619     }
620 
621     /** Aborts the previously scheduled show IME request. */
abortShowImePostLayout()622     void abortShowImePostLayout() {
623         if (mImeRequester == null) {
624             return;
625         }
626         ProtoLog.d(WM_DEBUG_IME, "abortShowImePostLayout");
627         Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, "WMS.showImePostLayout", 0);
628         logIsScheduledAndReadyToShowIme(true /* aborted */);
629         ImeTracker.forLogging().onFailed(
630                 mStatsToken, ImeTracker.PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT);
631         resetShowImePostLayout();
632     }
633 
634     /** Resets the state of the previously scheduled show IME request. */
resetShowImePostLayout()635     private void resetShowImePostLayout() {
636         mImeRequester = null;
637         mStatsToken = null;
638     }
639 
640     /** Checks whether there is a previously scheduled show IME request and we are ready to show. */
641     @VisibleForTesting
isScheduledAndReadyToShowIme()642     boolean isScheduledAndReadyToShowIme() {
643         // IMMS#mLastImeTargetWindow always considers focused window as
644         // IME target, however DisplayContent#computeImeTarget() can compute
645         // a different IME target.
646         // Refer to WindowManagerService#applyImeVisibility(token, false).
647         // If IMMS's imeTarget is child of DisplayContent's imeTarget and child window
648         // is above the parent, we will consider it as the same target for now.
649         // Also, if imeTarget is closing, it would be considered as outdated target.
650         // TODO(b/139861270): Remove the child & sublayer check once IMMS is aware of
651         //  actual IME target.
652         if (mImeRequester == null) {
653             // No show IME request previously scheduled.
654             return false;
655         }
656         if (!mServerVisible || mFrozen) {
657             // The window container is not available and considered visible.
658             // If frozen, the server visibility is not set until unfrozen.
659             return false;
660         }
661         if (mWindowContainer == null) {
662             // No window container set.
663             return false;
664         }
665         final WindowState windowState = mWindowContainer.asWindowState();
666         if (windowState == null) {
667             throw new IllegalArgumentException("IME insets must be provided by a window.");
668         }
669         if (!windowState.isDrawn() || windowState.mGivenInsetsPending) {
670             // The window is not drawn, or it has pending insets.
671             return false;
672         }
673         final InsetsControlTarget dcTarget = mDisplayContent.getImeTarget(IME_TARGET_LAYERING);
674         if (dcTarget == null) {
675             // No IME layering target.
676             return false;
677         }
678         final InsetsControlTarget controlTarget = getControlTarget();
679         if (controlTarget == null) {
680             // No IME control target.
681             return false;
682         }
683         if (controlTarget != mDisplayContent.getImeTarget(IME_TARGET_CONTROL)) {
684             // The control target does not match the one in DisplayContent.
685             return false;
686         }
687         if (mStateController.hasPendingControls(controlTarget)) {
688             // The control target has pending controls.
689             return false;
690         }
691         if (getLeash(controlTarget) == null) {
692             // The control target has no source control leash (or it is not ready for dispatching).
693             return false;
694         }
695 
696         ProtoLog.d(WM_DEBUG_IME, "dcTarget: %s mImeRequester: %s",
697                 dcTarget.getWindow().getName(), mImeRequester.getWindow() == null
698                         ? mImeRequester : mImeRequester.getWindow().getName());
699 
700         return isImeLayeringTarget(mImeRequester, dcTarget)
701                 || isAboveImeLayeringTarget(mImeRequester, dcTarget)
702                 || isImeFallbackTarget(mImeRequester)
703                 || isImeInputTarget(mImeRequester)
704                 || sameAsImeControlTarget(mImeRequester);
705     }
706 
707     /**
708      * Sets the statsToken before the IMS was shown/hidden.
709      * @param visible {@code true} to make it visible, false to hide it.
710      * @param statsToken the token tracking the current IME request.
711      */
receiveImeStatsToken(boolean visible, @NonNull ImeTracker.Token statsToken)712     void receiveImeStatsToken(boolean visible,
713             @NonNull ImeTracker.Token statsToken) {
714         if (!android.view.inputmethod.Flags.refactorInsetsController()) {
715             return;
716         }
717 
718         if (mStatsToken != null) {
719             // We have an ongoing show request will be cancelled by the newly received show
720             // request (cancelling the initial show) or hide request (aborting the initial show).
721             logIsScheduledAndReadyToShowIme(!visible /* aborted */);
722         }
723         ProtoLog.d(WM_DEBUG_IME, "receiveImeStatsToken: visible=%s", visible);
724         if (visible) {
725             ImeTracker.forLogging().onCancelled(
726                     mStatsToken, ImeTracker.PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT);
727             mStatsToken = statsToken;
728         } else {
729             ImeTracker.forLogging().onFailed(
730                     mStatsToken, ImeTracker.PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT);
731             mStatsToken = null;
732         }
733     }
734 
735     /**
736      * Logs the current state that can be checked by {@link #isScheduledAndReadyToShowIme}.
737      *
738      * @param aborted whether the scheduled show IME request was aborted or cancelled.
739      */
logIsScheduledAndReadyToShowIme(boolean aborted)740     private void logIsScheduledAndReadyToShowIme(boolean aborted) {
741         final var windowState = mWindowContainer != null ? mWindowContainer.asWindowState() : null;
742         final var dcTarget = mDisplayContent.getImeTarget(IME_TARGET_LAYERING);
743         final var controlTarget = getControlTarget();
744         final var sb = new StringBuilder();
745         sb.append("showImePostLayout ").append(aborted ? "aborted" : "cancelled");
746         sb.append(", isScheduledAndReadyToShowIme: ").append(isScheduledAndReadyToShowIme());
747         sb.append(", mImeRequester: ").append(mImeRequester);
748         sb.append(", serverVisible: ").append(mServerVisible);
749         sb.append(", frozen: ").append(mFrozen);
750         sb.append(", mWindowContainer is: ").append(mWindowContainer != null ? "non-null" : "null");
751         sb.append(", windowState: ").append(windowState);
752         if (windowState != null) {
753             sb.append(", isDrawn: ").append(windowState.isDrawn());
754             sb.append(", mGivenInsetsPending: ").append(windowState.mGivenInsetsPending);
755         }
756         sb.append(", dcTarget: ").append(dcTarget);
757         sb.append(", controlTarget: ").append(controlTarget);
758         if (mImeRequester != null && dcTarget != null && controlTarget != null) {
759             sb.append("\n");
760             sb.append("controlTarget == DisplayContent.controlTarget: ");
761             sb.append(controlTarget == mDisplayContent.getImeTarget(IME_TARGET_CONTROL));
762             sb.append(", hasPendingControls: ");
763             sb.append(mStateController.hasPendingControls(controlTarget));
764             final boolean hasLeash = getLeash(controlTarget) != null;
765             sb.append(", leash is: ").append(hasLeash ? "non-null" : "null");
766             if (!hasLeash) {
767                 sb.append(", control is: ").append(mControl != null ? "non-null" : "null");
768                 sb.append(", mIsLeashInitialized: ").append(mIsLeashInitialized);
769             }
770             sb.append(", isImeLayeringTarget: ");
771             sb.append(isImeLayeringTarget(mImeRequester, dcTarget));
772             sb.append(", isAboveImeLayeringTarget: ");
773             sb.append(isAboveImeLayeringTarget(mImeRequester, dcTarget));
774             sb.append(", isImeFallbackTarget: ");
775             sb.append(isImeFallbackTarget(mImeRequester));
776             sb.append(", isImeInputTarget: ");
777             sb.append(isImeInputTarget(mImeRequester));
778             sb.append(", sameAsImeControlTarget: ");
779             sb.append(sameAsImeControlTarget(mImeRequester));
780         }
781         Slog.d(TAG, sb.toString());
782     }
783 
784     // ---------------------------------------------------------------------------------------
785     // Methods for checking IME insets target changing state.
786     //
isImeLayeringTarget(@onNull InsetsControlTarget target, @NonNull InsetsControlTarget dcTarget)787     private static boolean isImeLayeringTarget(@NonNull InsetsControlTarget target,
788             @NonNull InsetsControlTarget dcTarget) {
789         return !isImeTargetWindowClosing(dcTarget.getWindow()) && target == dcTarget;
790     }
791 
isAboveImeLayeringTarget(@onNull InsetsControlTarget target, @NonNull InsetsControlTarget dcTarget)792     private static boolean isAboveImeLayeringTarget(@NonNull InsetsControlTarget target,
793             @NonNull InsetsControlTarget dcTarget) {
794         return target.getWindow() != null
795                 && dcTarget.getWindow().getParentWindow() == target
796                 && dcTarget.getWindow().mSubLayer > target.getWindow().mSubLayer;
797     }
798 
isImeFallbackTarget(@onNull InsetsControlTarget target)799     private boolean isImeFallbackTarget(@NonNull InsetsControlTarget target) {
800         return target == mDisplayContent.getImeFallback();
801     }
802 
isImeInputTarget(@onNull InsetsTarget target)803     private boolean isImeInputTarget(@NonNull InsetsTarget target) {
804         return target == mDisplayContent.getImeInputTarget();
805     }
806 
sameAsImeControlTarget(@onNull InsetsControlTarget target)807     private boolean sameAsImeControlTarget(@NonNull InsetsControlTarget target) {
808         final InsetsControlTarget controlTarget = getControlTarget();
809         return controlTarget == target
810                 && (target.getWindow() == null || !isImeTargetWindowClosing(target.getWindow()));
811     }
812 
isImeTargetWindowClosing(@onNull WindowState win)813     private static boolean isImeTargetWindowClosing(@NonNull WindowState win) {
814         return win.mAnimatingExit || win.mActivityRecord != null
815                 && (win.mActivityRecord.isInTransition()
816                     && !win.mActivityRecord.isVisibleRequested()
817                     || win.mActivityRecord.willCloseOrEnterPip());
818     }
819 
isTargetChangedWithinActivity(@onNull InsetsControlTarget target)820     private boolean isTargetChangedWithinActivity(@NonNull InsetsControlTarget target) {
821         // We don't consider the target out of the activity.
822         if (target.getWindow() == null) {
823             return false;
824         }
825         return mImeRequester != target
826                 && mImeRequester != null
827                 && mImeRequester.getWindow() != null
828                 && mImeRequester.getWindow().mActivityRecord == target.getWindow().mActivityRecord;
829     }
830     // ---------------------------------------------------------------------------------------
831 
832     @Override
dump(PrintWriter pw, String prefix)833     public void dump(PrintWriter pw, String prefix) {
834         super.dump(pw, prefix);
835         prefix = prefix + "  ";
836         pw.print(prefix);
837         pw.print("mImeShowing=");
838         pw.print(mImeShowing);
839         pw.print(" mLastDrawn=");
840         pw.print(mLastDrawn);
841         if (mImeRequester != null) {
842             pw.print(prefix);
843             pw.print("showImePostLayout pending for mImeRequester=");
844             pw.print(mImeRequester);
845             pw.println();
846         } else {
847             pw.print(prefix);
848             pw.print("showImePostLayout not scheduled, mImeRequester=null");
849             pw.println();
850         }
851         pw.println();
852     }
853 
854     @Override
dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTracingLogLevel int logLevel)855     void dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTracingLogLevel int logLevel) {
856         final long token = proto.start(fieldId);
857         super.dumpDebug(proto, INSETS_SOURCE_PROVIDER, logLevel);
858         final WindowState imeRequesterWindow =
859                 mImeRequester != null ? mImeRequester.getWindow() : null;
860         if (imeRequesterWindow != null) {
861             imeRequesterWindow.writeIdentifierToProto(proto, IME_TARGET_FROM_IME_IDENTIFIER);
862         }
863         proto.end(token);
864     }
865 
866     /**
867      * Sets whether the IME is currently supposed to be showing according to
868      * InputMethodManagerService.
869      */
setImeShowing(boolean imeShowing)870     public void setImeShowing(boolean imeShowing) {
871         mImeShowing = imeShowing;
872     }
873 
874     /**
875      * Returns whether the IME is currently supposed to be showing according to
876      * InputMethodManagerService.
877      */
isImeShowing()878     public boolean isImeShowing() {
879         return mImeShowing;
880     }
881 }
882