content: /*
   * Copyright (C) 2016 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License
   */
  
  package com.android.server.wm;
  
  import static android.graphics.Color.WHITE;
  import static android.graphics.Color.alpha;
  import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
  import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
  import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
  import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
  import static android.view.WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
  import static android.view.WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE;
  import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
  import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
  import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
  import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
  import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
  import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
  import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
  import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
  import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
  import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
  import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
  import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
  
  import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
  import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
  import static com.android.internal.policy.DecorView.getColorViewLeftInset;
  import static com.android.internal.policy.DecorView.getColorViewTopInset;
  import static com.android.internal.policy.DecorView.getNavigationBarRect;
  import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
  import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
  import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
  
  import android.annotation.Nullable;
  import android.app.ActivityManager.TaskDescription;
  import android.app.ActivityManager.TaskSnapshot;
  import android.app.ActivityThread;
  import android.content.Context;
  import android.graphics.Canvas;
  import android.graphics.Color;
  import android.graphics.GraphicBuffer;
  import android.graphics.Paint;
  import android.graphics.Rect;
  import android.os.Handler;
  import android.os.Looper;
  import android.os.Message;
  import android.os.RemoteException;
  import android.os.SystemClock;
  import android.util.MergedConfiguration;
  import android.util.Slog;
  import android.view.DisplayCutout;
  import android.view.IWindowSession;
  import android.view.InsetsState;
  import android.view.Surface;
  import android.view.SurfaceControl;
  import android.view.SurfaceSession;
  import android.view.View;
  import android.view.ViewGroup.LayoutParams;
  import android.view.WindowManager;
  import android.view.WindowManagerGlobal;
  
  import com.android.internal.R;
  import com.android.internal.annotations.VisibleForTesting;
  import com.android.internal.policy.DecorView;
  import com.android.internal.view.BaseIWindow;
  import com.android.server.policy.WindowManagerPolicy.StartingSurface;
  
  /**
   * This class represents a starting window that shows a snapshot.
   * <p>
   * DO NOT HOLD THE WINDOW MANAGER LOCK WHEN CALLING METHODS OF THIS CLASS!
   */
  class TaskSnapshotSurface implements StartingSurface {
  
      private static final long SIZE_MISMATCH_MINIMUM_TIME_MS = 450;
  
      /**
       * When creating the starting window, we use the exact same layout flags such that we end up
       * with a window with the exact same dimensions etc. However, these flags are not used in layout
       * and might cause other side effects so we exclude them.
       */
      private static final int FLAG_INHERIT_EXCLUDES = FLAG_NOT_FOCUSABLE
              | FLAG_NOT_TOUCHABLE
              | FLAG_NOT_TOUCH_MODAL
              | FLAG_ALT_FOCUSABLE_IM
              | FLAG_NOT_FOCUSABLE
              | FLAG_HARDWARE_ACCELERATED
              | FLAG_IGNORE_CHEEK_PRESSES
              | FLAG_LOCAL_FOCUS_MODE
              | FLAG_SLIPPERY
              | FLAG_WATCH_OUTSIDE_TOUCH
              | FLAG_SPLIT_TOUCH
              | FLAG_SCALED
              | FLAG_SECURE;
  
      private static final int PRIVATE_FLAG_INHERITS = PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
  
      private static final String TAG = TAG_WITH_CLASS_NAME ? "SnapshotStartingWindow" : TAG_WM;
      private static final int MSG_REPORT_DRAW = 0;
      private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s";
      private final Window mWindow;
      private final Surface mSurface;
      private SurfaceControl mSurfaceControl;
      private SurfaceControl mChildSurfaceControl;
      private final IWindowSession mSession;
      private final WindowManagerService mService;
      private final Rect mTaskBounds;
      private final Rect mStableInsets = new Rect();
      private final Rect mContentInsets = new Rect();
      private final Rect mFrame = new Rect();
      private TaskSnapshot mSnapshot;
      private final CharSequence mTitle;
      private boolean mHasDrawn;
      private long mShownTime;
      private final Handler mHandler;
      private boolean mSizeMismatch;
      private final Paint mBackgroundPaint = new Paint();
      private final int mStatusBarColor;
      @VisibleForTesting final SystemBarBackgroundPainter mSystemBarBackgroundPainter;
      private final int mOrientationOnCreation;
  
      static TaskSnapshotSurface create(WindowManagerService service, AppWindowToken token,
              TaskSnapshot snapshot) {
  
          final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
          final Window window = new Window();
          final IWindowSession session = WindowManagerGlobal.getWindowSession();
          window.setSession(session);
          final SurfaceControl surfaceControl = new SurfaceControl();
          final Rect tmpRect = new Rect();
          final DisplayCutout.ParcelableWrapper tmpCutout = new DisplayCutout.ParcelableWrapper();
          final Rect tmpFrame = new Rect();
          final Rect taskBounds;
          final Rect tmpContentInsets = new Rect();
          final Rect tmpStableInsets = new Rect();
          final InsetsState mTmpInsetsState = new InsetsState();
          final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
          final TaskDescription taskDescription = new TaskDescription();
          taskDescription.setBackgroundColor(WHITE);
          final int sysUiVis;
          final int windowFlags;
          final int windowPrivateFlags;
          final int currentOrientation;
          synchronized (service.mGlobalLock) {
              final WindowState mainWindow = token.findMainWindow();
              final Task task = token.getTask();
              if (task == null) {
                  Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find task for token="
                          + token);
                  return null;
              }
              final AppWindowToken topFullscreenToken = token.getTask().getTopFullscreenAppToken();
              if (topFullscreenToken == null) {
                  Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find top fullscreen for task="
                          + task);
                  return null;
              }
              final WindowState topFullscreenWindow = topFullscreenToken.getTopFullscreenWindow();
              if (mainWindow == null || topFullscreenWindow == null) {
                  Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find main window for token="
                          + token);
                  return null;
              }
              sysUiVis = topFullscreenWindow.getSystemUiVisibility();
              windowFlags = topFullscreenWindow.getAttrs().flags;
              windowPrivateFlags = topFullscreenWindow.getAttrs().privateFlags;
  
              layoutParams.packageName = mainWindow.getAttrs().packageName;
              layoutParams.windowAnimations = mainWindow.getAttrs().windowAnimations;
              layoutParams.dimAmount = mainWindow.getAttrs().dimAmount;
              layoutParams.type = TYPE_APPLICATION_STARTING;
              layoutParams.format = snapshot.getSnapshot().getFormat();
              layoutParams.flags = (windowFlags & ~FLAG_INHERIT_EXCLUDES)
                      | FLAG_NOT_FOCUSABLE
                      | FLAG_NOT_TOUCHABLE;
              layoutParams.privateFlags = windowPrivateFlags & PRIVATE_FLAG_INHERITS;
              layoutParams.token = token.token;
              layoutParams.width = LayoutParams.MATCH_PARENT;
              layoutParams.height = LayoutParams.MATCH_PARENT;
              layoutParams.systemUiVisibility = sysUiVis;
              layoutParams.setTitle(String.format(TITLE_FORMAT, task.mTaskId));
  
              final TaskDescription td = task.getTaskDescription();
              if (td != null) {
                  taskDescription.copyFrom(td);
              }
              taskBounds = new Rect();
              task.getBounds(taskBounds);
              currentOrientation = topFullscreenWindow.getConfiguration().orientation;
          }
          try {
              final int res = session.addToDisplay(window, window.mSeq, layoutParams,
                      View.GONE, token.getDisplayContent().getDisplayId(), tmpFrame, tmpRect, tmpRect,
                      tmpRect, tmpCutout, null, mTmpInsetsState);
              if (res < 0) {
                  Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
                  return null;
              }
          } catch (RemoteException e) {
              // Local call.
          }
          final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window,
                  surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, sysUiVis,
                  windowFlags, windowPrivateFlags, taskBounds,
                  currentOrientation);
          window.setOuter(snapshotSurface);
          try {
              session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1,
                      tmpFrame, tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect,
                      tmpCutout, tmpMergedConfiguration, surfaceControl, mTmpInsetsState);
          } catch (RemoteException e) {
              // Local call.
          }
          snapshotSurface.setFrames(tmpFrame, tmpContentInsets, tmpStableInsets);
          snapshotSurface.drawSnapshot();
          return snapshotSurface;
      }
  
      @VisibleForTesting
      TaskSnapshotSurface(WindowManagerService service, Window window, SurfaceControl surfaceControl,
              TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription,
              int sysUiVis, int windowFlags, int windowPrivateFlags, Rect taskBounds,
              int currentOrientation) {
          mService = service;
          mSurface = new Surface();
          mHandler = new Handler(mService.mH.getLooper());
          mSession = WindowManagerGlobal.getWindowSession();
          mWindow = window;
          mSurfaceControl = surfaceControl;
          mSnapshot = snapshot;
          mTitle = title;
          int backgroundColor = taskDescription.getBackgroundColor();
          mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
          mTaskBounds = taskBounds;
          mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags,
                  windowPrivateFlags, sysUiVis, taskDescription, 1f);
          mStatusBarColor = taskDescription.getStatusBarColor();
          mOrientationOnCreation = currentOrientation;
      }
  
      @Override
      public void remove() {
          synchronized (mService.mGlobalLock) {
              final long now = SystemClock.uptimeMillis();
              if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS) {
                  mHandler.postAtTime(this::remove, mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS);
                  if (DEBUG_STARTING_WINDOW) {
                      Slog.v(TAG, "Defer removing snapshot surface in "  + (now - mShownTime) + "ms");
                  }
                  return;
              }
          }
          try {
              if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Removing snapshot surface");
              mSession.remove(mWindow);
          } catch (RemoteException e) {
              // Local call.
          }
      }
  
      @VisibleForTesting
      void setFrames(Rect frame, Rect contentInsets, Rect stableInsets) {
          mFrame.set(frame);
          mContentInsets.set(contentInsets);
          mStableInsets.set(stableInsets);
          mSizeMismatch = (mFrame.width() != mSnapshot.getSnapshot().getWidth()
                  || mFrame.height() != mSnapshot.getSnapshot().getHeight());
          mSystemBarBackgroundPainter.setInsets(contentInsets, stableInsets);
      }
  
      private void drawSnapshot() {
          mSurface.copyFrom(mSurfaceControl);
  
          if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Drawing snapshot surface sizeMismatch="
                  + mSizeMismatch);
          if (mSizeMismatch) {
              // The dimensions of the buffer and the window don't match, so attaching the buffer
              // will fail. Better create a child window with the exact dimensions and fill the parent
              // window with the background color!
              drawSizeMismatchSnapshot();
          } else {
              drawSizeMatchSnapshot();
          }
          synchronized (mService.mGlobalLock) {
              mShownTime = SystemClock.uptimeMillis();
              mHasDrawn = true;
          }
          reportDrawn();
  
          // In case window manager leaks us, make sure we don't retain the snapshot.
          mSnapshot = null;
      }
  
      private void drawSizeMatchSnapshot() {
          mSurface.attachAndQueueBufferWithColorSpace(mSnapshot.getSnapshot(),
                  mSnapshot.getColorSpace());
          mSurface.release();
      }
  
      private void drawSizeMismatchSnapshot() {
          if (!mSurface.isValid()) {
              throw new IllegalStateException("mSurface does not hold a valid surface.");
          }
          final GraphicBuffer buffer = mSnapshot.getSnapshot();
          final SurfaceSession session = new SurfaceSession();
          // We consider nearly matched dimensions as there can be rounding errors and the user won't
          // notice very minute differences from scaling one dimension more than the other
          final boolean aspectRatioMismatch = Math.abs(
                  ((float) buffer.getWidth() / buffer.getHeight())
                  - ((float) mFrame.width() / mFrame.height())) > 0.01f;
  
          // Keep a reference to it such that it doesn't get destroyed when finalized.
          mChildSurfaceControl = new SurfaceControl.Builder(session)
                  .setName(mTitle + " - task-snapshot-surface")
                  .setBufferSize(buffer.getWidth(), buffer.getHeight())
                  .setFormat(buffer.getFormat())
                  .setParent(mSurfaceControl)
                  .build();
          Surface surface = new Surface();
          surface.copyFrom(mChildSurfaceControl);
  
          final Rect frame;
          SurfaceControl.openTransaction();
          try {
              // We can just show the surface here as it will still be hidden as the parent is
              // still hidden.
              mChildSurfaceControl.show();
              if (aspectRatioMismatch) {
                  // Clip off ugly navigation bar.
                  final Rect crop = calculateSnapshotCrop();
                  frame = calculateSnapshotFrame(crop);
                  mChildSurfaceControl.setWindowCrop(crop);
                  mChildSurfaceControl.setPosition(frame.left, frame.top);
              } else {
                  frame = null;
              }
  
              // Scale the mismatch dimensions to fill the task bounds
              final float scale = 1 / mSnapshot.getScale();
              mChildSurfaceControl.setMatrix(scale, 0, 0, scale);
          } finally {
              SurfaceControl.closeTransaction();
          }
          surface.attachAndQueueBufferWithColorSpace(buffer, mSnapshot.getColorSpace());
          surface.release();
  
          if (aspectRatioMismatch) {
              final Canvas c = mSurface.lockCanvas(null);
              drawBackgroundAndBars(c, frame);
              mSurface.unlockCanvasAndPost(c);
              mSurface.release();
          }
      }
  
      /**
       * Calculates the snapshot crop in snapshot coordinate space.
       *
       * @return crop rect in snapshot coordinate space.
       */
      @VisibleForTesting
      Rect calculateSnapshotCrop() {
          final Rect rect = new Rect();
          rect.set(0, 0, mSnapshot.getSnapshot().getWidth(), mSnapshot.getSnapshot().getHeight());
          final Rect insets = mSnapshot.getContentInsets();
  
          // Let's remove all system decorations except the status bar, but only if the task is at the
          // very top of the screen.
          final boolean isTop = mTaskBounds.top == 0 && mFrame.top == 0;
          rect.inset((int) (insets.left * mSnapshot.getScale()),
                  isTop ? 0 : (int) (insets.top * mSnapshot.getScale()),
                  (int) (insets.right * mSnapshot.getScale()),
                  (int) (insets.bottom * mSnapshot.getScale()));
          return rect;
      }
  
      /**
       * Calculates the snapshot frame in window coordinate space from crop.
       *
       * @param crop rect that is in snapshot coordinate space.
       */
      @VisibleForTesting
      Rect calculateSnapshotFrame(Rect crop) {
          final Rect frame = new Rect(crop);
          final float scale = mSnapshot.getScale();
  
          // Rescale the frame from snapshot to window coordinate space
          frame.scale(1 / scale);
  
          // By default, offset it to to top/left corner
          frame.offsetTo((int) (-crop.left / scale), (int) (-crop.top / scale));
  
          // However, we also need to make space for the navigation bar on the left side.
          final int colorViewLeftInset = getColorViewLeftInset(mStableInsets.left,
                  mContentInsets.left);
          frame.offset(colorViewLeftInset, 0);
          return frame;
      }
  
      @VisibleForTesting
      void drawBackgroundAndBars(Canvas c, Rect frame) {
          final int statusBarHeight = mSystemBarBackgroundPainter.getStatusBarColorViewHeight();
          final boolean fillHorizontally = c.getWidth() > frame.right;
          final boolean fillVertically = c.getHeight() > frame.bottom;
          if (fillHorizontally) {
              c.drawRect(frame.right, alpha(mStatusBarColor) == 0xFF ? statusBarHeight : 0,
                      c.getWidth(), fillVertically
                              ? frame.bottom
                              : c.getHeight(),
                      mBackgroundPaint);
          }
          if (fillVertically) {
              c.drawRect(0, frame.bottom, c.getWidth(), c.getHeight(), mBackgroundPaint);
          }
          mSystemBarBackgroundPainter.drawDecors(c, frame);
      }
  
      private void reportDrawn() {
          try {
              mSession.finishDrawing(mWindow);
          } catch (RemoteException e) {
              // Local call.
          }
      }
  
      private static Handler sHandler = new Handler(Looper.getMainLooper()) {
  
          @Override
          public void handleMessage(Message msg) {
              switch (msg.what) {
                  case MSG_REPORT_DRAW:
                      final boolean hasDrawn;
                      final TaskSnapshotSurface surface = (TaskSnapshotSurface) msg.obj;
                      synchronized (surface.mService.mGlobalLock) {
                          hasDrawn = surface.mHasDrawn;
                      }
                      if (hasDrawn) {
                          surface.reportDrawn();
                      }
                      break;
              }
          }
      };
  
      @VisibleForTesting
      static class Window extends BaseIWindow {
  
          private TaskSnapshotSurface mOuter;
  
          public void setOuter(TaskSnapshotSurface outer) {
              mOuter = outer;
          }
  
          @Override
          public void 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) {
              if (mergedConfiguration != null && mOuter != null
                      && mOuter.mOrientationOnCreation
                              != mergedConfiguration.getMergedConfiguration().orientation) {
  
                  // The orientation of the screen is changing. We better remove the snapshot ASAP as
                  // we are going to wait on the new window in any case to unfreeze the screen, and
                  // the starting window is not needed anymore.
                  sHandler.post(mOuter::remove);
              }
              if (reportDraw) {
                  sHandler.obtainMessage(MSG_REPORT_DRAW, mOuter).sendToTarget();
              }
          }
      }
  
      /**
       * Helper class to draw the background of the system bars in regions the task snapshot isn't
       * filling the window.
       */
      static class SystemBarBackgroundPainter {
  
          private final Rect mContentInsets = new Rect();
          private final Rect mStableInsets = new Rect();
          private final Paint mStatusBarPaint = new Paint();
          private final Paint mNavigationBarPaint = new Paint();
          private final int mStatusBarColor;
          private final int mNavigationBarColor;
          private final int mWindowFlags;
          private final int mWindowPrivateFlags;
          private final int mSysUiVis;
          private final float mScale;
  
          SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int sysUiVis,
                  TaskDescription taskDescription, float scale) {
              mWindowFlags = windowFlags;
              mWindowPrivateFlags = windowPrivateFlags;
              mSysUiVis = sysUiVis;
              mScale = scale;
              final Context context = ActivityThread.currentActivityThread().getSystemUiContext();
              final int semiTransparent = context.getColor(
                      R.color.system_bar_background_semi_transparent);
              mStatusBarColor = DecorView.calculateBarColor(windowFlags, FLAG_TRANSLUCENT_STATUS,
                      semiTransparent, taskDescription.getStatusBarColor(), sysUiVis,
                      SYSTEM_UI_FLAG_LIGHT_STATUS_BAR,
                      taskDescription.getEnsureStatusBarContrastWhenTransparent());
              mNavigationBarColor = DecorView.calculateBarColor(windowFlags,
                      FLAG_TRANSLUCENT_NAVIGATION, semiTransparent,
                      taskDescription.getNavigationBarColor(), sysUiVis,
                      SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
                      taskDescription.getEnsureNavigationBarContrastWhenTransparent()
                              && context.getResources().getBoolean(R.bool.config_navBarNeedsScrim));
              mStatusBarPaint.setColor(mStatusBarColor);
              mNavigationBarPaint.setColor(mNavigationBarColor);
          }
  
          void setInsets(Rect contentInsets, Rect stableInsets) {
              mContentInsets.set(contentInsets);
              mStableInsets.set(stableInsets);
          }
  
          int getStatusBarColorViewHeight() {
              final boolean forceBarBackground =
                      (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
              if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
                      mSysUiVis, mStatusBarColor, mWindowFlags, forceBarBackground)) {
                  return (int) (getColorViewTopInset(mStableInsets.top, mContentInsets.top) * mScale);
              } else {
                  return 0;
              }
          }
  
          private boolean isNavigationBarColorViewVisible() {
              final boolean forceBarBackground =
                      (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0;
              return NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible(
                      mSysUiVis, mNavigationBarColor, mWindowFlags, forceBarBackground);
          }
  
          void drawDecors(Canvas c, @Nullable Rect alreadyDrawnFrame) {
              drawStatusBarBackground(c, alreadyDrawnFrame, getStatusBarColorViewHeight());
              drawNavigationBarBackground(c);
          }
  
          @VisibleForTesting
          void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame,
                  int statusBarHeight) {
              if (statusBarHeight > 0 && Color.alpha(mStatusBarColor) != 0
                      && (alreadyDrawnFrame == null || c.getWidth() > alreadyDrawnFrame.right)) {
                  final int rightInset = (int) (DecorView.getColorViewRightInset(mStableInsets.right,
                          mContentInsets.right) * mScale);
                  final int left = alreadyDrawnFrame != null ? alreadyDrawnFrame.right : 0;
                  c.drawRect(left, 0, c.getWidth() - rightInset, statusBarHeight, mStatusBarPaint);
              }
          }
  
          @VisibleForTesting
          void drawNavigationBarBackground(Canvas c) {
              final Rect navigationBarRect = new Rect();
              getNavigationBarRect(c.getWidth(), c.getHeight(), mStableInsets, mContentInsets,
                      navigationBarRect, mScale);
              final boolean visible = isNavigationBarColorViewVisible();
              if (visible && Color.alpha(mNavigationBarColor) != 0 && !navigationBarRect.isEmpty()) {
                  c.drawRect(navigationBarRect, mNavigationBarPaint);
              }
          }
      }
  }
  