1 /* 2 * Copyright (C) 2020 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.wm.shell.startingsurface; 18 19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; 20 import static android.graphics.Color.WHITE; 21 import static android.graphics.Color.alpha; 22 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; 23 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; 24 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; 25 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; 26 import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 27 import static android.view.WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES; 28 import static android.view.WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE; 29 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 30 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; 31 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; 32 import static android.view.WindowManager.LayoutParams.FLAG_SCALED; 33 import static android.view.WindowManager.LayoutParams.FLAG_SECURE; 34 import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; 35 import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; 36 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; 37 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; 38 import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; 39 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; 40 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; 41 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST; 42 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; 43 44 import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES; 45 import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES; 46 import static com.android.internal.policy.DecorView.getNavigationBarRect; 47 48 import android.annotation.BinderThread; 49 import android.annotation.NonNull; 50 import android.annotation.Nullable; 51 import android.app.ActivityManager; 52 import android.app.ActivityManager.TaskDescription; 53 import android.app.ActivityThread; 54 import android.content.Context; 55 import android.graphics.Canvas; 56 import android.graphics.Color; 57 import android.graphics.GraphicBuffer; 58 import android.graphics.Matrix; 59 import android.graphics.Paint; 60 import android.graphics.PixelFormat; 61 import android.graphics.Point; 62 import android.graphics.Rect; 63 import android.graphics.RectF; 64 import android.hardware.HardwareBuffer; 65 import android.os.IBinder; 66 import android.os.RemoteException; 67 import android.os.SystemClock; 68 import android.os.Trace; 69 import android.util.MergedConfiguration; 70 import android.util.Slog; 71 import android.view.IWindowSession; 72 import android.view.InputChannel; 73 import android.view.InsetsSourceControl; 74 import android.view.InsetsState; 75 import android.view.SurfaceControl; 76 import android.view.SurfaceSession; 77 import android.view.View; 78 import android.view.ViewGroup; 79 import android.view.WindowInsets; 80 import android.view.WindowManager; 81 import android.view.WindowManagerGlobal; 82 import android.window.ClientWindowFrames; 83 import android.window.StartingWindowInfo; 84 import android.window.TaskSnapshot; 85 86 import com.android.internal.R; 87 import com.android.internal.annotations.VisibleForTesting; 88 import com.android.internal.policy.DecorView; 89 import com.android.internal.view.BaseIWindow; 90 import com.android.wm.shell.common.ShellExecutor; 91 92 /** 93 * This class represents a starting window that shows a snapshot. 94 * 95 * @hide 96 */ 97 public class TaskSnapshotWindow { 98 /** 99 * When creating the starting window, we use the exact same layout flags such that we end up 100 * with a window with the exact same dimensions etc. However, these flags are not used in layout 101 * and might cause other side effects so we exclude them. 102 */ 103 static final int FLAG_INHERIT_EXCLUDES = FLAG_NOT_FOCUSABLE 104 | FLAG_NOT_TOUCHABLE 105 | FLAG_NOT_TOUCH_MODAL 106 | FLAG_ALT_FOCUSABLE_IM 107 | FLAG_NOT_FOCUSABLE 108 | FLAG_HARDWARE_ACCELERATED 109 | FLAG_IGNORE_CHEEK_PRESSES 110 | FLAG_LOCAL_FOCUS_MODE 111 | FLAG_SLIPPERY 112 | FLAG_WATCH_OUTSIDE_TOUCH 113 | FLAG_SPLIT_TOUCH 114 | FLAG_SCALED 115 | FLAG_SECURE; 116 117 private static final String TAG = StartingSurfaceDrawer.TAG; 118 private static final boolean DEBUG = StartingSurfaceDrawer.DEBUG_TASK_SNAPSHOT; 119 private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s"; 120 121 private static final long DELAY_REMOVAL_TIME_GENERAL = 100; 122 private static final long DELAY_REMOVAL_TIME_IME_VISIBLE = 350; 123 124 //tmp vars for unused relayout params 125 private static final Point TMP_SURFACE_SIZE = new Point(); 126 127 private final Window mWindow; 128 private final Runnable mClearWindowHandler; 129 private final long mDelayRemovalTime; 130 private final ShellExecutor mSplashScreenExecutor; 131 private final SurfaceControl mSurfaceControl; 132 private final IWindowSession mSession; 133 private final Rect mTaskBounds; 134 private final Rect mFrame = new Rect(); 135 private final Rect mSystemBarInsets = new Rect(); 136 private TaskSnapshot mSnapshot; 137 private final RectF mTmpSnapshotSize = new RectF(); 138 private final RectF mTmpDstFrame = new RectF(); 139 private final CharSequence mTitle; 140 private boolean mHasDrawn; 141 private long mShownTime; 142 private boolean mSizeMismatch; 143 private final Paint mBackgroundPaint = new Paint(); 144 private final int mActivityType; 145 private final int mStatusBarColor; 146 private final SystemBarBackgroundPainter mSystemBarBackgroundPainter; 147 private final int mOrientationOnCreation; 148 private final SurfaceControl.Transaction mTransaction; 149 private final Matrix mSnapshotMatrix = new Matrix(); 150 private final float[] mTmpFloat9 = new float[9]; 151 create(StartingWindowInfo info, IBinder appToken, TaskSnapshot snapshot, ShellExecutor splashScreenExecutor, @NonNull Runnable clearWindowHandler)152 static TaskSnapshotWindow create(StartingWindowInfo info, IBinder appToken, 153 TaskSnapshot snapshot, ShellExecutor splashScreenExecutor, 154 @NonNull Runnable clearWindowHandler) { 155 final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo; 156 final int taskId = runningTaskInfo.taskId; 157 if (DEBUG) { 158 Slog.d(TAG, "create taskSnapshot surface for task: " + taskId); 159 } 160 161 final WindowManager.LayoutParams attrs = info.topOpaqueWindowLayoutParams; 162 final WindowManager.LayoutParams mainWindowParams = info.mainWindowLayoutParams; 163 final InsetsState topWindowInsetsState = info.topOpaqueWindowInsetsState; 164 if (attrs == null || mainWindowParams == null || topWindowInsetsState == null) { 165 Slog.w(TAG, "unable to create taskSnapshot surface for task: " + taskId); 166 return null; 167 } 168 final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(); 169 170 final int appearance = attrs.insetsFlags.appearance; 171 final int windowFlags = attrs.flags; 172 final int windowPrivateFlags = attrs.privateFlags; 173 174 layoutParams.packageName = mainWindowParams.packageName; 175 layoutParams.windowAnimations = mainWindowParams.windowAnimations; 176 layoutParams.dimAmount = mainWindowParams.dimAmount; 177 layoutParams.type = TYPE_APPLICATION_STARTING; 178 layoutParams.format = snapshot.getHardwareBuffer().getFormat(); 179 layoutParams.flags = (windowFlags & ~FLAG_INHERIT_EXCLUDES) 180 | FLAG_NOT_FOCUSABLE 181 | FLAG_NOT_TOUCHABLE; 182 // Setting as trusted overlay to let touches pass through. This is safe because this 183 // window is controlled by the system. 184 layoutParams.privateFlags = (windowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) 185 | PRIVATE_FLAG_TRUSTED_OVERLAY | PRIVATE_FLAG_USE_BLAST; 186 layoutParams.token = appToken; 187 layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT; 188 layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT; 189 layoutParams.insetsFlags.appearance = appearance; 190 layoutParams.insetsFlags.behavior = attrs.insetsFlags.behavior; 191 layoutParams.layoutInDisplayCutoutMode = attrs.layoutInDisplayCutoutMode; 192 layoutParams.setFitInsetsTypes(attrs.getFitInsetsTypes()); 193 layoutParams.setFitInsetsSides(attrs.getFitInsetsSides()); 194 layoutParams.setFitInsetsIgnoringVisibility(attrs.isFitInsetsIgnoringVisibility()); 195 196 layoutParams.setTitle(String.format(TITLE_FORMAT, taskId)); 197 198 final Point taskSize = snapshot.getTaskSize(); 199 final Rect taskBounds = new Rect(0, 0, taskSize.x, taskSize.y); 200 final int orientation = snapshot.getOrientation(); 201 final int activityType = runningTaskInfo.topActivityType; 202 final int displayId = runningTaskInfo.displayId; 203 204 final IWindowSession session = WindowManagerGlobal.getWindowSession(); 205 final SurfaceControl surfaceControl = new SurfaceControl(); 206 final ClientWindowFrames tmpFrames = new ClientWindowFrames(); 207 208 final InsetsSourceControl[] mTempControls = new InsetsSourceControl[0]; 209 final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration(); 210 211 final TaskDescription taskDescription; 212 if (runningTaskInfo.taskDescription != null) { 213 taskDescription = runningTaskInfo.taskDescription; 214 } else { 215 taskDescription = new TaskDescription(); 216 taskDescription.setBackgroundColor(WHITE); 217 } 218 219 final long delayRemovalTime = snapshot.hasImeSurface() ? DELAY_REMOVAL_TIME_IME_VISIBLE 220 : DELAY_REMOVAL_TIME_GENERAL; 221 222 final TaskSnapshotWindow snapshotSurface = new TaskSnapshotWindow( 223 surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, appearance, 224 windowFlags, windowPrivateFlags, taskBounds, orientation, activityType, 225 delayRemovalTime, topWindowInsetsState, clearWindowHandler, splashScreenExecutor); 226 final Window window = snapshotSurface.mWindow; 227 228 final InsetsState mTmpInsetsState = new InsetsState(); 229 final InputChannel tmpInputChannel = new InputChannel(); 230 231 try { 232 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "TaskSnapshot#addToDisplay"); 233 final int res = session.addToDisplay(window, layoutParams, View.GONE, displayId, 234 mTmpInsetsState, tmpInputChannel, mTmpInsetsState, mTempControls); 235 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 236 if (res < 0) { 237 Slog.w(TAG, "Failed to add snapshot starting window res=" + res); 238 return null; 239 } 240 } catch (RemoteException e) { 241 snapshotSurface.clearWindowSynced(); 242 } 243 window.setOuter(snapshotSurface); 244 try { 245 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "TaskSnapshot#relayout"); 246 session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, -1, 247 tmpFrames, tmpMergedConfiguration, surfaceControl, mTmpInsetsState, 248 mTempControls, TMP_SURFACE_SIZE); 249 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 250 } catch (RemoteException e) { 251 snapshotSurface.clearWindowSynced(); 252 } 253 254 final Rect systemBarInsets = getSystemBarInsets(tmpFrames.frame, topWindowInsetsState); 255 snapshotSurface.setFrames(tmpFrames.frame, systemBarInsets); 256 snapshotSurface.drawSnapshot(); 257 return snapshotSurface; 258 } 259 TaskSnapshotWindow(SurfaceControl surfaceControl, TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription, int appearance, int windowFlags, int windowPrivateFlags, Rect taskBounds, int currentOrientation, int activityType, long delayRemovalTime, InsetsState topWindowInsetsState, Runnable clearWindowHandler, ShellExecutor splashScreenExecutor)260 public TaskSnapshotWindow(SurfaceControl surfaceControl, 261 TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription, 262 int appearance, int windowFlags, int windowPrivateFlags, Rect taskBounds, 263 int currentOrientation, int activityType, long delayRemovalTime, 264 InsetsState topWindowInsetsState, Runnable clearWindowHandler, 265 ShellExecutor splashScreenExecutor) { 266 mSplashScreenExecutor = splashScreenExecutor; 267 mSession = WindowManagerGlobal.getWindowSession(); 268 mWindow = new Window(); 269 mWindow.setSession(mSession); 270 mSurfaceControl = surfaceControl; 271 mSnapshot = snapshot; 272 mTitle = title; 273 int backgroundColor = taskDescription.getBackgroundColor(); 274 mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE); 275 mTaskBounds = taskBounds; 276 mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags, 277 windowPrivateFlags, appearance, taskDescription, 1f, topWindowInsetsState); 278 mStatusBarColor = taskDescription.getStatusBarColor(); 279 mOrientationOnCreation = currentOrientation; 280 mActivityType = activityType; 281 mDelayRemovalTime = delayRemovalTime; 282 mTransaction = new SurfaceControl.Transaction(); 283 mClearWindowHandler = clearWindowHandler; 284 } 285 getBackgroundColor()286 int getBackgroundColor() { 287 return mBackgroundPaint.getColor(); 288 } 289 290 /** 291 * Ask system bar background painter to draw status bar background. 292 * @hide 293 */ drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame)294 public void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame) { 295 mSystemBarBackgroundPainter.drawStatusBarBackground(c, alreadyDrawnFrame, 296 mSystemBarBackgroundPainter.getStatusBarColorViewHeight()); 297 } 298 299 /** 300 * Ask system bar background painter to draw navigation bar background. 301 * @hide 302 */ drawNavigationBarBackground(Canvas c)303 public void drawNavigationBarBackground(Canvas c) { 304 mSystemBarBackgroundPainter.drawNavigationBarBackground(c); 305 } 306 remove()307 void remove() { 308 final long now = SystemClock.uptimeMillis(); 309 if ((now - mShownTime < mDelayRemovalTime) 310 // Show the latest content as soon as possible for unlocking to home. 311 && mActivityType != ACTIVITY_TYPE_HOME) { 312 final long delayTime = mShownTime + mDelayRemovalTime - now; 313 mSplashScreenExecutor.executeDelayed(() -> remove(), delayTime); 314 if (DEBUG) { 315 Slog.d(TAG, "Defer removing snapshot surface in " + delayTime); 316 } 317 return; 318 } 319 try { 320 if (DEBUG) { 321 Slog.d(TAG, "Removing snapshot surface, mHasDrawn: " + mHasDrawn); 322 } 323 mSession.remove(mWindow); 324 } catch (RemoteException e) { 325 // nothing 326 } 327 } 328 329 /** 330 * Set frame size. 331 * @hide 332 */ setFrames(Rect frame, Rect systemBarInsets)333 public void setFrames(Rect frame, Rect systemBarInsets) { 334 mFrame.set(frame); 335 mSystemBarInsets.set(systemBarInsets); 336 final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer(); 337 mSizeMismatch = (mFrame.width() != snapshot.getWidth() 338 || mFrame.height() != snapshot.getHeight()); 339 mSystemBarBackgroundPainter.setInsets(systemBarInsets); 340 } 341 getSystemBarInsets(Rect frame, InsetsState state)342 static Rect getSystemBarInsets(Rect frame, InsetsState state) { 343 return state.calculateInsets(frame, WindowInsets.Type.systemBars(), 344 false /* ignoreVisibility */); 345 } 346 drawSnapshot()347 private void drawSnapshot() { 348 if (DEBUG) { 349 Slog.d(TAG, "Drawing snapshot surface sizeMismatch= " + mSizeMismatch); 350 } 351 if (mSizeMismatch) { 352 // The dimensions of the buffer and the window don't match, so attaching the buffer 353 // will fail. Better create a child window with the exact dimensions and fill the parent 354 // window with the background color! 355 drawSizeMismatchSnapshot(); 356 } else { 357 drawSizeMatchSnapshot(); 358 } 359 mShownTime = SystemClock.uptimeMillis(); 360 mHasDrawn = true; 361 reportDrawn(); 362 363 // In case window manager leaks us, make sure we don't retain the snapshot. 364 mSnapshot = null; 365 } 366 drawSizeMatchSnapshot()367 private void drawSizeMatchSnapshot() { 368 GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer( 369 mSnapshot.getHardwareBuffer()); 370 mTransaction.setBuffer(mSurfaceControl, graphicBuffer) 371 .setColorSpace(mSurfaceControl, mSnapshot.getColorSpace()) 372 .apply(); 373 } 374 drawSizeMismatchSnapshot()375 private void drawSizeMismatchSnapshot() { 376 final HardwareBuffer buffer = mSnapshot.getHardwareBuffer(); 377 final SurfaceSession session = new SurfaceSession(); 378 379 // We consider nearly matched dimensions as there can be rounding errors and the user won't 380 // notice very minute differences from scaling one dimension more than the other 381 final boolean aspectRatioMismatch = Math.abs( 382 ((float) buffer.getWidth() / buffer.getHeight()) 383 - ((float) mFrame.width() / mFrame.height())) > 0.01f; 384 385 // Keep a reference to it such that it doesn't get destroyed when finalized. 386 SurfaceControl childSurfaceControl = new SurfaceControl.Builder(session) 387 .setName(mTitle + " - task-snapshot-surface") 388 .setBLASTLayer() 389 .setFormat(buffer.getFormat()) 390 .setParent(mSurfaceControl) 391 .setCallsite("TaskSnapshotWindow.drawSizeMismatchSnapshot") 392 .build(); 393 394 final Rect frame; 395 // We can just show the surface here as it will still be hidden as the parent is 396 // still hidden. 397 mTransaction.show(childSurfaceControl); 398 if (aspectRatioMismatch) { 399 // Clip off ugly navigation bar. 400 final Rect crop = calculateSnapshotCrop(); 401 frame = calculateSnapshotFrame(crop); 402 mTransaction.setWindowCrop(childSurfaceControl, crop); 403 mTransaction.setPosition(childSurfaceControl, frame.left, frame.top); 404 mTmpSnapshotSize.set(crop); 405 mTmpDstFrame.set(frame); 406 } else { 407 frame = null; 408 mTmpSnapshotSize.set(0, 0, buffer.getWidth(), buffer.getHeight()); 409 mTmpDstFrame.set(mFrame); 410 mTmpDstFrame.offsetTo(0, 0); 411 } 412 413 // Scale the mismatch dimensions to fill the task bounds 414 mSnapshotMatrix.setRectToRect(mTmpSnapshotSize, mTmpDstFrame, Matrix.ScaleToFit.FILL); 415 mTransaction.setMatrix(childSurfaceControl, mSnapshotMatrix, mTmpFloat9); 416 GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer( 417 mSnapshot.getHardwareBuffer()); 418 mTransaction.setColorSpace(childSurfaceControl, mSnapshot.getColorSpace()); 419 mTransaction.setBuffer(childSurfaceControl, graphicBuffer); 420 421 if (aspectRatioMismatch) { 422 GraphicBuffer background = GraphicBuffer.create(mFrame.width(), mFrame.height(), 423 PixelFormat.RGBA_8888, 424 GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_HW_COMPOSER 425 | GraphicBuffer.USAGE_SW_WRITE_RARELY); 426 final Canvas c = background.lockCanvas(); 427 drawBackgroundAndBars(c, frame); 428 background.unlockCanvasAndPost(c); 429 mTransaction.setBuffer(mSurfaceControl, background); 430 } 431 mTransaction.apply(); 432 } 433 434 /** 435 * Calculates the snapshot crop in snapshot coordinate space. 436 * 437 * @return crop rect in snapshot coordinate space. 438 */ calculateSnapshotCrop()439 public Rect calculateSnapshotCrop() { 440 final Rect rect = new Rect(); 441 final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer(); 442 rect.set(0, 0, snapshot.getWidth(), snapshot.getHeight()); 443 final Rect insets = mSnapshot.getContentInsets(); 444 445 final float scaleX = (float) snapshot.getWidth() / mSnapshot.getTaskSize().x; 446 final float scaleY = (float) snapshot.getHeight() / mSnapshot.getTaskSize().y; 447 448 // Let's remove all system decorations except the status bar, but only if the task is at the 449 // very top of the screen. 450 final boolean isTop = mTaskBounds.top == 0 && mFrame.top == 0; 451 rect.inset((int) (insets.left * scaleX), 452 isTop ? 0 : (int) (insets.top * scaleY), 453 (int) (insets.right * scaleX), 454 (int) (insets.bottom * scaleY)); 455 return rect; 456 } 457 458 /** 459 * Calculates the snapshot frame in window coordinate space from crop. 460 * 461 * @param crop rect that is in snapshot coordinate space. 462 */ calculateSnapshotFrame(Rect crop)463 public Rect calculateSnapshotFrame(Rect crop) { 464 final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer(); 465 final float scaleX = (float) snapshot.getWidth() / mSnapshot.getTaskSize().x; 466 final float scaleY = (float) snapshot.getHeight() / mSnapshot.getTaskSize().y; 467 468 // Rescale the frame from snapshot to window coordinate space 469 final Rect frame = new Rect(0, 0, 470 (int) (crop.width() / scaleX + 0.5f), 471 (int) (crop.height() / scaleY + 0.5f) 472 ); 473 474 // However, we also need to make space for the navigation bar on the left side. 475 frame.offset(mSystemBarInsets.left, 0); 476 return frame; 477 } 478 479 /** 480 * Draw status bar and navigation bar background. 481 * @hide 482 */ drawBackgroundAndBars(Canvas c, Rect frame)483 public void drawBackgroundAndBars(Canvas c, Rect frame) { 484 final int statusBarHeight = mSystemBarBackgroundPainter.getStatusBarColorViewHeight(); 485 final boolean fillHorizontally = c.getWidth() > frame.right; 486 final boolean fillVertically = c.getHeight() > frame.bottom; 487 if (fillHorizontally) { 488 c.drawRect(frame.right, alpha(mStatusBarColor) == 0xFF ? statusBarHeight : 0, 489 c.getWidth(), fillVertically 490 ? frame.bottom 491 : c.getHeight(), 492 mBackgroundPaint); 493 } 494 if (fillVertically) { 495 c.drawRect(0, frame.bottom, c.getWidth(), c.getHeight(), mBackgroundPaint); 496 } 497 mSystemBarBackgroundPainter.drawDecors(c, frame); 498 } 499 500 /** 501 * Clear window from drawer, must be post on main executor. 502 */ clearWindowSynced()503 private void clearWindowSynced() { 504 mSplashScreenExecutor.executeDelayed(mClearWindowHandler, 0); 505 } 506 reportDrawn()507 private void reportDrawn() { 508 try { 509 mSession.finishDrawing(mWindow, null /* postDrawTransaction */); 510 } catch (RemoteException e) { 511 clearWindowSynced(); 512 } 513 } 514 515 @BinderThread 516 static class Window extends BaseIWindow { 517 private TaskSnapshotWindow mOuter; 518 setOuter(TaskSnapshotWindow outer)519 public void setOuter(TaskSnapshotWindow outer) { 520 mOuter = outer; 521 } 522 523 @Override resized(ClientWindowFrames frames, boolean reportDraw, MergedConfiguration mergedConfiguration, boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId)524 public void resized(ClientWindowFrames frames, boolean reportDraw, 525 MergedConfiguration mergedConfiguration, boolean forceLayout, 526 boolean alwaysConsumeSystemBars, int displayId) { 527 if (mOuter != null) { 528 mOuter.mSplashScreenExecutor.execute(() -> { 529 if (mergedConfiguration != null 530 && mOuter.mOrientationOnCreation 531 != mergedConfiguration.getMergedConfiguration().orientation) { 532 // The orientation of the screen is changing. We better remove the snapshot 533 // ASAP as we are going to wait on the new window in any case to unfreeze 534 // the screen, and the starting window is not needed anymore. 535 mOuter.clearWindowSynced(); 536 } else if (reportDraw) { 537 if (mOuter.mHasDrawn) { 538 mOuter.reportDrawn(); 539 } 540 } 541 }); 542 } 543 } 544 } 545 546 /** 547 * Helper class to draw the background of the system bars in regions the task snapshot isn't 548 * filling the window. 549 */ 550 static class SystemBarBackgroundPainter { 551 private final Paint mStatusBarPaint = new Paint(); 552 private final Paint mNavigationBarPaint = new Paint(); 553 private final int mStatusBarColor; 554 private final int mNavigationBarColor; 555 private final int mWindowFlags; 556 private final int mWindowPrivateFlags; 557 private final float mScale; 558 private final InsetsState mInsetsState; 559 private final Rect mSystemBarInsets = new Rect(); 560 SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int appearance, TaskDescription taskDescription, float scale, InsetsState insetsState)561 SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int appearance, 562 TaskDescription taskDescription, float scale, InsetsState insetsState) { 563 mWindowFlags = windowFlags; 564 mWindowPrivateFlags = windowPrivateFlags; 565 mScale = scale; 566 final Context context = ActivityThread.currentActivityThread().getSystemUiContext(); 567 final int semiTransparent = context.getColor( 568 R.color.system_bar_background_semi_transparent); 569 mStatusBarColor = DecorView.calculateBarColor(windowFlags, FLAG_TRANSLUCENT_STATUS, 570 semiTransparent, taskDescription.getStatusBarColor(), appearance, 571 APPEARANCE_LIGHT_STATUS_BARS, 572 taskDescription.getEnsureStatusBarContrastWhenTransparent()); 573 mNavigationBarColor = DecorView.calculateBarColor(windowFlags, 574 FLAG_TRANSLUCENT_NAVIGATION, semiTransparent, 575 taskDescription.getNavigationBarColor(), appearance, 576 APPEARANCE_LIGHT_NAVIGATION_BARS, 577 taskDescription.getEnsureNavigationBarContrastWhenTransparent() 578 && context.getResources().getBoolean(R.bool.config_navBarNeedsScrim)); 579 mStatusBarPaint.setColor(mStatusBarColor); 580 mNavigationBarPaint.setColor(mNavigationBarColor); 581 mInsetsState = insetsState; 582 } 583 setInsets(Rect systemBarInsets)584 void setInsets(Rect systemBarInsets) { 585 mSystemBarInsets.set(systemBarInsets); 586 } 587 getStatusBarColorViewHeight()588 int getStatusBarColorViewHeight() { 589 final boolean forceBarBackground = 590 (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0; 591 if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible( 592 mInsetsState, mStatusBarColor, mWindowFlags, forceBarBackground)) { 593 return (int) (mSystemBarInsets.top * mScale); 594 } else { 595 return 0; 596 } 597 } 598 isNavigationBarColorViewVisible()599 private boolean isNavigationBarColorViewVisible() { 600 final boolean forceBarBackground = 601 (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0; 602 return NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES.isVisible( 603 mInsetsState, mNavigationBarColor, mWindowFlags, forceBarBackground); 604 } 605 drawDecors(Canvas c, @Nullable Rect alreadyDrawnFrame)606 void drawDecors(Canvas c, @Nullable Rect alreadyDrawnFrame) { 607 drawStatusBarBackground(c, alreadyDrawnFrame, getStatusBarColorViewHeight()); 608 drawNavigationBarBackground(c); 609 } 610 drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame, int statusBarHeight)611 void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame, 612 int statusBarHeight) { 613 if (statusBarHeight > 0 && Color.alpha(mStatusBarColor) != 0 614 && (alreadyDrawnFrame == null || c.getWidth() > alreadyDrawnFrame.right)) { 615 final int rightInset = (int) (mSystemBarInsets.right * mScale); 616 final int left = alreadyDrawnFrame != null ? alreadyDrawnFrame.right : 0; 617 c.drawRect(left, 0, c.getWidth() - rightInset, statusBarHeight, mStatusBarPaint); 618 } 619 } 620 621 @VisibleForTesting drawNavigationBarBackground(Canvas c)622 void drawNavigationBarBackground(Canvas c) { 623 final Rect navigationBarRect = new Rect(); 624 getNavigationBarRect(c.getWidth(), c.getHeight(), mSystemBarInsets, navigationBarRect, 625 mScale); 626 final boolean visible = isNavigationBarColorViewVisible(); 627 if (visible && Color.alpha(mNavigationBarColor) != 0 && !navigationBarRect.isEmpty()) { 628 c.drawRect(navigationBarRect, mNavigationBarPaint); 629 } 630 } 631 } 632 } 633