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         mSurface.copyFrom(mSurfaceControl);
288 
289         if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Drawing snapshot surface sizeMismatch="
290                 + mSizeMismatch);
291         if (mSizeMismatch) {
292             // The dimensions of the buffer and the window don't match, so attaching the buffer
293             // will fail. Better create a child window with the exact dimensions and fill the parent
294             // window with the background color!
295             drawSizeMismatchSnapshot();
296         } else {
297             drawSizeMatchSnapshot();
298         }
299         synchronized (mService.mGlobalLock) {
300             mShownTime = SystemClock.uptimeMillis();
301             mHasDrawn = true;
302         }
303         reportDrawn();
304 
305         // In case window manager leaks us, make sure we don't retain the snapshot.
306         mSnapshot = null;
307     }
308 
drawSizeMatchSnapshot()309     private void drawSizeMatchSnapshot() {
310         mSurface.attachAndQueueBufferWithColorSpace(mSnapshot.getSnapshot(),
311                 mSnapshot.getColorSpace());
312         mSurface.release();
313     }
314 
drawSizeMismatchSnapshot()315     private void drawSizeMismatchSnapshot() {
316         if (!mSurface.isValid()) {
317             throw new IllegalStateException("mSurface does not hold a valid surface.");
318         }
319         final GraphicBuffer buffer = mSnapshot.getSnapshot();
320         final SurfaceSession session = new SurfaceSession();
321         // We consider nearly matched dimensions as there can be rounding errors and the user won't
322         // notice very minute differences from scaling one dimension more than the other
323         final boolean aspectRatioMismatch = Math.abs(
324                 ((float) buffer.getWidth() / buffer.getHeight())
325                 - ((float) mFrame.width() / mFrame.height())) > 0.01f;
326 
327         // Keep a reference to it such that it doesn't get destroyed when finalized.
328         mChildSurfaceControl = new SurfaceControl.Builder(session)
329                 .setName(mTitle + " - task-snapshot-surface")
330                 .setBufferSize(buffer.getWidth(), buffer.getHeight())
331                 .setFormat(buffer.getFormat())
332                 .setParent(mSurfaceControl)
333                 .build();
334         Surface surface = new Surface();
335         surface.copyFrom(mChildSurfaceControl);
336 
337         final Rect frame;
338         SurfaceControl.openTransaction();
339         try {
340             // We can just show the surface here as it will still be hidden as the parent is
341             // still hidden.
342             mChildSurfaceControl.show();
343             if (aspectRatioMismatch) {
344                 // Clip off ugly navigation bar.
345                 final Rect crop = calculateSnapshotCrop();
346                 frame = calculateSnapshotFrame(crop);
347                 mChildSurfaceControl.setWindowCrop(crop);
348                 mChildSurfaceControl.setPosition(frame.left, frame.top);
349             } else {
350                 frame = null;
351             }
352 
353             // Scale the mismatch dimensions to fill the task bounds
354             final float scale = 1 / mSnapshot.getScale();
355             mChildSurfaceControl.setMatrix(scale, 0, 0, scale);
356         } finally {
357             SurfaceControl.closeTransaction();
358         }
359         surface.attachAndQueueBufferWithColorSpace(buffer, mSnapshot.getColorSpace());
360         surface.release();
361 
362         if (aspectRatioMismatch) {
363             final Canvas c = mSurface.lockCanvas(null);
364             drawBackgroundAndBars(c, frame);
365             mSurface.unlockCanvasAndPost(c);
366             mSurface.release();
367         }
368     }
369 
370     /**
371      * Calculates the snapshot crop in snapshot coordinate space.
372      *
373      * @return crop rect in snapshot coordinate space.
374      */
375     @VisibleForTesting
calculateSnapshotCrop()376     Rect calculateSnapshotCrop() {
377         final Rect rect = new Rect();
378         rect.set(0, 0, mSnapshot.getSnapshot().getWidth(), mSnapshot.getSnapshot().getHeight());
379         final Rect insets = mSnapshot.getContentInsets();
380 
381         // Let's remove all system decorations except the status bar, but only if the task is at the
382         // very top of the screen.
383         final boolean isTop = mTaskBounds.top == 0 && mFrame.top == 0;
384         rect.inset((int) (insets.left * mSnapshot.getScale()),
385                 isTop ? 0 : (int) (insets.top * mSnapshot.getScale()),
386                 (int) (insets.right * mSnapshot.getScale()),
387                 (int) (insets.bottom * mSnapshot.getScale()));
388         return rect;
389     }
390 
391     /**
392      * Calculates the snapshot frame in window coordinate space from crop.
393      *
394      * @param crop rect that is in snapshot coordinate space.
395      */
396     @VisibleForTesting
calculateSnapshotFrame(Rect crop)397     Rect calculateSnapshotFrame(Rect crop) {
398         final Rect frame = new Rect(crop);
399         final float scale = mSnapshot.getScale();
400 
401         // Rescale the frame from snapshot to window coordinate space
402         frame.scale(1 / scale);
403 
404         // By default, offset it to to top/left corner
405         frame.offsetTo((int) (-crop.left / scale), (int) (-crop.top / scale));
406 
407         // However, we also need to make space for the navigation bar on the left side.
408         final int colorViewLeftInset = getColorViewLeftInset(mStableInsets.left,
409                 mContentInsets.left);
410         frame.offset(colorViewLeftInset, 0);
411         return frame;
412     }
413 
414     @VisibleForTesting
drawBackgroundAndBars(Canvas c, Rect frame)415     void drawBackgroundAndBars(Canvas c, Rect frame) {
416         final int statusBarHeight = mSystemBarBackgroundPainter.getStatusBarColorViewHeight();
417         final boolean fillHorizontally = c.getWidth() > frame.right;
418         final boolean fillVertically = c.getHeight() > frame.bottom;
419         if (fillHorizontally) {
420             c.drawRect(frame.right, alpha(mStatusBarColor) == 0xFF ? statusBarHeight : 0,
421                     c.getWidth(), fillVertically
422                             ? frame.bottom
423                             : c.getHeight(),
424                     mBackgroundPaint);
425         }
426         if (fillVertically) {
427             c.drawRect(0, frame.bottom, c.getWidth(), c.getHeight(), mBackgroundPaint);
428         }
429         mSystemBarBackgroundPainter.drawDecors(c, frame);
430     }
431 
reportDrawn()432     private void reportDrawn() {
433         try {
434             mSession.finishDrawing(mWindow);
435         } catch (RemoteException e) {
436             // Local call.
437         }
438     }
439 
440     private static Handler sHandler = new Handler(Looper.getMainLooper()) {
441 
442         @Override
443         public void handleMessage(Message msg) {
444             switch (msg.what) {
445                 case MSG_REPORT_DRAW:
446                     final boolean hasDrawn;
447                     final TaskSnapshotSurface surface = (TaskSnapshotSurface) msg.obj;
448                     synchronized (surface.mService.mGlobalLock) {
449                         hasDrawn = surface.mHasDrawn;
450                     }
451                     if (hasDrawn) {
452                         surface.reportDrawn();
453                     }
454                     break;
455             }
456         }
457     };
458 
459     @VisibleForTesting
460     static class Window extends BaseIWindow {
461 
462         private TaskSnapshotSurface mOuter;
463 
setOuter(TaskSnapshotSurface outer)464         public void setOuter(TaskSnapshotSurface outer) {
465             mOuter = outer;
466         }
467 
468         @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)469         public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets,
470                 Rect stableInsets, Rect outsets, boolean reportDraw,
471                 MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
472                 boolean alwaysConsumeSystemBars, int displayId,
473                 DisplayCutout.ParcelableWrapper displayCutout) {
474             if (mergedConfiguration != null && mOuter != null
475                     && mOuter.mOrientationOnCreation
476                             != mergedConfiguration.getMergedConfiguration().orientation) {
477 
478                 // The orientation of the screen is changing. We better remove the snapshot ASAP as
479                 // we are going to wait on the new window in any case to unfreeze the screen, and
480                 // the starting window is not needed anymore.
481                 sHandler.post(mOuter::remove);
482             }
483             if (reportDraw) {
484                 sHandler.obtainMessage(MSG_REPORT_DRAW, mOuter).sendToTarget();
485             }
486         }
487     }
488 
489     /**
490      * Helper class to draw the background of the system bars in regions the task snapshot isn't
491      * filling the window.
492      */
493     static class SystemBarBackgroundPainter {
494 
495         private final Rect mContentInsets = new Rect();
496         private final Rect mStableInsets = new Rect();
497         private final Paint mStatusBarPaint = new Paint();
498         private final Paint mNavigationBarPaint = new Paint();
499         private final int mStatusBarColor;
500         private final int mNavigationBarColor;
501         private final int mWindowFlags;
502         private final int mWindowPrivateFlags;
503         private final int mSysUiVis;
504         private final float mScale;
505 
SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int sysUiVis, TaskDescription taskDescription, float scale)506         SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int sysUiVis,
507                 TaskDescription taskDescription, float scale) {
508             mWindowFlags = windowFlags;
509             mWindowPrivateFlags = windowPrivateFlags;
510             mSysUiVis = sysUiVis;
511             mScale = scale;
512             final Context context = ActivityThread.currentActivityThread().getSystemUiContext();
513             final int semiTransparent = context.getColor(
514                     R.color.system_bar_background_semi_transparent);
515             mStatusBarColor = DecorView.calculateBarColor(windowFlags, FLAG_TRANSLUCENT_STATUS,
516                     semiTransparent, taskDescription.getStatusBarColor(), sysUiVis,
517                     SYSTEM_UI_FLAG_LIGHT_STATUS_BAR,
518                     taskDescription.getEnsureStatusBarContrastWhenTransparent());
519             mNavigationBarColor = DecorView.calculateBarColor(windowFlags,
520                     FLAG_TRANSLUCENT_NAVIGATION, semiTransparent,
521                     taskDescription.getNavigationBarColor(), sysUiVis,
522                     SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
523                     taskDescription.getEnsureNavigationBarContrastWhenTransparent()
524                             && context.getResources().getBoolean(R.bool.config_navBarNeedsScrim));
525             mStatusBarPaint.setColor(mStatusBarColor);
526             mNavigationBarPaint.setColor(mNavigationBarColor);
527         }
528 
setInsets(Rect contentInsets, Rect stableInsets)529         void setInsets(Rect contentInsets, Rect stableInsets) {
530             mContentInsets.set(contentInsets);
531             mStableInsets.set(stableInsets);
532         }
533 
getStatusBarColorViewHeight()534         int getStatusBarColorViewHeight() {
535             final boolean forceBarBackground =
536                     (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
537             if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
538                     mSysUiVis, mStatusBarColor, mWindowFlags, forceBarBackground)) {
539                 return (int) (getColorViewTopInset(mStableInsets.top, mContentInsets.top) * mScale);
540             } else {
541                 return 0;
542             }
543         }
544 
isNavigationBarColorViewVisible()545         private boolean isNavigationBarColorViewVisible() {
546             final boolean forceBarBackground =
547                     (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
548             return NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
549                     mSysUiVis, mNavigationBarColor, mWindowFlags, forceBarBackground);
550         }
551 
drawDecors(Canvas c, @Nullable Rect alreadyDrawnFrame)552         void drawDecors(Canvas c, @Nullable Rect alreadyDrawnFrame) {
553             drawStatusBarBackground(c, alreadyDrawnFrame, getStatusBarColorViewHeight());
554             drawNavigationBarBackground(c);
555         }
556 
557         @VisibleForTesting
drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame, int statusBarHeight)558         void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame,
559                 int statusBarHeight) {
560             if (statusBarHeight > 0 && Color.alpha(mStatusBarColor) != 0
561                     && (alreadyDrawnFrame == null || c.getWidth() > alreadyDrawnFrame.right)) {
562                 final int rightInset = (int) (DecorView.getColorViewRightInset(mStableInsets.right,
563                         mContentInsets.right) * mScale);
564                 final int left = alreadyDrawnFrame != null ? alreadyDrawnFrame.right : 0;
565                 c.drawRect(left, 0, c.getWidth() - rightInset, statusBarHeight, mStatusBarPaint);
566             }
567         }
568 
569         @VisibleForTesting
drawNavigationBarBackground(Canvas c)570         void drawNavigationBarBackground(Canvas c) {
571             final Rect navigationBarRect = new Rect();
572             getNavigationBarRect(c.getWidth(), c.getHeight(), mStableInsets, mContentInsets,
573                     navigationBarRect, mScale);
574             final boolean visible = isNavigationBarColorViewVisible();
575             if (visible && Color.alpha(mNavigationBarColor) != 0 && !navigationBarRect.isEmpty()) {
576                 c.drawRect(navigationBarRect, mNavigationBarPaint);
577             }
578         }
579     }
580 }
581