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