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 android.view.InsetsState.ITYPE_IME;
20 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
21 import static android.view.InsetsState.ITYPE_STATUS_BAR;
22 import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
23 import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME;
24 import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
25 import static android.view.ViewRootImpl.sNewInsetsMode;
26 
27 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_IME;
28 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_INSETS_CONTROL;
29 import static com.android.server.wm.WindowManagerService.H.LAYOUT_AND_ASSIGN_WINDOW_LAYERS_IF_NEEDED;
30 
31 import android.annotation.NonNull;
32 import android.annotation.Nullable;
33 import android.graphics.Point;
34 import android.graphics.Rect;
35 import android.util.proto.ProtoOutputStream;
36 import android.view.InsetsSource;
37 import android.view.InsetsSourceControl;
38 import android.view.InsetsState;
39 import android.view.SurfaceControl;
40 import android.view.SurfaceControl.Transaction;
41 
42 import com.android.internal.annotations.VisibleForTesting;
43 import com.android.internal.util.function.TriConsumer;
44 import com.android.server.protolog.common.ProtoLog;
45 import com.android.server.wm.SurfaceAnimator.AnimationType;
46 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
47 
48 import java.io.PrintWriter;
49 
50 /**
51  * Controller for a specific inset source on the server. It's called provider as it provides the
52  * {@link InsetsSource} to the client that uses it in {@link InsetsSourceConsumer}.
53  */
54 class InsetsSourceProvider {
55 
56     protected final DisplayContent mDisplayContent;
57     protected final @NonNull InsetsSource mSource;
58     protected WindowState mWin;
59 
60     private final Rect mTmpRect = new Rect();
61     private final InsetsStateController mStateController;
62     private final InsetsSourceControl mFakeControl;
63     private @Nullable InsetsSourceControl mControl;
64     private @Nullable InsetsControlTarget mControlTarget;
65     private @Nullable InsetsControlTarget mPendingControlTarget;
66     private @Nullable InsetsControlTarget mFakeControlTarget;
67 
68     private @Nullable ControlAdapter mAdapter;
69     private TriConsumer<DisplayFrames, WindowState, Rect> mFrameProvider;
70     private TriConsumer<DisplayFrames, WindowState, Rect> mImeFrameProvider;
71     private final Rect mImeOverrideFrame = new Rect();
72     private boolean mIsLeashReadyForDispatching;
73 
74     /** The visibility override from the current controlling window. */
75     private boolean mClientVisible;
76 
77     /**
78      * Whether the window is available and considered visible as in {@link WindowState#isVisible}.
79      */
80     private boolean mServerVisible;
81 
82     private boolean mSeamlessRotating;
83     private long mFinishSeamlessRotateFrameNumber = -1;
84 
85     private final boolean mControllable;
86 
InsetsSourceProvider(InsetsSource source, InsetsStateController stateController, DisplayContent displayContent)87     InsetsSourceProvider(InsetsSource source, InsetsStateController stateController,
88             DisplayContent displayContent) {
89         mClientVisible = InsetsState.getDefaultVisibility(source.getType());
90         mSource = source;
91         mDisplayContent = displayContent;
92         mStateController = stateController;
93         mFakeControl = new InsetsSourceControl(source.getType(), null /* leash */,
94                 new Point());
95 
96         final int type = source.getType();
97         if (type == ITYPE_STATUS_BAR || type == ITYPE_NAVIGATION_BAR) {
98             mControllable = sNewInsetsMode == NEW_INSETS_MODE_FULL;
99         } else if (type == ITYPE_IME) {
100             mControllable = sNewInsetsMode >= NEW_INSETS_MODE_IME;
101         } else {
102             mControllable = false;
103         }
104     }
105 
getSource()106     InsetsSource getSource() {
107         return mSource;
108     }
109 
110     /**
111      * @return Whether the current flag configuration allows to control this source.
112      */
isControllable()113     boolean isControllable() {
114         return mControllable;
115     }
116 
117     /**
118      * Updates the window that currently backs this source.
119      *
120      * @param win The window that links to this source.
121      * @param frameProvider Based on display frame state and the window, calculates the resulting
122      *                      frame that should be reported to clients.
123      * @param imeFrameProvider Based on display frame state and the window, calculates the resulting
124      *                         frame that should be reported to IME.
125      */
setWindow(@ullable WindowState win, @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider, @Nullable TriConsumer<DisplayFrames, WindowState, Rect> imeFrameProvider)126     void setWindow(@Nullable WindowState win,
127             @Nullable TriConsumer<DisplayFrames, WindowState, Rect> frameProvider,
128             @Nullable TriConsumer<DisplayFrames, WindowState, Rect> imeFrameProvider) {
129         if (mWin != null) {
130             if (mControllable) {
131                 mWin.setControllableInsetProvider(null);
132             }
133             // The window may be animating such that we can hand out the leash to the control
134             // target. Revoke the leash by cancelling the animation to correct the state.
135             // TODO: Ideally, we should wait for the animation to finish so previous window can
136             // animate-out as new one animates-in.
137             mWin.cancelAnimation();
138         }
139         ProtoLog.d(WM_DEBUG_IME, "InsetsSource setWin %s", win);
140         mWin = win;
141         mFrameProvider = frameProvider;
142         mImeFrameProvider = imeFrameProvider;
143         if (win == null) {
144             setServerVisible(false);
145             mSource.setFrame(new Rect());
146             mSource.setVisibleFrame(null);
147         } else if (mControllable) {
148             mWin.setControllableInsetProvider(this);
149             if (mPendingControlTarget != null) {
150                 updateControlForTarget(mPendingControlTarget, true /* force */);
151                 mPendingControlTarget = null;
152             }
153         }
154     }
155 
156     /**
157      * @return Whether there is a window which backs this source.
158      */
hasWindow()159     boolean hasWindow() {
160         return mWin != null;
161     }
162 
163     /**
164      * The source frame can affect the layout of other windows, so this should be called once the
165      * window gets laid out.
166      */
updateSourceFrame()167     void updateSourceFrame() {
168         if (mWin == null) {
169             return;
170         }
171 
172         // Make sure we set the valid source frame only when server visible is true, because the
173         // frame may not yet determined that server side doesn't think the window is ready to
174         // visible. (i.e. No surface, pending insets that were given during layout, etc..)
175         if (mServerVisible) {
176             mTmpRect.set(mWin.getFrameLw());
177             if (mFrameProvider != null) {
178                 mFrameProvider.accept(mWin.getDisplayContent().mDisplayFrames, mWin, mTmpRect);
179             } else {
180                 mTmpRect.inset(mWin.mGivenContentInsets);
181             }
182         } else {
183             mTmpRect.setEmpty();
184         }
185         mSource.setFrame(mTmpRect);
186 
187         if (mImeFrameProvider != null) {
188             mImeOverrideFrame.set(mWin.getFrameLw());
189             mImeFrameProvider.accept(mWin.getDisplayContent().mDisplayFrames, mWin,
190                     mImeOverrideFrame);
191         }
192 
193         if (mWin.mGivenVisibleInsets.left != 0 || mWin.mGivenVisibleInsets.top != 0
194                 || mWin.mGivenVisibleInsets.right != 0 || mWin.mGivenVisibleInsets.bottom != 0) {
195             mTmpRect.set(mWin.getFrameLw());
196             mTmpRect.inset(mWin.mGivenVisibleInsets);
197             mSource.setVisibleFrame(mTmpRect);
198         } else {
199             mSource.setVisibleFrame(null);
200         }
201     }
202 
203     /** @return A new source computed by the specified window frame in the given display frames. */
createSimulatedSource(DisplayFrames displayFrames, WindowFrames windowFrames)204     InsetsSource createSimulatedSource(DisplayFrames displayFrames, WindowFrames windowFrames) {
205         // Don't copy visible frame because it might not be calculated in the provided display
206         // frames and it is not significant for this usage.
207         final InsetsSource source = new InsetsSource(mSource.getType());
208         source.setVisible(mSource.isVisible());
209         mTmpRect.set(windowFrames.mFrame);
210         if (mFrameProvider != null) {
211             mFrameProvider.accept(displayFrames, mWin, mTmpRect);
212         }
213         source.setFrame(mTmpRect);
214         return source;
215     }
216 
217     /**
218      * Called when a layout pass has occurred.
219      */
onPostLayout()220     void onPostLayout() {
221         if (mWin == null) {
222             return;
223         }
224 
225         setServerVisible(mWin.wouldBeVisibleIfPolicyIgnored() && mWin.isVisibleByPolicy()
226                 && !mWin.mGivenInsetsPending);
227         updateSourceFrame();
228         if (mControl != null) {
229             final Rect frame = mWin.getWindowFrames().mFrame;
230             if (mControl.setSurfacePosition(frame.left, frame.top) && mControlTarget != null) {
231                 // The leash has been stale, we need to create a new one for the client.
232                 updateControlForTarget(mControlTarget, true /* force */);
233                 mStateController.notifyControlChanged(mControlTarget);
234             }
235         }
236     }
237 
238     /**
239      * @see InsetsStateController#onControlFakeTargetChanged(int, InsetsControlTarget)
240      */
updateControlForFakeTarget(@ullable InsetsControlTarget fakeTarget)241     void updateControlForFakeTarget(@Nullable InsetsControlTarget fakeTarget) {
242         if (fakeTarget == mFakeControlTarget) {
243             return;
244         }
245         mFakeControlTarget = fakeTarget;
246     }
247 
updateControlForTarget(@ullable InsetsControlTarget target, boolean force)248     void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force) {
249         if (mSeamlessRotating) {
250             // We are un-rotating the window against the display rotation. We don't want the target
251             // to control the window for now.
252             return;
253         }
254         if (target != null && target.getWindow() != null) {
255             // ime control target could be a different window.
256             // Refer WindowState#getImeControlTarget().
257             target = target.getWindow().getImeControlTarget();
258         }
259 
260         if (mWin != null && mWin.getSurfaceControl() == null) {
261             // if window doesn't have a surface, set it null and return.
262             setWindow(null, null, null);
263         }
264         if (mWin == null) {
265             mPendingControlTarget = target;
266             return;
267         }
268         if (target == mControlTarget && !force) {
269             return;
270         }
271         if (target == null) {
272             // Cancelling the animation will invoke onAnimationCancelled, resetting all the fields.
273             mWin.cancelAnimation();
274             setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
275             return;
276         }
277         mAdapter = new ControlAdapter();
278         if (getSource().getType() == ITYPE_IME) {
279             setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
280         }
281         final Transaction t = mDisplayContent.getPendingTransaction();
282         mWin.startAnimation(t, mAdapter, !mClientVisible /* hidden */,
283                 ANIMATION_TYPE_INSETS_CONTROL);
284 
285         // The leash was just created. We cannot dispatch it until its surface transaction is
286         // applied. Otherwise, the client's operation to the leash might be overwritten by us.
287         mIsLeashReadyForDispatching = false;
288 
289         final SurfaceControl leash = mAdapter.mCapturedLeash;
290         final long frameNumber = mFinishSeamlessRotateFrameNumber;
291         mFinishSeamlessRotateFrameNumber = -1;
292         if (frameNumber >= 0 && mWin.mHasSurface && leash != null) {
293             // We just finished the seamless rotation. We don't want to change the position or the
294             // window crop of the surface controls (including the leash) until the client finishes
295             // drawing the new frame of the new orientation. Although we cannot defer the reparent
296             // operation, it is fine, because reparent won't cause any visual effect.
297             final SurfaceControl barrier = mWin.getClientViewRootSurface();
298             t.deferTransactionUntil(mWin.getSurfaceControl(), barrier, frameNumber);
299             t.deferTransactionUntil(leash, barrier, frameNumber);
300         }
301         mControlTarget = target;
302         updateVisibility();
303         mControl = new InsetsSourceControl(mSource.getType(), leash,
304                 new Point(mWin.getWindowFrames().mFrame.left, mWin.getWindowFrames().mFrame.top));
305         ProtoLog.d(WM_DEBUG_IME,
306                 "InsetsSource Control %s for target %s", mControl, mControlTarget);
307     }
308 
startSeamlessRotation()309     void startSeamlessRotation() {
310         if (!mSeamlessRotating) {
311             mSeamlessRotating = true;
312 
313             // This will revoke the leash and clear the control target.
314             mWin.cancelAnimation();
315         }
316     }
317 
finishSeamlessRotation(boolean timeout)318     void finishSeamlessRotation(boolean timeout) {
319         if (mSeamlessRotating) {
320             mSeamlessRotating = false;
321             mFinishSeamlessRotateFrameNumber = timeout ? -1 : mWin.getFrameNumber();
322         }
323     }
324 
onInsetsModified(InsetsControlTarget caller, InsetsSource modifiedSource)325     boolean onInsetsModified(InsetsControlTarget caller, InsetsSource modifiedSource) {
326         if (mControlTarget != caller || modifiedSource.isVisible() == mClientVisible) {
327             return false;
328         }
329         setClientVisible(modifiedSource.isVisible());
330         return true;
331     }
332 
onSurfaceTransactionApplied()333     void onSurfaceTransactionApplied() {
334         mIsLeashReadyForDispatching = true;
335     }
336 
setClientVisible(boolean clientVisible)337     private void setClientVisible(boolean clientVisible) {
338         if (mClientVisible == clientVisible) {
339             return;
340         }
341         mClientVisible = clientVisible;
342         mDisplayContent.mWmService.mH.obtainMessage(
343                 LAYOUT_AND_ASSIGN_WINDOW_LAYERS_IF_NEEDED, mDisplayContent).sendToTarget();
344         updateVisibility();
345     }
346 
347     @VisibleForTesting
setServerVisible(boolean serverVisible)348     void setServerVisible(boolean serverVisible) {
349         mServerVisible = serverVisible;
350         updateVisibility();
351     }
352 
updateVisibility()353     private void updateVisibility() {
354         mSource.setVisible(mServerVisible && (isMirroredSource() || mClientVisible));
355         ProtoLog.d(WM_DEBUG_IME,
356                 "InsetsSource updateVisibility serverVisible: %s clientVisible: %s",
357                 mServerVisible, mClientVisible);
358     }
359 
isMirroredSource()360     private boolean isMirroredSource() {
361         if (mWin == null) {
362             return false;
363         }
364         final int[] provides = mWin.mAttrs.providesInsetsTypes;
365         if (provides == null) {
366             return false;
367         }
368         for (int i = 0; i < provides.length; i++) {
369             if (provides[i] == ITYPE_IME) {
370                 return true;
371             }
372         }
373         return false;
374     }
375 
getControl(InsetsControlTarget target)376     InsetsSourceControl getControl(InsetsControlTarget target) {
377         if (target == mControlTarget) {
378             if (!mIsLeashReadyForDispatching && mControl != null) {
379                 // The surface transaction of preparing leash is not applied yet. We don't send it
380                 // to the client in case that the client applies its transaction sooner than ours
381                 // that we could unexpectedly overwrite the surface state.
382                 return new InsetsSourceControl(mControl.getType(), null /* leash */,
383                         mControl.getSurfacePosition());
384             }
385             return mControl;
386         }
387         if (target == mFakeControlTarget) {
388             return mFakeControl;
389         }
390         return null;
391     }
392 
getControlTarget()393     InsetsControlTarget getControlTarget() {
394         return mControlTarget;
395     }
396 
isClientVisible()397     boolean isClientVisible() {
398         return sNewInsetsMode == NEW_INSETS_MODE_NONE || mClientVisible;
399     }
400 
401     /**
402      * @return Whether this provider uses a different frame to dispatch to the IME.
403      */
overridesImeFrame()404     boolean overridesImeFrame() {
405         return mImeFrameProvider != null;
406     }
407 
408     /**
409      * @return Rect to dispatch to the IME as frame. Only valid if {@link #overridesImeFrame()}
410      *         returns {@code true}.
411      */
getImeOverrideFrame()412     Rect getImeOverrideFrame() {
413         return mImeOverrideFrame;
414     }
415 
dump(PrintWriter pw, String prefix)416     public void dump(PrintWriter pw, String prefix) {
417         pw.println(prefix + "InsetsSourceProvider");
418         pw.print(prefix + " mSource="); mSource.dump(prefix + "  ", pw);
419         if (mControl != null) {
420             pw.print(prefix + " mControl=");
421             mControl.dump(prefix + "  ", pw);
422         }
423         pw.print(prefix + " mFakeControl="); mFakeControl.dump(prefix + "  ", pw);
424         pw.print(" mIsLeashReadyForDispatching="); pw.print(mIsLeashReadyForDispatching);
425         pw.print(" mImeOverrideFrame="); pw.print(mImeOverrideFrame.toString());
426         if (mWin != null) {
427             pw.print(prefix + " mWin=");
428             mWin.dump(pw, prefix + "  ", false /* dumpAll */);
429         }
430         if (mAdapter != null) {
431             pw.print(prefix + " mAdapter=");
432             mAdapter.dump(pw, prefix + "  ");
433         }
434         if (mControlTarget != null) {
435             pw.print(prefix + " mControlTarget=");
436             if (mControlTarget.getWindow() != null) {
437                 mControlTarget.getWindow().dump(pw, prefix + "  ", false /* dumpAll */);
438             }
439         }
440         if (mPendingControlTarget != null) {
441             pw.print(prefix + " mPendingControlTarget=");
442             if (mPendingControlTarget.getWindow() != null) {
443                 mPendingControlTarget.getWindow().dump(pw, prefix + "  ", false /* dumpAll */);
444             }
445         }
446         if (mFakeControlTarget != null) {
447             pw.print(prefix + " mFakeControlTarget=");
448             if (mFakeControlTarget.getWindow() != null) {
449                 mFakeControlTarget.getWindow().dump(pw, prefix + "  ", false /* dumpAll */);
450             }
451         }
452     }
453 
454     private class ControlAdapter implements AnimationAdapter {
455 
456         private SurfaceControl mCapturedLeash;
457 
458         @Override
getShowWallpaper()459         public boolean getShowWallpaper() {
460             return false;
461         }
462 
463         @Override
startAnimation(SurfaceControl animationLeash, Transaction t, @AnimationType int type, OnAnimationFinishedCallback finishCallback)464         public void startAnimation(SurfaceControl animationLeash, Transaction t,
465                 @AnimationType int type, OnAnimationFinishedCallback finishCallback) {
466             // TODO(b/118118435): We can remove the type check when implementing the transient bar
467             //                    animation.
468             if (mSource.getType() == ITYPE_IME) {
469                 // TODO: use 0 alpha and remove t.hide() once b/138459974 is fixed.
470                 t.setAlpha(animationLeash, 1 /* alpha */);
471                 t.hide(animationLeash);
472             }
473             ProtoLog.i(WM_DEBUG_IME,
474                     "ControlAdapter startAnimation mSource: %s controlTarget: %s", mSource,
475                     mControlTarget);
476 
477             mCapturedLeash = animationLeash;
478             final Rect frame = mWin.getWindowFrames().mFrame;
479             t.setPosition(mCapturedLeash, frame.left, frame.top);
480         }
481 
482         @Override
onAnimationCancelled(SurfaceControl animationLeash)483         public void onAnimationCancelled(SurfaceControl animationLeash) {
484             if (mAdapter == this) {
485                 mStateController.notifyControlRevoked(mControlTarget, InsetsSourceProvider.this);
486                 mControl = null;
487                 mControlTarget = null;
488                 mAdapter = null;
489                 setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
490                 ProtoLog.i(WM_DEBUG_IME,
491                         "ControlAdapter onAnimationCancelled mSource: %s mControlTarget: %s",
492                         mSource, mControlTarget);
493             }
494         }
495 
496         @Override
getDurationHint()497         public long getDurationHint() {
498             return 0;
499         }
500 
501         @Override
getStatusBarTransitionsStartTime()502         public long getStatusBarTransitionsStartTime() {
503             return 0;
504         }
505 
506         @Override
dump(PrintWriter pw, String prefix)507         public void dump(PrintWriter pw, String prefix) {
508             pw.println(prefix + "ControlAdapter");
509             pw.print(prefix + " mCapturedLeash="); pw.print(mCapturedLeash);
510         }
511 
512         @Override
dumpDebug(ProtoOutputStream proto)513         public void dumpDebug(ProtoOutputStream proto) {
514         }
515     }
516 }
517