1 /*
2  * Copyright (C) 2018 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.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_INSETS;
20 import static com.android.server.wm.InsetsSourceProviderProto.CAPTURED_LEASH;
21 import static com.android.server.wm.InsetsSourceProviderProto.CLIENT_VISIBLE;
22 import static com.android.server.wm.InsetsSourceProviderProto.CONTROL;
23 import static com.android.server.wm.InsetsSourceProviderProto.CONTROLLABLE;
24 import static com.android.server.wm.InsetsSourceProviderProto.CONTROL_TARGET;
25 import static com.android.server.wm.InsetsSourceProviderProto.FAKE_CONTROL;
26 import static com.android.server.wm.InsetsSourceProviderProto.FAKE_CONTROL_TARGET;
27 import static com.android.server.wm.InsetsSourceProviderProto.FRAME;
28 import static com.android.server.wm.InsetsSourceProviderProto.IS_LEASH_READY_FOR_DISPATCHING;
29 import static com.android.server.wm.InsetsSourceProviderProto.PENDING_CONTROL_TARGET;
30 import static com.android.server.wm.InsetsSourceProviderProto.SEAMLESS_ROTATING;
31 import static com.android.server.wm.InsetsSourceProviderProto.SERVER_VISIBLE;
32 import static com.android.server.wm.InsetsSourceProviderProto.SOURCE;
33 import static com.android.server.wm.InsetsSourceProviderProto.SOURCE_WINDOW_STATE;
34 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_INSETS_CONTROL;
35 
36 import android.annotation.NonNull;
37 import android.annotation.Nullable;
38 import android.graphics.Insets;
39 import android.graphics.Point;
40 import android.graphics.Rect;
41 import android.util.SparseArray;
42 import android.util.proto.ProtoOutputStream;
43 import android.view.InsetsSource;
44 import android.view.InsetsSource.Flags;
45 import android.view.InsetsSourceControl;
46 import android.view.SurfaceControl;
47 import android.view.SurfaceControl.Transaction;
48 import android.view.WindowInsets;
49 
50 import com.android.internal.annotations.VisibleForTesting;
51 import com.android.internal.protolog.common.ProtoLog;
52 import com.android.internal.util.function.TriFunction;
53 import com.android.server.wm.SurfaceAnimator.AnimationType;
54 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
55 
56 import java.io.PrintWriter;
57 import java.util.function.Consumer;
58 
59 /**
60  * Controller for a specific inset source on the server. It's called provider as it provides the
61  * {@link InsetsSource} to the client that uses it in {@link android.view.InsetsSourceConsumer}.
62  */
63 class InsetsSourceProvider {
64 
65     private static final Rect EMPTY_RECT = new Rect();
66 
67     protected final @NonNull InsetsSource mSource;
68     protected final @NonNull DisplayContent mDisplayContent;
69     protected final @NonNull InsetsStateController mStateController;
70     protected @Nullable WindowContainer mWindowContainer;
71     protected @Nullable InsetsSourceControl mControl;
72     protected @Nullable InsetsControlTarget mControlTarget;
73     protected boolean mIsLeashReadyForDispatching;
74 
75     private final Rect mTmpRect = new Rect();
76     private final InsetsSourceControl mFakeControl;
77     private final Consumer<Transaction> mSetLeashPositionConsumer;
78     private @Nullable InsetsControlTarget mPendingControlTarget;
79     private @Nullable InsetsControlTarget mFakeControlTarget;
80 
81     private @Nullable ControlAdapter mAdapter;
82     private TriFunction<DisplayFrames, WindowContainer, Rect, Integer> mFrameProvider;
83     private SparseArray<TriFunction<DisplayFrames, WindowContainer, Rect, Integer>>
84             mOverrideFrameProviders;
85     private final SparseArray<Rect> mOverrideFrames = new SparseArray<Rect>();
86     private final Rect mSourceFrame = new Rect();
87     private final Rect mLastSourceFrame = new Rect();
88     private @NonNull Insets mInsetsHint = Insets.NONE;
89     private boolean mInsetsHintStale = true;
90     private @Flags int mFlagsFromFrameProvider;
91     private @Flags int mFlagsFromServer;
92     private boolean mHasPendingPosition;
93 
94     /** The visibility override from the current controlling window. */
95     private boolean mClientVisible;
96 
97     /**
98      * Whether the window container is available and considered visible as in
99      * {@link WindowContainer#isVisible}.
100      */
101     private boolean mServerVisible;
102 
103     private boolean mSeamlessRotating;
104 
105     private final boolean mControllable;
106 
107     /**
108      * Whether to forced the dimensions of the source window container to the inset frame and crop
109      * out any overflow.
110      * Used to crop the taskbar inset source when a task animation is occurring to hide the taskbar
111      * rounded corners overlays.
112      *
113      * TODO: Remove when we enable shell transitions (b/202383002)
114      */
115     private boolean mCropToProvidingInsets = false;
116 
InsetsSourceProvider(@onNull InsetsSource source, @NonNull InsetsStateController stateController, @NonNull DisplayContent displayContent)117     InsetsSourceProvider(@NonNull InsetsSource source,
118             @NonNull InsetsStateController stateController,
119             @NonNull DisplayContent displayContent) {
120         mClientVisible = (WindowInsets.Type.defaultVisible() & source.getType()) != 0;
121         mSource = source;
122         mDisplayContent = displayContent;
123         mStateController = stateController;
124         mFakeControl = new InsetsSourceControl(
125                 source.getId(), source.getType(), null /* leash */, false /* initialVisible */,
126                 new Point(), Insets.NONE);
127         mControllable = (InsetsPolicy.CONTROLLABLE_TYPES & source.getType()) != 0;
128         mSetLeashPositionConsumer = t -> {
129             if (mControl != null) {
130                 final SurfaceControl leash = mControl.getLeash();
131                 if (leash != null) {
132                     final Point position = mControl.getSurfacePosition();
133                     t.setPosition(leash, position.x, position.y);
134                 }
135             }
136             if (mHasPendingPosition) {
137                 mHasPendingPosition = false;
138                 if (mPendingControlTarget != mControlTarget) {
139                     mStateController.notifyControlTargetChanged(mPendingControlTarget, this);
140                 }
141             }
142         };
143     }
144 
getSource()145     InsetsSource getSource() {
146         return mSource;
147     }
148 
149     /**
150      * @return Whether the current flag configuration allows to control this source.
151      */
isControllable()152     boolean isControllable() {
153         return mControllable;
154     }
155 
156     /**
157      * Updates the window container that currently backs this source.
158      *
159      * @param windowContainer The window container that links to this source.
160      * @param frameProvider Based on display frame state and the window, calculates the resulting
161      *                      frame that should be reported to clients.
162      *                      This will only be used when the window container providing the insets is
163      *                      not a WindowState.
164      * @param overrideFrameProviders Based on display frame state and the window, calculates the
165      *                               resulting frame that should be reported to given window type.
166      */
setWindowContainer(@ullable WindowContainer windowContainer, @Nullable TriFunction<DisplayFrames, WindowContainer, Rect, Integer> frameProvider, @Nullable SparseArray<TriFunction<DisplayFrames, WindowContainer, Rect, Integer>> overrideFrameProviders)167     void setWindowContainer(@Nullable WindowContainer windowContainer,
168             @Nullable TriFunction<DisplayFrames, WindowContainer, Rect, Integer> frameProvider,
169             @Nullable SparseArray<TriFunction<DisplayFrames, WindowContainer, Rect, Integer>>
170                     overrideFrameProviders) {
171         if (mWindowContainer != null) {
172             if (mControllable) {
173                 mWindowContainer.setControllableInsetProvider(null);
174             }
175             // The window container may be animating such that we can hand out the leash to the
176             // control target. Revoke the leash by cancelling the animation to correct the state.
177             // TODO: Ideally, we should wait for the animation to finish so previous window can
178             // animate-out as new one animates-in.
179             mWindowContainer.cancelAnimation();
180             mWindowContainer.getInsetsSourceProviders().remove(mSource.getId());
181             mSeamlessRotating = false;
182             mHasPendingPosition = false;
183         }
184         ProtoLog.d(WM_DEBUG_WINDOW_INSETS, "InsetsSource setWin %s for type %s",
185                 windowContainer, WindowInsets.Type.toString(mSource.getType()));
186         mWindowContainer = windowContainer;
187         // TODO: remove the frame provider for non-WindowState container.
188         mFrameProvider = frameProvider;
189         mOverrideFrames.clear();
190         mOverrideFrameProviders = overrideFrameProviders;
191         if (windowContainer == null) {
192             setServerVisible(false);
193             mSource.setVisibleFrame(null);
194             mSource.setFlags(0, 0xffffffff);
195             mSourceFrame.setEmpty();
196         } else {
197             mWindowContainer.getInsetsSourceProviders().put(mSource.getId(), this);
198             if (mControllable) {
199                 mWindowContainer.setControllableInsetProvider(this);
200                 if (mPendingControlTarget != mControlTarget) {
201                     mStateController.notifyControlTargetChanged(mPendingControlTarget, this);
202                 }
203             }
204         }
205     }
206 
setFlags(@lags int flags, @Flags int mask)207     boolean setFlags(@Flags int flags, @Flags int mask) {
208         mFlagsFromServer = (mFlagsFromServer & ~mask) | (flags & mask);
209         final @Flags int mergedFlags = mFlagsFromFrameProvider | mFlagsFromServer;
210         if (mSource.getFlags() != mergedFlags) {
211             mSource.setFlags(mergedFlags);
212             return true;
213         }
214         return false;
215     }
216 
217     /**
218      * The source frame can affect the layout of other windows, so this should be called once the
219      * window container gets laid out.
220      */
updateSourceFrame(Rect frame)221     void updateSourceFrame(Rect frame) {
222         if (mWindowContainer == null) {
223             return;
224         }
225         WindowState win = mWindowContainer.asWindowState();
226 
227         if (win == null) {
228             // For all the non window WindowContainers.
229             if (mServerVisible) {
230                 mTmpRect.set(mWindowContainer.getBounds());
231                 if (mFrameProvider != null) {
232                     mFrameProvider.apply(mWindowContainer.getDisplayContent().mDisplayFrames,
233                             mWindowContainer, mTmpRect);
234                 }
235             } else {
236                 mTmpRect.setEmpty();
237             }
238             mSource.setFrame(mTmpRect);
239             mSource.setVisibleFrame(null);
240             return;
241         }
242 
243         mSourceFrame.set(frame);
244         if (mFrameProvider != null) {
245             mFlagsFromFrameProvider = mFrameProvider.apply(
246                     mWindowContainer.getDisplayContent().mDisplayFrames,
247                     mWindowContainer,
248                     mSourceFrame);
249             mSource.setFlags(mFlagsFromFrameProvider | mFlagsFromServer);
250         }
251         updateSourceFrameForServerVisibility();
252         if (!mLastSourceFrame.equals(mSourceFrame)) {
253             mLastSourceFrame.set(mSourceFrame);
254             mInsetsHintStale = true;
255         }
256 
257         if (mOverrideFrameProviders != null) {
258             // Not necessary to clear the mOverrideFrames here. It will be cleared every time the
259             // override frame provider updates.
260             for (int i = mOverrideFrameProviders.size() - 1; i >= 0; i--) {
261                 final int windowType = mOverrideFrameProviders.keyAt(i);
262                 final Rect overrideFrame;
263                 if (mOverrideFrames.contains(windowType)) {
264                     overrideFrame = mOverrideFrames.get(windowType);
265                     overrideFrame.set(frame);
266                 } else {
267                     overrideFrame = new Rect(frame);
268                 }
269                 final TriFunction<DisplayFrames, WindowContainer, Rect, Integer> provider =
270                         mOverrideFrameProviders.get(windowType);
271                 if (provider != null) {
272                     mOverrideFrameProviders.get(windowType).apply(
273                             mWindowContainer.getDisplayContent().mDisplayFrames, mWindowContainer,
274                             overrideFrame);
275                 }
276                 mOverrideFrames.put(windowType, overrideFrame);
277             }
278         }
279 
280         if (win.mGivenVisibleInsets.left != 0 || win.mGivenVisibleInsets.top != 0
281                 || win.mGivenVisibleInsets.right != 0
282                 || win.mGivenVisibleInsets.bottom != 0) {
283             mTmpRect.set(frame);
284             mTmpRect.inset(win.mGivenVisibleInsets);
285             mSource.setVisibleFrame(mTmpRect);
286         } else {
287             mSource.setVisibleFrame(null);
288         }
289     }
290 
updateSourceFrameForServerVisibility()291     private void updateSourceFrameForServerVisibility() {
292         // Make sure we set the valid source frame only when server visible is true, because the
293         // frame may not yet be determined that server side doesn't think the window is ready to
294         // visible. (i.e. No surface, pending insets that were given during layout, etc..)
295         final Rect frame = mServerVisible ? mSourceFrame : EMPTY_RECT;
296         if (mSource.getFrame().equals(frame)) {
297             return;
298         }
299         mSource.setFrame(frame);
300         if (mWindowContainer != null) {
301             mSource.updateSideHint(mWindowContainer.getBounds());
302         }
303     }
304 
onWindowContainerBoundsChanged()305     void onWindowContainerBoundsChanged() {
306         mInsetsHintStale = true;
307     }
308 
309     @VisibleForTesting
getInsetsHint()310     Insets getInsetsHint() {
311         if (!mServerVisible) {
312             return mInsetsHint;
313         }
314         final WindowState win = mWindowContainer.asWindowState();
315         if (win != null && win.mGivenInsetsPending) {
316             return mInsetsHint;
317         }
318         if (mInsetsHintStale) {
319             final Rect bounds = mWindowContainer.getBounds();
320             mInsetsHint = mSource.calculateInsets(bounds, true /* ignoreVisibility */);
321             mInsetsHintStale = false;
322         }
323         return mInsetsHint;
324     }
325 
326     /** @return A new source computed by the specified window frame in the given display frames. */
createSimulatedSource(DisplayFrames displayFrames, Rect frame)327     InsetsSource createSimulatedSource(DisplayFrames displayFrames, Rect frame) {
328         final InsetsSource source = new InsetsSource(mSource);
329         mTmpRect.set(frame);
330         if (mFrameProvider != null) {
331             mFrameProvider.apply(displayFrames, mWindowContainer, mTmpRect);
332         }
333         source.setFrame(mTmpRect);
334 
335         // Don't copy visible frame because it might not be calculated in the provided display
336         // frames and it is not significant for this usage.
337         source.setVisibleFrame(null);
338 
339         return source;
340     }
341 
342     /**
343      * Called when a layout pass has occurred.
344      */
onPostLayout()345     void onPostLayout() {
346         if (mWindowContainer == null) {
347             return;
348         }
349         WindowState windowState = mWindowContainer.asWindowState();
350         boolean isServerVisible = windowState != null
351                 ? windowState.wouldBeVisibleIfPolicyIgnored() && windowState.isVisibleByPolicy()
352                 : mWindowContainer.isVisibleRequested();
353 
354         if (android.view.inputmethod.Flags.refactorInsetsController()) {
355             if (mControl != null && mControl.getType() == WindowInsets.Type.ime() && !mServerVisible
356                     && isServerVisible && windowState != null) {
357                 // in case the IME becomes visible, we need to check if it is already drawn and
358                 // does not have given insets pending. If it's not yet drawn, we do not set
359                 // server visibility
360                 isServerVisible = windowState.isDrawn() && !windowState.mGivenInsetsPending;
361             }
362         }
363         final boolean serverVisibleChanged = mServerVisible != isServerVisible;
364         setServerVisible(isServerVisible);
365         updateInsetsControlPosition(windowState, serverVisibleChanged);
366     }
367 
updateInsetsControlPosition(WindowState windowState)368     void updateInsetsControlPosition(WindowState windowState) {
369         updateInsetsControlPosition(windowState, false);
370     }
371 
updateInsetsControlPosition(WindowState windowState, boolean serverVisibleChanged)372     private void updateInsetsControlPosition(WindowState windowState,
373             boolean serverVisibleChanged) {
374         if (mControl == null) {
375             return;
376         }
377         boolean changed = false;
378         final Point position = getWindowFrameSurfacePosition();
379         if (mControl.setSurfacePosition(position.x, position.y) && mControlTarget != null) {
380             changed = true;
381             if (windowState != null && windowState.getWindowFrames().didFrameSizeChange()
382                     && windowState.mWinAnimator.getShown() && mWindowContainer.okToDisplay()) {
383                 mHasPendingPosition = true;
384                 windowState.applyWithNextDraw(mSetLeashPositionConsumer);
385             } else {
386                 Transaction t = mWindowContainer.getSyncTransaction();
387                 if (windowState != null) {
388                     // Make the buffer, token transformation, and leash position to be updated
389                     // together when the window is drawn for new rotation. Otherwise the window
390                     // may be outside the screen by the inconsistent orientations.
391                     final AsyncRotationController rotationController =
392                             mDisplayContent.getAsyncRotationController();
393                     if (rotationController != null) {
394                         final Transaction drawT =
395                                 rotationController.getDrawTransaction(windowState.mToken);
396                         if (drawT != null) {
397                             t = drawT;
398                         }
399                     }
400                 }
401                 mSetLeashPositionConsumer.accept(t);
402             }
403         }
404         final Insets insetsHint = getInsetsHint();
405         if (!mControl.getInsetsHint().equals(insetsHint)) {
406             mControl.setInsetsHint(insetsHint);
407             changed = true;
408         }
409         if (android.view.inputmethod.Flags.refactorInsetsController() && serverVisibleChanged) {
410             changed = true;
411         }
412         if (changed) {
413             mStateController.notifyControlChanged(mControlTarget);
414         }
415     }
416 
getWindowFrameSurfacePosition()417     private Point getWindowFrameSurfacePosition() {
418         final WindowState win = mWindowContainer.asWindowState();
419         if (win != null && mControl != null) {
420             final AsyncRotationController controller = mDisplayContent.getAsyncRotationController();
421             if (controller != null && controller.shouldFreezeInsetsPosition(win)) {
422                 // Use previous position because the window still shows with old rotation.
423                 return mControl.getSurfacePosition();
424             }
425         }
426         final Rect frame = win != null ? win.getFrame() : mWindowContainer.getBounds();
427         final Point position = new Point();
428         mWindowContainer.transformFrameToSurfacePosition(frame.left, frame.top, position);
429         return position;
430     }
431 
432     /**
433      * @see InsetsStateController#onControlTargetChanged
434      */
updateFakeControlTarget(@ullable InsetsControlTarget fakeTarget)435     void updateFakeControlTarget(@Nullable InsetsControlTarget fakeTarget) {
436         if (fakeTarget == mFakeControlTarget) {
437             return;
438         }
439         mFakeControlTarget = fakeTarget;
440     }
441 
442     /**
443      * Ensures that the inset source window container is cropped so that anything that doesn't fit
444      * within the inset frame is cropped out until removeCropToProvidingInsetsBounds is called.
445      *
446      * The inset source surface will get cropped to the be of the size of the insets it's providing.
447      *
448      * For example, for the taskbar window which serves as the ITYPE_EXTRA_NAVIGATION_BAR inset
449      * source, the window is larger than the insets because of the rounded corners overlay, but
450      * during task animations we want to make sure that the overlay is cropped out of the window so
451      * that they don't hide the window animations.
452      *
453      * @param t The transaction to use to apply immediate overflow cropping operations.
454      *
455      * NOTE: The relies on the inset source window to have a leash (usually this would be a leash
456      * for the ANIMATION_TYPE_INSETS_CONTROL animation if the inset is controlled by the client)
457      *
458      * TODO: Remove when we migrate over to shell transitions (b/202383002)
459      */
setCropToProvidingInsetsBounds(Transaction t)460     void setCropToProvidingInsetsBounds(Transaction t) {
461         mCropToProvidingInsets = true;
462 
463         if (mWindowContainer != null && mWindowContainer.mSurfaceAnimator.hasLeash()) {
464             // apply to existing leash
465             t.setWindowCrop(mWindowContainer.mSurfaceAnimator.mLeash,
466                     getProvidingInsetsBoundsCropRect());
467         }
468     }
469 
470     /**
471      * Removes any overflow cropping and future cropping to the inset source window's leash that may
472      * have been set with a call to setCropToProvidingInsetsBounds().
473      * @param t The transaction to use to apply immediate removal of overflow cropping.
474      *
475      * TODO: Remove when we migrate over to shell transitions (b/202383002)
476      */
removeCropToProvidingInsetsBounds(Transaction t)477     void removeCropToProvidingInsetsBounds(Transaction t) {
478         mCropToProvidingInsets = false;
479 
480         // apply to existing leash
481         if (mWindowContainer != null && mWindowContainer.mSurfaceAnimator.hasLeash()) {
482             t.setWindowCrop(mWindowContainer.mSurfaceAnimator.mLeash, null);
483         }
484     }
485 
getProvidingInsetsBoundsCropRect()486     private Rect getProvidingInsetsBoundsCropRect() {
487         Rect sourceWindowFrame = mWindowContainer.asWindowState() != null
488                 ? mWindowContainer.asWindowState().getFrame()
489                 : mWindowContainer.getBounds();
490         Rect insetFrame = getSource().getFrame();
491 
492         // The rectangle in buffer space we want to crop to
493         return new Rect(
494                 insetFrame.left - sourceWindowFrame.left,
495                 insetFrame.top - sourceWindowFrame.top,
496                 insetFrame.right - sourceWindowFrame.left,
497                 insetFrame.bottom - sourceWindowFrame.top
498         );
499     }
500 
updateControlForTarget(@ullable InsetsControlTarget target, boolean force)501     void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force) {
502         if (mSeamlessRotating) {
503             // We are un-rotating the window against the display rotation. We don't want the target
504             // to control the window for now.
505             return;
506         }
507         mPendingControlTarget = target;
508 
509         if (mWindowContainer != null && mWindowContainer.getSurfaceControl() == null) {
510             // if window doesn't have a surface, set it null and return.
511             setWindowContainer(null, null, null);
512         }
513         if (mWindowContainer == null) {
514             return;
515         }
516         if (target == mControlTarget && !force) {
517             return;
518         }
519         if (mHasPendingPosition) {
520             // Don't create a new leash while having a pending position. Otherwise, the position
521             // will be changed earlier than expected, which can cause flicker.
522             return;
523         }
524         if (target == null) {
525             // Cancelling the animation will invoke onAnimationCancelled, resetting all the fields.
526             mWindowContainer.cancelAnimation();
527             setClientVisible((WindowInsets.Type.defaultVisible() & mSource.getType()) != 0);
528             return;
529         }
530         final Point surfacePosition = getWindowFrameSurfacePosition();
531         mAdapter = new ControlAdapter(surfacePosition);
532         if (mSource.getType() == WindowInsets.Type.ime()) {
533             setClientVisible(target.isRequestedVisible(WindowInsets.Type.ime()));
534         }
535         final Transaction t = mWindowContainer.getSyncTransaction();
536         mWindowContainer.startAnimation(t, mAdapter, !mClientVisible /* hidden */,
537                 ANIMATION_TYPE_INSETS_CONTROL);
538 
539         // The leash was just created. We cannot dispatch it until its surface transaction is
540         // applied. Otherwise, the client's operation to the leash might be overwritten by us.
541         mIsLeashReadyForDispatching = false;
542 
543         final SurfaceControl leash = mAdapter.mCapturedLeash;
544         mControlTarget = target;
545         updateVisibility();
546         boolean initiallyVisible = mClientVisible;
547         if (mSource.getType() == WindowInsets.Type.ime()) {
548             // The IME cannot be initially visible, see ControlAdapter#startAnimation below.
549             // Also, the ImeInsetsSourceConsumer clears the client visibility upon losing control,
550             // but this won't have reached here yet by the time the new control is created.
551             // Note: The DisplayImeController needs the correct previous client's visibility, so we
552             // only override the initiallyVisible here.
553             initiallyVisible = false;
554         }
555         mControl = new InsetsSourceControl(mSource.getId(), mSource.getType(), leash,
556                 initiallyVisible, surfacePosition, getInsetsHint());
557 
558         ProtoLog.d(WM_DEBUG_WINDOW_INSETS,
559                 "InsetsSource Control %s for target %s", mControl, mControlTarget);
560     }
561 
startSeamlessRotation()562     void startSeamlessRotation() {
563         if (!mSeamlessRotating) {
564             mSeamlessRotating = true;
565             mWindowContainer.cancelAnimation();
566         }
567     }
568 
finishSeamlessRotation()569     void finishSeamlessRotation() {
570         mSeamlessRotating = false;
571     }
572 
updateClientVisibility(InsetsControlTarget caller)573     boolean updateClientVisibility(InsetsControlTarget caller) {
574         final boolean requestedVisible = caller.isRequestedVisible(mSource.getType());
575         if (caller != mControlTarget || requestedVisible == mClientVisible) {
576             return false;
577         }
578         setClientVisible(requestedVisible);
579         return true;
580     }
581 
onSurfaceTransactionApplied()582     void onSurfaceTransactionApplied() {
583         mIsLeashReadyForDispatching = true;
584     }
585 
setClientVisible(boolean clientVisible)586     void setClientVisible(boolean clientVisible) {
587         if (mClientVisible == clientVisible) {
588             return;
589         }
590         mClientVisible = clientVisible;
591         updateVisibility();
592         // The visibility change needs a traversal to apply.
593         mDisplayContent.setLayoutNeeded();
594         mDisplayContent.mWmService.mWindowPlacerLocked.requestTraversal();
595     }
596 
597     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
setServerVisible(boolean serverVisible)598     void setServerVisible(boolean serverVisible) {
599         mServerVisible = serverVisible;
600         updateSourceFrameForServerVisibility();
601         updateVisibility();
602     }
603 
updateVisibility()604     protected void updateVisibility() {
605         mSource.setVisible(mServerVisible && mClientVisible);
606         ProtoLog.d(WM_DEBUG_WINDOW_INSETS,
607                 "InsetsSource updateVisibility for %s, serverVisible: %s clientVisible: %s",
608                 WindowInsets.Type.toString(mSource.getType()),
609                 mServerVisible, mClientVisible);
610     }
611 
isLeashReadyForDispatching()612     protected boolean isLeashReadyForDispatching() {
613         return mIsLeashReadyForDispatching;
614     }
615 
616     /**
617      * Gets the source control for the given control target. If this is the provider's control
618      * target, but the leash is not ready for dispatching, a new source control object with the
619      * leash set to {@code null} is returned.
620      *
621      * @param target the control target to get the source control for.
622      */
623     @Nullable
getControl(InsetsControlTarget target)624     InsetsSourceControl getControl(InsetsControlTarget target) {
625         if (target == mControlTarget) {
626             if (!isLeashReadyForDispatching() && mControl != null) {
627                 // The surface transaction of preparing leash is not applied yet. We don't send it
628                 // to the client in case that the client applies its transaction sooner than ours
629                 // that we could unexpectedly overwrite the surface state.
630                 return new InsetsSourceControl(mControl.getId(), mControl.getType(),
631                         null /* leash */, mControl.isInitiallyVisible(),
632                         mControl.getSurfacePosition(), mControl.getInsetsHint());
633             }
634             return mControl;
635         }
636         if (target == mFakeControlTarget) {
637             return mFakeControl;
638         }
639         return null;
640     }
641 
642     /**
643      * Gets the leash of the source control for the given control target. If this is not the
644      * provider's control target, or the leash is not ready for dispatching, this will
645      * return {@code null}.
646      *
647      * @param target the control target to get the source control leash for.
648      */
649     @Nullable
getLeash(@onNull InsetsControlTarget target)650     protected SurfaceControl getLeash(@NonNull InsetsControlTarget target) {
651         return target == mControlTarget && mIsLeashReadyForDispatching && mControl != null
652                 ? mControl.getLeash() : null;
653     }
654 
655     @Nullable
getControlTarget()656     InsetsControlTarget getControlTarget() {
657         return mControlTarget;
658     }
659 
660     @Nullable
getFakeControlTarget()661     InsetsControlTarget getFakeControlTarget() {
662         return mFakeControlTarget;
663     }
664 
isClientVisible()665     boolean isClientVisible() {
666         return mClientVisible;
667     }
668 
overridesFrame(int windowType)669     boolean overridesFrame(int windowType) {
670         return mOverrideFrames.contains(windowType);
671     }
672 
getOverriddenFrame(int windowType)673     Rect getOverriddenFrame(int windowType) {
674         return mOverrideFrames.get(windowType);
675     }
676 
dump(PrintWriter pw, String prefix)677     public void dump(PrintWriter pw, String prefix) {
678         pw.println(prefix + getClass().getSimpleName());
679         prefix = prefix + "  ";
680         pw.print(prefix + "mSource="); mSource.dump("", pw);
681         pw.print(prefix + "mSourceFrame=");
682         pw.println(mSourceFrame);
683         if (mOverrideFrames.size() > 0) {
684             pw.print(prefix + "mOverrideFrames=");
685             pw.println(mOverrideFrames);
686         }
687         if (mControl != null) {
688             pw.print(prefix + "mControl=");
689             mControl.dump("", pw);
690         }
691         if (mControllable) {
692             pw.print(prefix + "mInsetsHint=");
693             pw.print(mInsetsHint);
694             if (mInsetsHintStale) {
695                 pw.print(" stale");
696             }
697             pw.println();
698         }
699         pw.print(prefix);
700         pw.print("mIsLeashReadyForDispatching="); pw.print(mIsLeashReadyForDispatching);
701         pw.print(" mHasPendingPosition="); pw.print(mHasPendingPosition);
702         pw.println();
703         if (mWindowContainer != null) {
704             pw.print(prefix + "mWindowContainer=");
705             pw.println(mWindowContainer);
706         }
707         if (mAdapter != null) {
708             pw.print(prefix + "mAdapter=");
709             mAdapter.dump(pw, "");
710         }
711         if (mControlTarget != null) {
712             pw.print(prefix + "mControlTarget=");
713             pw.println(mControlTarget);
714         }
715         if (mPendingControlTarget != mControlTarget) {
716             pw.print(prefix + "mPendingControlTarget=");
717             pw.println(mPendingControlTarget);
718         }
719         if (mFakeControlTarget != null) {
720             pw.print(prefix + "mFakeControlTarget=");
721             pw.println(mFakeControlTarget);
722         }
723     }
724 
dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel)725     void dumpDebug(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel) {
726         final long token = proto.start(fieldId);
727         mSource.dumpDebug(proto, SOURCE);
728         mTmpRect.dumpDebug(proto, FRAME);
729         mFakeControl.dumpDebug(proto, FAKE_CONTROL);
730         if (mControl != null) {
731             mControl.dumpDebug(proto, CONTROL);
732         }
733         if (mControlTarget != null && mControlTarget.getWindow() != null) {
734             mControlTarget.getWindow().dumpDebug(proto, CONTROL_TARGET, logLevel);
735         }
736         if (mPendingControlTarget != null && mPendingControlTarget != mControlTarget
737                 && mPendingControlTarget.getWindow() != null) {
738             mPendingControlTarget.getWindow().dumpDebug(proto, PENDING_CONTROL_TARGET, logLevel);
739         }
740         if (mFakeControlTarget != null && mFakeControlTarget.getWindow() != null) {
741             mFakeControlTarget.getWindow().dumpDebug(proto, FAKE_CONTROL_TARGET, logLevel);
742         }
743         if (mAdapter != null && mAdapter.mCapturedLeash != null) {
744             mAdapter.mCapturedLeash.dumpDebug(proto, CAPTURED_LEASH);
745         }
746         proto.write(IS_LEASH_READY_FOR_DISPATCHING, mIsLeashReadyForDispatching);
747         proto.write(CLIENT_VISIBLE, mClientVisible);
748         proto.write(SERVER_VISIBLE, mServerVisible);
749         proto.write(SEAMLESS_ROTATING, mSeamlessRotating);
750         proto.write(CONTROLLABLE, mControllable);
751         if (mWindowContainer != null && mWindowContainer.asWindowState() != null) {
752             mWindowContainer.asWindowState().dumpDebug(proto, SOURCE_WINDOW_STATE, logLevel);
753         }
754         proto.end(token);
755     }
756 
757     private class ControlAdapter implements AnimationAdapter {
758 
759         private final Point mSurfacePosition;
760         private SurfaceControl mCapturedLeash;
761 
ControlAdapter(Point surfacePosition)762         ControlAdapter(Point surfacePosition) {
763             mSurfacePosition = surfacePosition;
764         }
765 
766         @Override
getShowWallpaper()767         public boolean getShowWallpaper() {
768             return false;
769         }
770 
771         @Override
startAnimation(SurfaceControl animationLeash, Transaction t, @AnimationType int type, @NonNull OnAnimationFinishedCallback finishCallback)772         public void startAnimation(SurfaceControl animationLeash, Transaction t,
773                 @AnimationType int type, @NonNull OnAnimationFinishedCallback finishCallback) {
774             // TODO(b/166736352): Check if we still need to control the IME visibility here.
775             if (mSource.getType() == WindowInsets.Type.ime()) {
776                 if (!android.view.inputmethod.Flags.refactorInsetsController() || !mClientVisible) {
777                     // TODO: use 0 alpha and remove t.hide() once b/138459974 is fixed.
778                     t.setAlpha(animationLeash, 1 /* alpha */);
779                     t.hide(animationLeash);
780                 }
781             }
782             ProtoLog.i(WM_DEBUG_WINDOW_INSETS,
783                     "ControlAdapter startAnimation mSource: %s controlTarget: %s", mSource,
784                     mControlTarget);
785 
786             mCapturedLeash = animationLeash;
787             t.setPosition(mCapturedLeash, mSurfacePosition.x, mSurfacePosition.y);
788 
789             if (mCropToProvidingInsets) {
790                 // Apply crop to hide overflow
791                 t.setWindowCrop(mCapturedLeash, getProvidingInsetsBoundsCropRect());
792             }
793         }
794 
795         @Override
onAnimationCancelled(SurfaceControl animationLeash)796         public void onAnimationCancelled(SurfaceControl animationLeash) {
797             if (mAdapter == this) {
798                 mStateController.notifyControlRevoked(mControlTarget, InsetsSourceProvider.this);
799                 mControl = null;
800                 mControlTarget = null;
801                 mAdapter = null;
802                 setClientVisible((WindowInsets.Type.defaultVisible() & mSource.getType()) != 0);
803                 ProtoLog.i(WM_DEBUG_WINDOW_INSETS,
804                         "ControlAdapter onAnimationCancelled mSource: %s mControlTarget: %s",
805                         mSource, mControlTarget);
806             }
807         }
808 
809         @Override
getDurationHint()810         public long getDurationHint() {
811             return 0;
812         }
813 
814         @Override
getStatusBarTransitionsStartTime()815         public long getStatusBarTransitionsStartTime() {
816             return 0;
817         }
818 
819         @Override
dump(PrintWriter pw, String prefix)820         public void dump(PrintWriter pw, String prefix) {
821             pw.print(prefix + "ControlAdapter mCapturedLeash=");
822             pw.print(mCapturedLeash);
823             pw.println();
824         }
825 
826         @Override
dumpDebug(ProtoOutputStream proto)827         public void dumpDebug(ProtoOutputStream proto) {
828         }
829     }
830 }
831