1 /*
2  * Copyright (C) 2022 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 android.window;
18 
19 import static android.graphics.Color.WHITE;
20 import static android.graphics.Color.alpha;
21 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
22 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
23 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
24 import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
25 import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
26 import static android.view.WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
27 import static android.view.WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE;
28 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
29 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
30 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
31 import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
32 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
33 import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
34 import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
35 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
36 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
37 import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
38 import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
39 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED;
40 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
41 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
42 
43 import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
44 import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
45 import static com.android.internal.policy.DecorView.getNavigationBarRect;
46 
47 import android.annotation.NonNull;
48 import android.annotation.Nullable;
49 import android.app.ActivityManager;
50 import android.app.ActivityThread;
51 import android.content.Context;
52 import android.graphics.Canvas;
53 import android.graphics.GraphicBuffer;
54 import android.graphics.Paint;
55 import android.graphics.PixelFormat;
56 import android.graphics.Rect;
57 import android.hardware.HardwareBuffer;
58 import android.os.IBinder;
59 import android.util.Log;
60 import android.view.InsetsState;
61 import android.view.SurfaceControl;
62 import android.view.SurfaceSession;
63 import android.view.ViewGroup;
64 import android.view.WindowInsets;
65 import android.view.WindowManager;
66 
67 import com.android.internal.R;
68 import com.android.internal.annotations.VisibleForTesting;
69 import com.android.internal.policy.DecorView;
70 import com.android.window.flags.Flags;
71 
72 /**
73  * Utils class to help draw a snapshot on a surface.
74  * @hide
75  */
76 public class SnapshotDrawerUtils {
77     private static final String TAG = "SnapshotDrawerUtils";
78 
79     /**
80      * Used to check if toolkitSetFrameRateReadOnly flag is enabled
81      *
82      * @hide
83      */
84     private static boolean sToolkitSetFrameRateReadOnlyFlagValue =
85             android.view.flags.Flags.toolkitSetFrameRateReadOnly();
86 
87     /**
88      * When creating the starting window, we use the exact same layout flags such that we end up
89      * with a window with the exact same dimensions etc. However, these flags are not used in layout
90      * and might cause other side effects so we exclude them.
91      */
92     static final int FLAG_INHERIT_EXCLUDES = FLAG_NOT_FOCUSABLE
93             | FLAG_NOT_TOUCHABLE
94             | FLAG_NOT_TOUCH_MODAL
95             | FLAG_ALT_FOCUSABLE_IM
96             | FLAG_NOT_FOCUSABLE
97             | FLAG_HARDWARE_ACCELERATED
98             | FLAG_IGNORE_CHEEK_PRESSES
99             | FLAG_LOCAL_FOCUS_MODE
100             | FLAG_SLIPPERY
101             | FLAG_WATCH_OUTSIDE_TOUCH
102             | FLAG_SPLIT_TOUCH
103             | FLAG_SCALED
104             | FLAG_SECURE
105             | FLAG_DIM_BEHIND;
106 
107     private static final Paint sBackgroundPaint = new Paint();
108 
109     /**
110      * The internal object to hold the surface and drawing on it.
111      */
112     @VisibleForTesting
113     public static class SnapshotSurface {
114         private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
115         private final SurfaceControl mRootSurface;
116         private final TaskSnapshot mSnapshot;
117         private final CharSequence mTitle;
118 
119         private SystemBarBackgroundPainter mSystemBarBackgroundPainter;
120         private final Rect mFrame = new Rect();
121         private final Rect mSystemBarInsets = new Rect();
122         private final int mSnapshotW;
123         private final int mSnapshotH;
124         private boolean mSizeMismatch;
125 
SnapshotSurface(SurfaceControl rootSurface, TaskSnapshot snapshot, CharSequence title)126         public SnapshotSurface(SurfaceControl rootSurface, TaskSnapshot snapshot,
127                 CharSequence title) {
128             mRootSurface = rootSurface;
129             mSnapshot = snapshot;
130             mTitle = title;
131             final HardwareBuffer hwBuffer = snapshot.getHardwareBuffer();
132             mSnapshotW = hwBuffer.getWidth();
133             mSnapshotH = hwBuffer.getHeight();
134         }
135 
136         /**
137          * Initiate system bar painter to draw the system bar background.
138          */
139         @VisibleForTesting
initiateSystemBarPainter(int windowFlags, int windowPrivateFlags, int appearance, ActivityManager.TaskDescription taskDescription, @WindowInsets.Type.InsetsType int requestedVisibleTypes)140         public void initiateSystemBarPainter(int windowFlags, int windowPrivateFlags,
141                 int appearance, ActivityManager.TaskDescription taskDescription,
142                 @WindowInsets.Type.InsetsType int requestedVisibleTypes) {
143             mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags,
144                     windowPrivateFlags, appearance, taskDescription, 1f, requestedVisibleTypes);
145             int backgroundColor = taskDescription.getBackgroundColor();
146             sBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
147         }
148 
149         /**
150          * Set frame size that the snapshot should fill. It is the bounds of a task or activity.
151          */
152         @VisibleForTesting
setFrames(Rect frame, Rect systemBarInsets)153         public void setFrames(Rect frame, Rect systemBarInsets) {
154             mFrame.set(frame);
155             mSystemBarInsets.set(systemBarInsets);
156             mSizeMismatch = (mFrame.width() != mSnapshotW || mFrame.height() != mSnapshotH);
157             mSystemBarBackgroundPainter.setInsets(systemBarInsets);
158         }
159 
drawSnapshot(boolean releaseAfterDraw)160         private void drawSnapshot(boolean releaseAfterDraw) {
161             Log.v(TAG, "Drawing snapshot surface sizeMismatch=" + mSizeMismatch);
162             if (mSizeMismatch) {
163                 // The dimensions of the buffer and the window don't match, so attaching the buffer
164                 // will fail. Better create a child window with the exact dimensions and fill the
165                 // parent window with the background color!
166                 drawSizeMismatchSnapshot();
167             } else {
168                 drawSizeMatchSnapshot();
169             }
170 
171             // In case window manager leaks us, make sure we don't retain the snapshot.
172             if (mSnapshot.getHardwareBuffer() != null) {
173                 mSnapshot.getHardwareBuffer().close();
174             }
175             if (releaseAfterDraw) {
176                 mRootSurface.release();
177             }
178         }
179 
drawSizeMatchSnapshot()180         private void drawSizeMatchSnapshot() {
181             mTransaction.setBuffer(mRootSurface, mSnapshot.getHardwareBuffer())
182                     .setColorSpace(mRootSurface, mSnapshot.getColorSpace())
183                     .apply();
184         }
185 
drawSizeMismatchSnapshot()186         private void drawSizeMismatchSnapshot() {
187             final HardwareBuffer buffer = mSnapshot.getHardwareBuffer();
188             final SurfaceSession session = new SurfaceSession();
189 
190             // We consider nearly matched dimensions as there can be rounding errors and the user
191             // won't notice very minute differences from scaling one dimension more than the other
192             boolean aspectRatioMismatch = !isAspectRatioMatch(mFrame, mSnapshotW, mSnapshotH)
193                     && !Flags.drawSnapshotAspectRatioMatch();
194 
195             // Keep a reference to it such that it doesn't get destroyed when finalized.
196             SurfaceControl childSurfaceControl = new SurfaceControl.Builder(session)
197                     .setName(mTitle + " - task-snapshot-surface")
198                     .setBLASTLayer()
199                     .setFormat(buffer.getFormat())
200                     .setParent(mRootSurface)
201                     .setCallsite("TaskSnapshotWindow.drawSizeMismatchSnapshot")
202                     .build();
203 
204             final Rect frame;
205             final Rect letterboxInsets = mSnapshot.getLetterboxInsets();
206             float offsetX = letterboxInsets.left;
207             float offsetY = letterboxInsets.top;
208             // We can just show the surface here as it will still be hidden as the parent is
209             // still hidden.
210             mTransaction.show(childSurfaceControl);
211             if (aspectRatioMismatch) {
212                 Rect crop = null;
213                 if (letterboxInsets.left != 0 || letterboxInsets.top != 0
214                         || letterboxInsets.right != 0 || letterboxInsets.bottom != 0) {
215                     // Clip off letterbox.
216                     crop = calculateSnapshotCrop(letterboxInsets);
217                     // If the snapshot can cover the frame, then no need to draw background.
218                     aspectRatioMismatch = !isAspectRatioMatch(mFrame, crop);
219                 }
220                 // if letterbox doesn't match window frame, try crop by content insets
221                 if (aspectRatioMismatch) {
222                     // Clip off ugly navigation bar.
223                     final Rect contentInsets = mSnapshot.getContentInsets();
224                     crop = calculateSnapshotCrop(contentInsets);
225                     offsetX = contentInsets.left;
226                     offsetY = contentInsets.top;
227                 }
228                 frame = calculateSnapshotFrame(crop);
229                 mTransaction.setCrop(childSurfaceControl, crop);
230             } else {
231                 frame = null;
232             }
233 
234             // Align the snapshot with content area.
235             if (offsetX != 0f || offsetY != 0f) {
236                 mTransaction.setPosition(childSurfaceControl,
237                         -offsetX * mFrame.width() / mSnapshot.getTaskSize().x,
238                         -offsetY * mFrame.height() / mSnapshot.getTaskSize().y);
239             }
240             // Scale the mismatch dimensions to fill the target frame.
241             final float scaleX = (float) mFrame.width() / mSnapshotW;
242             final float scaleY = (float) mFrame.height() / mSnapshotH;
243             mTransaction.setScale(childSurfaceControl, scaleX, scaleY);
244             mTransaction.setColorSpace(childSurfaceControl, mSnapshot.getColorSpace());
245             mTransaction.setBuffer(childSurfaceControl, mSnapshot.getHardwareBuffer());
246 
247             if (aspectRatioMismatch) {
248                 GraphicBuffer background = GraphicBuffer.create(mFrame.width(), mFrame.height(),
249                         PixelFormat.RGBA_8888,
250                         GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_HW_COMPOSER
251                                 | GraphicBuffer.USAGE_SW_WRITE_RARELY);
252                 final Canvas c = background != null ? background.lockCanvas() : null;
253                 if (c == null) {
254                     Log.e(TAG, "Unable to draw snapshot: failed to allocate graphic buffer for "
255                             + mTitle);
256                     mTransaction.clear();
257                     childSurfaceControl.release();
258                     return;
259                 }
260                 drawBackgroundAndBars(c, frame);
261                 background.unlockCanvasAndPost(c);
262                 mTransaction.setBuffer(mRootSurface,
263                         HardwareBuffer.createFromGraphicBuffer(background));
264             }
265             mTransaction.apply();
266             childSurfaceControl.release();
267         }
268 
269         /**
270          * Calculates the snapshot crop in snapshot coordinate space.
271          * @param insets Content insets or Letterbox insets
272          * @return crop rect in snapshot coordinate space.
273          */
274         @VisibleForTesting
calculateSnapshotCrop(@onNull Rect insets)275         public Rect calculateSnapshotCrop(@NonNull Rect insets) {
276             final Rect rect = new Rect();
277             rect.set(0, 0, mSnapshotW, mSnapshotH);
278 
279             final float scaleX = (float) mSnapshotW / mSnapshot.getTaskSize().x;
280             final float scaleY = (float) mSnapshotH / mSnapshot.getTaskSize().y;
281 
282             // Let's remove all system decorations except the status bar, but only if the task is at
283             // the very top of the screen.
284             final boolean isTop = mFrame.top == 0;
285             rect.inset((int) (insets.left * scaleX),
286                     isTop ? 0 : (int) (insets.top * scaleY),
287                     (int) (insets.right * scaleX),
288                     (int) (insets.bottom * scaleY));
289             return rect;
290         }
291 
292         /**
293          * Calculates the snapshot frame in window coordinate space from crop.
294          *
295          * @param crop rect that is in snapshot coordinate space.
296          */
297         @VisibleForTesting
calculateSnapshotFrame(Rect crop)298         public Rect calculateSnapshotFrame(Rect crop) {
299             final float scaleX = (float) mSnapshotW / mSnapshot.getTaskSize().x;
300             final float scaleY = (float) mSnapshotH / mSnapshot.getTaskSize().y;
301 
302             // Rescale the frame from snapshot to window coordinate space
303             final Rect frame = new Rect(0, 0,
304                     (int) (crop.width() / scaleX + 0.5f),
305                     (int) (crop.height() / scaleY + 0.5f)
306             );
307 
308             // However, we also need to make space for the navigation bar on the left side.
309             frame.offset(mSystemBarInsets.left, 0);
310             return frame;
311         }
312 
313         /**
314          * Draw status bar and navigation bar background.
315          */
316         @VisibleForTesting
drawBackgroundAndBars(Canvas c, Rect frame)317         public void drawBackgroundAndBars(Canvas c, Rect frame) {
318             final int statusBarHeight = mSystemBarBackgroundPainter.getStatusBarColorViewHeight();
319             final boolean fillHorizontally = c.getWidth() > frame.right;
320             final boolean fillVertically = c.getHeight() > frame.bottom;
321             if (fillHorizontally) {
322                 c.drawRect(frame.right, alpha(mSystemBarBackgroundPainter.mStatusBarColor) == 0xFF
323                         ? statusBarHeight : 0, c.getWidth(), fillVertically
324                         ? frame.bottom : c.getHeight(), sBackgroundPaint);
325             }
326             if (fillVertically) {
327                 c.drawRect(0, frame.bottom, c.getWidth(), c.getHeight(), sBackgroundPaint);
328             }
329             mSystemBarBackgroundPainter.drawDecors(c, frame);
330         }
331 
332         /**
333          * Ask system bar background painter to draw status bar background.
334          */
335         @VisibleForTesting
drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame)336         public void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame) {
337             mSystemBarBackgroundPainter.drawStatusBarBackground(c, alreadyDrawnFrame,
338                     mSystemBarBackgroundPainter.getStatusBarColorViewHeight());
339         }
340 
341         /**
342          * Ask system bar background painter to draw navigation bar background.
343          */
344         @VisibleForTesting
drawNavigationBarBackground(Canvas c)345         public void drawNavigationBarBackground(Canvas c) {
346             mSystemBarBackgroundPainter.drawNavigationBarBackground(c);
347         }
348     }
349 
isAspectRatioMatch(Rect frame, int w, int h)350     private static boolean isAspectRatioMatch(Rect frame, int w, int h) {
351         if (frame.isEmpty()) {
352             return false;
353         }
354         return Math.abs(((float) w / h) - ((float) frame.width() / frame.height())) <= 0.01f;
355     }
356 
isAspectRatioMatch(Rect frame1, Rect frame2)357     private static boolean isAspectRatioMatch(Rect frame1, Rect frame2) {
358         if (frame1.isEmpty() || frame2.isEmpty()) {
359             return false;
360         }
361         return Math.abs(
362                 ((float) frame2.width() / frame2.height())
363                         - ((float) frame1.width() / frame1.height())) <= 0.01f;
364     }
365 
366     /**
367      * Get or create a TaskDescription from a RunningTaskInfo.
368      */
getOrCreateTaskDescription( ActivityManager.RunningTaskInfo runningTaskInfo)369     public static ActivityManager.TaskDescription getOrCreateTaskDescription(
370             ActivityManager.RunningTaskInfo runningTaskInfo) {
371         final ActivityManager.TaskDescription taskDescription;
372         if (runningTaskInfo.taskDescription != null) {
373             taskDescription = runningTaskInfo.taskDescription;
374         } else {
375             taskDescription = new ActivityManager.TaskDescription();
376             taskDescription.setBackgroundColor(WHITE);
377         }
378         return taskDescription;
379     }
380 
381     /**
382      * Help method to draw the snapshot on a surface.
383      */
drawSnapshotOnSurface(StartingWindowInfo info, WindowManager.LayoutParams lp, SurfaceControl rootSurface, TaskSnapshot snapshot, Rect windowBounds, InsetsState topWindowInsetsState, boolean releaseAfterDraw)384     public static void drawSnapshotOnSurface(StartingWindowInfo info, WindowManager.LayoutParams lp,
385             SurfaceControl rootSurface, TaskSnapshot snapshot,
386             Rect windowBounds, InsetsState topWindowInsetsState,
387             boolean releaseAfterDraw) {
388         if (windowBounds.isEmpty()) {
389             Log.e(TAG, "Unable to draw snapshot on an empty windowBounds");
390             return;
391         }
392         final SnapshotSurface drawSurface = new SnapshotSurface(
393                 rootSurface, snapshot, lp.getTitle());
394         final WindowManager.LayoutParams attrs = Flags.drawSnapshotAspectRatioMatch()
395                 ? info.mainWindowLayoutParams : info.topOpaqueWindowLayoutParams;
396         final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo;
397         final ActivityManager.TaskDescription taskDescription =
398                 getOrCreateTaskDescription(runningTaskInfo);
399         drawSurface.initiateSystemBarPainter(lp.flags, lp.privateFlags,
400                 attrs.insetsFlags.appearance, taskDescription, info.requestedVisibleTypes);
401         final Rect systemBarInsets = getSystemBarInsets(windowBounds, topWindowInsetsState);
402         drawSurface.setFrames(windowBounds, systemBarInsets);
403         drawSurface.drawSnapshot(releaseAfterDraw);
404     }
405 
406     /**
407      * Help method to create a layout parameters for a window.
408      */
createLayoutParameters(StartingWindowInfo info, CharSequence title, @WindowManager.LayoutParams.WindowType int windowType, int pixelFormat, IBinder token)409     public static WindowManager.LayoutParams createLayoutParameters(StartingWindowInfo info,
410             CharSequence title, @WindowManager.LayoutParams.WindowType int windowType,
411             int pixelFormat, IBinder token) {
412         final WindowManager.LayoutParams attrs = Flags.drawSnapshotAspectRatioMatch()
413                 ? info.mainWindowLayoutParams : info.topOpaqueWindowLayoutParams;
414         final WindowManager.LayoutParams mainWindowParams = info.mainWindowLayoutParams;
415         final InsetsState topWindowInsetsState = info.topOpaqueWindowInsetsState;
416         if (attrs == null || mainWindowParams == null || topWindowInsetsState == null) {
417             Log.w(TAG, "unable to create taskSnapshot surface ");
418             return null;
419         }
420         final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
421 
422         final int appearance = attrs.insetsFlags.appearance;
423         final int windowFlags = attrs.flags;
424         final int windowPrivateFlags = attrs.privateFlags;
425 
426         layoutParams.packageName = mainWindowParams.packageName;
427         layoutParams.windowAnimations = mainWindowParams.windowAnimations;
428         layoutParams.dimAmount = mainWindowParams.dimAmount;
429         layoutParams.type = windowType;
430         layoutParams.format = pixelFormat;
431         layoutParams.flags = (windowFlags & ~FLAG_INHERIT_EXCLUDES)
432                 | FLAG_NOT_FOCUSABLE
433                 | FLAG_NOT_TOUCHABLE;
434         layoutParams.privateFlags =
435                 (windowPrivateFlags
436                         & (PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS
437                         | PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED))
438                 // Setting as trusted overlay to let touches pass through. This is safe because this
439                 // window is controlled by the system.
440                 | PRIVATE_FLAG_TRUSTED_OVERLAY;
441         layoutParams.token = token;
442         layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
443         layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
444         layoutParams.insetsFlags.appearance = appearance;
445         layoutParams.insetsFlags.behavior = attrs.insetsFlags.behavior;
446         layoutParams.layoutInDisplayCutoutMode = attrs.layoutInDisplayCutoutMode;
447         layoutParams.setFitInsetsTypes(attrs.getFitInsetsTypes());
448         layoutParams.setFitInsetsSides(attrs.getFitInsetsSides());
449         layoutParams.setFitInsetsIgnoringVisibility(attrs.isFitInsetsIgnoringVisibility());
450         if (sToolkitSetFrameRateReadOnlyFlagValue) {
451             layoutParams.setFrameRatePowerSavingsBalanced(false);
452         }
453 
454         layoutParams.setTitle(title);
455         layoutParams.inputFeatures |= INPUT_FEATURE_NO_INPUT_CHANNEL;
456         return layoutParams;
457     }
458 
getSystemBarInsets(Rect frame, InsetsState state)459     static Rect getSystemBarInsets(Rect frame, InsetsState state) {
460         return state.calculateInsets(frame, WindowInsets.Type.systemBars(),
461                 false /* ignoreVisibility */).toRect();
462     }
463 
464     /**
465      * Helper class to draw the background of the system bars in regions the task snapshot isn't
466      * filling the window.
467      */
468     public static class SystemBarBackgroundPainter {
469         private final Paint mStatusBarPaint = new Paint();
470         private final Paint mNavigationBarPaint = new Paint();
471         private final int mStatusBarColor;
472         private final int mNavigationBarColor;
473         private final int mWindowFlags;
474         private final int mWindowPrivateFlags;
475         private final float mScale;
476         private final @WindowInsets.Type.InsetsType int mRequestedVisibleTypes;
477         private final Rect mSystemBarInsets = new Rect();
478 
SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int appearance, ActivityManager.TaskDescription taskDescription, float scale, @WindowInsets.Type.InsetsType int requestedVisibleTypes)479         public SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int appearance,
480                 ActivityManager.TaskDescription taskDescription, float scale,
481                 @WindowInsets.Type.InsetsType int requestedVisibleTypes) {
482             mWindowFlags = windowFlags;
483             mWindowPrivateFlags = windowPrivateFlags;
484             mScale = scale;
485             final Context context = ActivityThread.currentActivityThread().getSystemUiContext();
486             final int semiTransparent = context.getColor(
487                     R.color.system_bar_background_semi_transparent);
488             mStatusBarColor = DecorView.calculateBarColor(windowFlags, FLAG_TRANSLUCENT_STATUS,
489                     semiTransparent, taskDescription.getStatusBarColor(), appearance,
490                     APPEARANCE_LIGHT_STATUS_BARS,
491                     taskDescription.getEnsureStatusBarContrastWhenTransparent(),
492                     false /* movesBarColorToScrim */);
493             mNavigationBarColor = DecorView.calculateBarColor(windowFlags,
494                     FLAG_TRANSLUCENT_NAVIGATION, semiTransparent,
495                     taskDescription.getNavigationBarColor(), appearance,
496                     APPEARANCE_LIGHT_NAVIGATION_BARS,
497                     taskDescription.getEnsureNavigationBarContrastWhenTransparent()
498                             && context.getResources().getBoolean(
499                             R.bool.config_navBarNeedsScrim),
500                     (windowPrivateFlags & PRIVATE_FLAG_EDGE_TO_EDGE_ENFORCED) != 0);
501             mStatusBarPaint.setColor(mStatusBarColor);
502             mNavigationBarPaint.setColor(mNavigationBarColor);
503             mRequestedVisibleTypes = requestedVisibleTypes;
504         }
505 
506         /**
507          * Set system bar insets.
508          */
setInsets(Rect systemBarInsets)509         public void setInsets(Rect systemBarInsets) {
510             mSystemBarInsets.set(systemBarInsets);
511         }
512 
getStatusBarColorViewHeight()513         int getStatusBarColorViewHeight() {
514             final boolean forceBarBackground =
515                     (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
516             if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
517                     mRequestedVisibleTypes, mStatusBarColor, mWindowFlags,
518                     forceBarBackground)) {
519                 return (int) (mSystemBarInsets.top * mScale);
520             } else {
521                 return 0;
522             }
523         }
524 
isNavigationBarColorViewVisible()525         private boolean isNavigationBarColorViewVisible() {
526             final boolean forceBarBackground =
527                     (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
528             return NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
529                     mRequestedVisibleTypes, mNavigationBarColor, mWindowFlags,
530                     forceBarBackground);
531         }
532 
533         /**
534          * Draw bar colors to a canvas.
535          */
drawDecors(Canvas c, @Nullable Rect alreadyDrawnFrame)536         public void drawDecors(Canvas c, @Nullable Rect alreadyDrawnFrame) {
537             drawStatusBarBackground(c, alreadyDrawnFrame, getStatusBarColorViewHeight());
538             drawNavigationBarBackground(c);
539         }
540 
drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame, int statusBarHeight)541         void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame,
542                 int statusBarHeight) {
543             if (statusBarHeight > 0 && alpha(mStatusBarColor) != 0
544                     && (alreadyDrawnFrame == null || c.getWidth() > alreadyDrawnFrame.right)) {
545                 final int rightInset = (int) (mSystemBarInsets.right * mScale);
546                 final int left = alreadyDrawnFrame != null ? alreadyDrawnFrame.right : 0;
547                 c.drawRect(left, 0, c.getWidth() - rightInset, statusBarHeight,
548                         mStatusBarPaint);
549             }
550         }
551 
drawNavigationBarBackground(Canvas c)552         void drawNavigationBarBackground(Canvas c) {
553             final Rect navigationBarRect = new Rect();
554             getNavigationBarRect(c.getWidth(), c.getHeight(), mSystemBarInsets, navigationBarRect,
555                     mScale);
556             final boolean visible = isNavigationBarColorViewVisible();
557             if (visible && alpha(mNavigationBarColor) != 0
558                     && !navigationBarRect.isEmpty()) {
559                 c.drawRect(navigationBarRect, mNavigationBarPaint);
560             }
561         }
562     }
563 }
564