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