1 /*
2  * Copyright (C) 2016 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.graphics.Color.WHITE;
20 import static android.graphics.Color.alpha;
21 import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
22 import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
23 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
24 import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
25 import static android.view.WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
26 import static android.view.WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE;
27 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
28 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
29 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
30 import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
31 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
32 import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
33 import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
34 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
35 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
36 import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
37 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
38 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
39 
40 import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
41 import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
42 import static com.android.internal.policy.DecorView.getColorViewLeftInset;
43 import static com.android.internal.policy.DecorView.getColorViewTopInset;
44 import static com.android.internal.policy.DecorView.getNavigationBarRect;
45 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
46 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
47 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
48 
49 import android.annotation.Nullable;
50 import android.app.ActivityManager.TaskDescription;
51 import android.app.ActivityManager.TaskSnapshot;
52 import android.app.ActivityThread;
53 import android.content.Context;
54 import android.graphics.Canvas;
55 import android.graphics.Color;
56 import android.graphics.GraphicBuffer;
57 import android.graphics.Paint;
58 import android.graphics.Rect;
59 import android.os.Handler;
60 import android.os.Looper;
61 import android.os.Message;
62 import android.os.RemoteException;
63 import android.os.SystemClock;
64 import android.util.MergedConfiguration;
65 import android.util.Slog;
66 import android.view.DisplayCutout;
67 import android.view.IWindowSession;
68 import android.view.InsetsState;
69 import android.view.Surface;
70 import android.view.SurfaceControl;
71 import android.view.SurfaceSession;
72 import android.view.View;
73 import android.view.ViewGroup.LayoutParams;
74 import android.view.WindowManager;
75 import android.view.WindowManagerGlobal;
76 
77 import com.android.internal.R;
78 import com.android.internal.annotations.VisibleForTesting;
79 import com.android.internal.policy.DecorView;
80 import com.android.internal.view.BaseIWindow;
81 import com.android.server.policy.WindowManagerPolicy.StartingSurface;
82 
83 /**
84  * This class represents a starting window that shows a snapshot.
85  * <p>
86  * DO NOT HOLD THE WINDOW MANAGER LOCK WHEN CALLING METHODS OF THIS CLASS!
87  */
88 class TaskSnapshotSurface implements StartingSurface {
89 
90     private static final long SIZE_MISMATCH_MINIMUM_TIME_MS = 450;
91 
92     /**
93      * When creating the starting window, we use the exact same layout flags such that we end up
94      * with a window with the exact same dimensions etc. However, these flags are not used in layout
95      * and might cause other side effects so we exclude them.
96      */
97     private static final int FLAG_INHERIT_EXCLUDES = FLAG_NOT_FOCUSABLE
98             | FLAG_NOT_TOUCHABLE
99             | FLAG_NOT_TOUCH_MODAL
100             | FLAG_ALT_FOCUSABLE_IM
101             | FLAG_NOT_FOCUSABLE
102             | FLAG_HARDWARE_ACCELERATED
103             | FLAG_IGNORE_CHEEK_PRESSES
104             | FLAG_LOCAL_FOCUS_MODE
105             | FLAG_SLIPPERY
106             | FLAG_WATCH_OUTSIDE_TOUCH
107             | FLAG_SPLIT_TOUCH
108             | FLAG_SCALED
109             | FLAG_SECURE;
110 
111     private static final int PRIVATE_FLAG_INHERITS = PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
112 
113     private static final String TAG = TAG_WITH_CLASS_NAME ? "SnapshotStartingWindow" : TAG_WM;
114     private static final int MSG_REPORT_DRAW = 0;
115     private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s";
116     private final Window mWindow;
117     private final Surface mSurface;
118     private SurfaceControl mSurfaceControl;
119     private SurfaceControl mChildSurfaceControl;
120     private final IWindowSession mSession;
121     private final WindowManagerService mService;
122     private final Rect mTaskBounds;
123     private final Rect mStableInsets = new Rect();
124     private final Rect mContentInsets = new Rect();
125     private final Rect mFrame = new Rect();
126     private TaskSnapshot mSnapshot;
127     private final CharSequence mTitle;
128     private boolean mHasDrawn;
129     private long mShownTime;
130     private final Handler mHandler;
131     private boolean mSizeMismatch;
132     private final Paint mBackgroundPaint = new Paint();
133     private final int mStatusBarColor;
134     @VisibleForTesting final SystemBarBackgroundPainter mSystemBarBackgroundPainter;
135     private final int mOrientationOnCreation;
136 
create(WindowManagerService service, AppWindowToken token, TaskSnapshot snapshot)137     static TaskSnapshotSurface create(WindowManagerService service, AppWindowToken token,
138             TaskSnapshot snapshot) {
139 
140         final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
141         final Window window = new Window();
142         final IWindowSession session = WindowManagerGlobal.getWindowSession();
143         window.setSession(session);
144         final SurfaceControl surfaceControl = new SurfaceControl();
145         final Rect tmpRect = new Rect();
146         final DisplayCutout.ParcelableWrapper tmpCutout = new DisplayCutout.ParcelableWrapper();
147         final Rect tmpFrame = new Rect();
148         final Rect taskBounds;
149         final Rect tmpContentInsets = new Rect();
150         final Rect tmpStableInsets = new Rect();
151         final InsetsState mTmpInsetsState = new InsetsState();
152         final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
153         final TaskDescription taskDescription = new TaskDescription();
154         taskDescription.setBackgroundColor(WHITE);
155         final int sysUiVis;
156         final int windowFlags;
157         final int windowPrivateFlags;
158         final int currentOrientation;
159         synchronized (service.mGlobalLock) {
160             final WindowState mainWindow = token.findMainWindow();
161             final Task task = token.getTask();
162             if (task == null) {
163                 Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find task for token="
164                         + token);
165                 return null;
166             }
167             final AppWindowToken topFullscreenToken = token.getTask().getTopFullscreenAppToken();
168             if (topFullscreenToken == null) {
169                 Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find top fullscreen for task="
170                         + task);
171                 return null;
172             }
173             final WindowState topFullscreenWindow = topFullscreenToken.getTopFullscreenWindow();
174             if (mainWindow == null || topFullscreenWindow == null) {
175                 Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find main window for token="
176                         + token);
177                 return null;
178             }
179             sysUiVis = topFullscreenWindow.getSystemUiVisibility();
180             windowFlags = topFullscreenWindow.getAttrs().flags;
181             windowPrivateFlags = topFullscreenWindow.getAttrs().privateFlags;
182 
183             layoutParams.packageName = mainWindow.getAttrs().packageName;
184             layoutParams.windowAnimations = mainWindow.getAttrs().windowAnimations;
185             layoutParams.dimAmount = mainWindow.getAttrs().dimAmount;
186             layoutParams.type = TYPE_APPLICATION_STARTING;
187             layoutParams.format = snapshot.getSnapshot().getFormat();
188             layoutParams.flags = (windowFlags & ~FLAG_INHERIT_EXCLUDES)
189                     | FLAG_NOT_FOCUSABLE
190                     | FLAG_NOT_TOUCHABLE;
191             layoutParams.privateFlags = windowPrivateFlags & PRIVATE_FLAG_INHERITS;
192             layoutParams.token = token.token;
193             layoutParams.width = LayoutParams.MATCH_PARENT;
194             layoutParams.height = LayoutParams.MATCH_PARENT;
195             layoutParams.systemUiVisibility = sysUiVis;
196             layoutParams.setTitle(String.format(TITLE_FORMAT, task.mTaskId));
197 
198             final TaskDescription td = task.getTaskDescription();
199             if (td != null) {
200                 taskDescription.copyFrom(td);
201             }
202             taskBounds = new Rect();
203             task.getBounds(taskBounds);
204             currentOrientation = topFullscreenWindow.getConfiguration().orientation;
205         }
206         try {
207             final int res = session.addToDisplay(window, window.mSeq, layoutParams,
208                     View.GONE, token.getDisplayContent().getDisplayId(), tmpFrame, tmpRect, tmpRect,
209                     tmpRect, tmpCutout, null, mTmpInsetsState);
210             if (res < 0) {
211                 Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
212                 return null;
213             }
214         } catch (RemoteException e) {
215             // Local call.
216         }
217         final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window,
218                 surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, sysUiVis,
219                 windowFlags, windowPrivateFlags, taskBounds,
220                 currentOrientation);
221         window.setOuter(snapshotSurface);
222         try {
223             session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1,
224                     tmpFrame, tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect,
225                     tmpCutout, tmpMergedConfiguration, surfaceControl, mTmpInsetsState);
226         } catch (RemoteException e) {
227             // Local call.
228         }
229         snapshotSurface.setFrames(tmpFrame, tmpContentInsets, tmpStableInsets);
230         snapshotSurface.drawSnapshot();
231         return snapshotSurface;
232     }
233 
234     @VisibleForTesting
TaskSnapshotSurface(WindowManagerService service, Window window, SurfaceControl surfaceControl, TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription, int sysUiVis, int windowFlags, int windowPrivateFlags, Rect taskBounds, int currentOrientation)235     TaskSnapshotSurface(WindowManagerService service, Window window, SurfaceControl surfaceControl,
236             TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription,
237             int sysUiVis, int windowFlags, int windowPrivateFlags, Rect taskBounds,
238             int currentOrientation) {
239         mService = service;
240         mSurface = new Surface();
241         mHandler = new Handler(mService.mH.getLooper());
242         mSession = WindowManagerGlobal.getWindowSession();
243         mWindow = window;
244         mSurfaceControl = surfaceControl;
245         mSnapshot = snapshot;
246         mTitle = title;
247         int backgroundColor = taskDescription.getBackgroundColor();
248         mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
249         mTaskBounds = taskBounds;
250         mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags,
251                 windowPrivateFlags, sysUiVis, taskDescription, 1f);
252         mStatusBarColor = taskDescription.getStatusBarColor();
253         mOrientationOnCreation = currentOrientation;
254     }
255 
256     @Override
remove()257     public void remove() {
258         synchronized (mService.mGlobalLock) {
259             final long now = SystemClock.uptimeMillis();
260             if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS) {
261                 mHandler.postAtTime(this::remove, mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS);
262                 if (DEBUG_STARTING_WINDOW) {
263                     Slog.v(TAG, "Defer removing snapshot surface in "  + (now - mShownTime) + "ms");
264                 }
265                 return;
266             }
267         }
268         try {
269             if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Removing snapshot surface");
270             mSession.remove(mWindow);
271         } catch (RemoteException e) {
272             // Local call.
273         }
274     }
275 
276     @VisibleForTesting
setFrames(Rect frame, Rect contentInsets, Rect stableInsets)277     void setFrames(Rect frame, Rect contentInsets, Rect stableInsets) {
278         mFrame.set(frame);
279         mContentInsets.set(contentInsets);
280         mStableInsets.set(stableInsets);
281         mSizeMismatch = (mFrame.width() != mSnapshot.getSnapshot().getWidth()
282                 || mFrame.height() != mSnapshot.getSnapshot().getHeight());
283         mSystemBarBackgroundPainter.setInsets(contentInsets, stableInsets);
284     }
285 
drawSnapshot()286     private void drawSnapshot() {
287         final GraphicBuffer buffer = mSnapshot.getSnapshot();
288         mSurface.copyFrom(mSurfaceControl);
289 
290         if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Drawing snapshot surface sizeMismatch="
291                 + mSizeMismatch);
292         if (mSizeMismatch) {
293             // The dimensions of the buffer and the window don't match, so attaching the buffer
294             // will fail. Better create a child window with the exact dimensions and fill the parent
295             // window with the background color!
296             drawSizeMismatchSnapshot(buffer);
297         } else {
298             drawSizeMatchSnapshot(buffer);
299         }
300         synchronized (mService.mGlobalLock) {
301             mShownTime = SystemClock.uptimeMillis();
302             mHasDrawn = true;
303         }
304         reportDrawn();
305 
306         // In case window manager leaks us, make sure we don't retain the snapshot.
307         mSnapshot = null;
308     }
309 
drawSizeMatchSnapshot(GraphicBuffer buffer)310     private void drawSizeMatchSnapshot(GraphicBuffer buffer) {
311         mSurface.attachAndQueueBuffer(buffer);
312         mSurface.release();
313     }
314 
drawSizeMismatchSnapshot(GraphicBuffer buffer)315     private void drawSizeMismatchSnapshot(GraphicBuffer buffer) {
316         if (!mSurface.isValid()) {
317             throw new IllegalStateException("mSurface does not hold a valid surface.");
318         }
319         final SurfaceSession session = new SurfaceSession();
320         // We consider nearly matched dimensions as there can be rounding errors and the user won't
321         // notice very minute differences from scaling one dimension more than the other
322         final boolean aspectRatioMismatch = Math.abs(
323                 ((float) buffer.getWidth() / buffer.getHeight())
324                 - ((float) mFrame.width() / mFrame.height())) > 0.01f;
325 
326         // Keep a reference to it such that it doesn't get destroyed when finalized.
327         mChildSurfaceControl = new SurfaceControl.Builder(session)
328                 .setName(mTitle + " - task-snapshot-surface")
329                 .setBufferSize(buffer.getWidth(), buffer.getHeight())
330                 .setFormat(buffer.getFormat())
331                 .setParent(mSurfaceControl)
332                 .build();
333         Surface surface = new Surface();
334         surface.copyFrom(mChildSurfaceControl);
335 
336         final Rect frame;
337         SurfaceControl.openTransaction();
338         try {
339             // We can just show the surface here as it will still be hidden as the parent is
340             // still hidden.
341             mChildSurfaceControl.show();
342             if (aspectRatioMismatch) {
343                 // Clip off ugly navigation bar.
344                 final Rect crop = calculateSnapshotCrop();
345                 frame = calculateSnapshotFrame(crop);
346                 mChildSurfaceControl.setWindowCrop(crop);
347                 mChildSurfaceControl.setPosition(frame.left, frame.top);
348             } else {
349                 frame = null;
350             }
351 
352             // Scale the mismatch dimensions to fill the task bounds
353             final float scale = 1 / mSnapshot.getScale();
354             mChildSurfaceControl.setMatrix(scale, 0, 0, scale);
355         } finally {
356             SurfaceControl.closeTransaction();
357         }
358         surface.attachAndQueueBuffer(buffer);
359         surface.release();
360 
361         if (aspectRatioMismatch) {
362             final Canvas c = mSurface.lockCanvas(null);
363             drawBackgroundAndBars(c, frame);
364             mSurface.unlockCanvasAndPost(c);
365             mSurface.release();
366         }
367     }
368 
369     /**
370      * Calculates the snapshot crop in snapshot coordinate space.
371      *
372      * @return crop rect in snapshot coordinate space.
373      */
374     @VisibleForTesting
calculateSnapshotCrop()375     Rect calculateSnapshotCrop() {
376         final Rect rect = new Rect();
377         rect.set(0, 0, mSnapshot.getSnapshot().getWidth(), mSnapshot.getSnapshot().getHeight());
378         final Rect insets = mSnapshot.getContentInsets();
379 
380         // Let's remove all system decorations except the status bar, but only if the task is at the
381         // very top of the screen.
382         final boolean isTop = mTaskBounds.top == 0 && mFrame.top == 0;
383         rect.inset((int) (insets.left * mSnapshot.getScale()),
384                 isTop ? 0 : (int) (insets.top * mSnapshot.getScale()),
385                 (int) (insets.right * mSnapshot.getScale()),
386                 (int) (insets.bottom * mSnapshot.getScale()));
387         return rect;
388     }
389 
390     /**
391      * Calculates the snapshot frame in window coordinate space from crop.
392      *
393      * @param crop rect that is in snapshot coordinate space.
394      */
395     @VisibleForTesting
calculateSnapshotFrame(Rect crop)396     Rect calculateSnapshotFrame(Rect crop) {
397         final Rect frame = new Rect(crop);
398         final float scale = mSnapshot.getScale();
399 
400         // Rescale the frame from snapshot to window coordinate space
401         frame.scale(1 / scale);
402 
403         // By default, offset it to to top/left corner
404         frame.offsetTo((int) (-crop.left / scale), (int) (-crop.top / scale));
405 
406         // However, we also need to make space for the navigation bar on the left side.
407         final int colorViewLeftInset = getColorViewLeftInset(mStableInsets.left,
408                 mContentInsets.left);
409         frame.offset(colorViewLeftInset, 0);
410         return frame;
411     }
412 
413     @VisibleForTesting
drawBackgroundAndBars(Canvas c, Rect frame)414     void drawBackgroundAndBars(Canvas c, Rect frame) {
415         final int statusBarHeight = mSystemBarBackgroundPainter.getStatusBarColorViewHeight();
416         final boolean fillHorizontally = c.getWidth() > frame.right;
417         final boolean fillVertically = c.getHeight() > frame.bottom;
418         if (fillHorizontally) {
419             c.drawRect(frame.right, alpha(mStatusBarColor) == 0xFF ? statusBarHeight : 0,
420                     c.getWidth(), fillVertically
421                             ? frame.bottom
422                             : c.getHeight(),
423                     mBackgroundPaint);
424         }
425         if (fillVertically) {
426             c.drawRect(0, frame.bottom, c.getWidth(), c.getHeight(), mBackgroundPaint);
427         }
428         mSystemBarBackgroundPainter.drawDecors(c, frame);
429     }
430 
reportDrawn()431     private void reportDrawn() {
432         try {
433             mSession.finishDrawing(mWindow);
434         } catch (RemoteException e) {
435             // Local call.
436         }
437     }
438 
439     private static Handler sHandler = new Handler(Looper.getMainLooper()) {
440 
441         @Override
442         public void handleMessage(Message msg) {
443             switch (msg.what) {
444                 case MSG_REPORT_DRAW:
445                     final boolean hasDrawn;
446                     final TaskSnapshotSurface surface = (TaskSnapshotSurface) msg.obj;
447                     synchronized (surface.mService.mGlobalLock) {
448                         hasDrawn = surface.mHasDrawn;
449                     }
450                     if (hasDrawn) {
451                         surface.reportDrawn();
452                     }
453                     break;
454             }
455         }
456     };
457 
458     @VisibleForTesting
459     static class Window extends BaseIWindow {
460 
461         private TaskSnapshotSurface mOuter;
462 
setOuter(TaskSnapshotSurface outer)463         public void setOuter(TaskSnapshotSurface outer) {
464             mOuter = outer;
465         }
466 
467         @Override
resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw, MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId, DisplayCutout.ParcelableWrapper displayCutout)468         public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets,
469                 Rect stableInsets, Rect outsets, boolean reportDraw,
470                 MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
471                 boolean alwaysConsumeSystemBars, int displayId,
472                 DisplayCutout.ParcelableWrapper displayCutout) {
473             if (mergedConfiguration != null && mOuter != null
474                     && mOuter.mOrientationOnCreation
475                             != mergedConfiguration.getMergedConfiguration().orientation) {
476 
477                 // The orientation of the screen is changing. We better remove the snapshot ASAP as
478                 // we are going to wait on the new window in any case to unfreeze the screen, and
479                 // the starting window is not needed anymore.
480                 sHandler.post(mOuter::remove);
481             }
482             if (reportDraw) {
483                 sHandler.obtainMessage(MSG_REPORT_DRAW, mOuter).sendToTarget();
484             }
485         }
486     }
487 
488     /**
489      * Helper class to draw the background of the system bars in regions the task snapshot isn't
490      * filling the window.
491      */
492     static class SystemBarBackgroundPainter {
493 
494         private final Rect mContentInsets = new Rect();
495         private final Rect mStableInsets = new Rect();
496         private final Paint mStatusBarPaint = new Paint();
497         private final Paint mNavigationBarPaint = new Paint();
498         private final int mStatusBarColor;
499         private final int mNavigationBarColor;
500         private final int mWindowFlags;
501         private final int mWindowPrivateFlags;
502         private final int mSysUiVis;
503         private final float mScale;
504 
SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int sysUiVis, TaskDescription taskDescription, float scale)505         SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int sysUiVis,
506                 TaskDescription taskDescription, float scale) {
507             mWindowFlags = windowFlags;
508             mWindowPrivateFlags = windowPrivateFlags;
509             mSysUiVis = sysUiVis;
510             mScale = scale;
511             final Context context = ActivityThread.currentActivityThread().getSystemUiContext();
512             final int semiTransparent = context.getColor(
513                     R.color.system_bar_background_semi_transparent);
514             mStatusBarColor = DecorView.calculateBarColor(windowFlags, FLAG_TRANSLUCENT_STATUS,
515                     semiTransparent, taskDescription.getStatusBarColor(), sysUiVis,
516                     SYSTEM_UI_FLAG_LIGHT_STATUS_BAR,
517                     taskDescription.getEnsureStatusBarContrastWhenTransparent());
518             mNavigationBarColor = DecorView.calculateBarColor(windowFlags,
519                     FLAG_TRANSLUCENT_NAVIGATION, semiTransparent,
520                     taskDescription.getNavigationBarColor(), sysUiVis,
521                     SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
522                     taskDescription.getEnsureNavigationBarContrastWhenTransparent()
523                             && context.getResources().getBoolean(R.bool.config_navBarNeedsScrim));
524             mStatusBarPaint.setColor(mStatusBarColor);
525             mNavigationBarPaint.setColor(mNavigationBarColor);
526         }
527 
setInsets(Rect contentInsets, Rect stableInsets)528         void setInsets(Rect contentInsets, Rect stableInsets) {
529             mContentInsets.set(contentInsets);
530             mStableInsets.set(stableInsets);
531         }
532 
getStatusBarColorViewHeight()533         int getStatusBarColorViewHeight() {
534             final boolean forceBarBackground =
535                     (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
536             if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
537                     mSysUiVis, mStatusBarColor, mWindowFlags, forceBarBackground)) {
538                 return (int) (getColorViewTopInset(mStableInsets.top, mContentInsets.top) * mScale);
539             } else {
540                 return 0;
541             }
542         }
543 
isNavigationBarColorViewVisible()544         private boolean isNavigationBarColorViewVisible() {
545             final boolean forceBarBackground =
546                     (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
547             return NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
548                     mSysUiVis, mNavigationBarColor, mWindowFlags, forceBarBackground);
549         }
550 
drawDecors(Canvas c, @Nullable Rect alreadyDrawnFrame)551         void drawDecors(Canvas c, @Nullable Rect alreadyDrawnFrame) {
552             drawStatusBarBackground(c, alreadyDrawnFrame, getStatusBarColorViewHeight());
553             drawNavigationBarBackground(c);
554         }
555 
556         @VisibleForTesting
drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame, int statusBarHeight)557         void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame,
558                 int statusBarHeight) {
559             if (statusBarHeight > 0 && Color.alpha(mStatusBarColor) != 0
560                     && (alreadyDrawnFrame == null || c.getWidth() > alreadyDrawnFrame.right)) {
561                 final int rightInset = (int) (DecorView.getColorViewRightInset(mStableInsets.right,
562                         mContentInsets.right) * mScale);
563                 final int left = alreadyDrawnFrame != null ? alreadyDrawnFrame.right : 0;
564                 c.drawRect(left, 0, c.getWidth() - rightInset, statusBarHeight, mStatusBarPaint);
565             }
566         }
567 
568         @VisibleForTesting
drawNavigationBarBackground(Canvas c)569         void drawNavigationBarBackground(Canvas c) {
570             final Rect navigationBarRect = new Rect();
571             getNavigationBarRect(c.getWidth(), c.getHeight(), mStableInsets, mContentInsets,
572                     navigationBarRect, mScale);
573             final boolean visible = isNavigationBarColorViewVisible();
574             if (visible && Color.alpha(mNavigationBarColor) != 0 && !navigationBarRect.isEmpty()) {
575                 c.drawRect(navigationBarRect, mNavigationBarPaint);
576             }
577         }
578     }
579 }
580