diff options
| -rw-r--r-- | core/java/android/window/SnapshotDrawerUtils.java | 508 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java (renamed from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java) | 126 | ||||
| -rw-r--r-- | libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java | 191 | ||||
| -rw-r--r-- | libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java | 16 | ||||
| -rw-r--r-- | libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java | 118 | ||||
| -rw-r--r-- | libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java | 423 | ||||
| -rw-r--r-- | services/core/java/com/android/server/wm/TaskSnapshotController.java | 111 |
7 files changed, 791 insertions, 702 deletions
diff --git a/core/java/android/window/SnapshotDrawerUtils.java b/core/java/android/window/SnapshotDrawerUtils.java new file mode 100644 index 000000000000..1a58fd556609 --- /dev/null +++ b/core/java/android/window/SnapshotDrawerUtils.java @@ -0,0 +1,508 @@ +/* + * Copyright (C) 2022 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 android.window; + +import static android.graphics.Color.WHITE; +import static android.graphics.Color.alpha; +import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; +import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; +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.PRIVATE_FLAG_TRUSTED_OVERLAY; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST; + +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.getNavigationBarRect; + +import android.annotation.Nullable; +import android.app.ActivityManager; +import android.app.ActivityThread; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.GraphicBuffer; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.RectF; +import android.hardware.HardwareBuffer; +import android.os.IBinder; +import android.util.Log; +import android.view.InsetsState; +import android.view.SurfaceControl; +import android.view.SurfaceSession; +import android.view.ViewGroup; +import android.view.WindowInsets; +import android.view.WindowManager; + +import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.policy.DecorView; + +/** + * Utils class to help draw a snapshot on a surface. + * @hide + */ +public class SnapshotDrawerUtils { + private static final String TAG = "SnapshotDrawerUtils"; + + /** + * 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. + */ + 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 RectF sTmpSnapshotSize = new RectF(); + private static final RectF sTmpDstFrame = new RectF(); + + private static final Matrix sSnapshotMatrix = new Matrix(); + private static final float[] sTmpFloat9 = new float[9]; + private static final Paint sBackgroundPaint = new Paint(); + + /** + * The internal object to hold the surface and drawing on it. + */ + @VisibleForTesting + public static class SnapshotSurface { + private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction(); + private final SurfaceControl mRootSurface; + private final TaskSnapshot mSnapshot; + private final CharSequence mTitle; + + private SystemBarBackgroundPainter mSystemBarBackgroundPainter; + private final Rect mTaskBounds; + private final Rect mFrame = new Rect(); + private final Rect mSystemBarInsets = new Rect(); + private boolean mSizeMismatch; + + public SnapshotSurface(SurfaceControl rootSurface, TaskSnapshot snapshot, + CharSequence title, + Rect taskBounds) { + mRootSurface = rootSurface; + mSnapshot = snapshot; + mTitle = title; + mTaskBounds = taskBounds; + } + + /** + * Initiate system bar painter to draw the system bar background. + */ + void initiateSystemBarPainter(int windowFlags, int windowPrivateFlags, + int appearance, ActivityManager.TaskDescription taskDescription, + @WindowInsets.Type.InsetsType int requestedVisibleTypes) { + mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags, + windowPrivateFlags, appearance, taskDescription, 1f, requestedVisibleTypes); + int backgroundColor = taskDescription.getBackgroundColor(); + sBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE); + } + + /** + * Set frame size. + */ + void setFrames(Rect frame, Rect systemBarInsets) { + mFrame.set(frame); + mSystemBarInsets.set(systemBarInsets); + final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer(); + mSizeMismatch = (mFrame.width() != snapshot.getWidth() + || mFrame.height() != snapshot.getHeight()); + mSystemBarBackgroundPainter.setInsets(systemBarInsets); + } + + private void drawSnapshot(boolean releaseAfterDraw) { + Log.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(); + } + + // In case window manager leaks us, make sure we don't retain the snapshot. + if (mSnapshot.getHardwareBuffer() != null) { + mSnapshot.getHardwareBuffer().close(); + } + if (releaseAfterDraw) { + mRootSurface.release(); + } + } + + private void drawSizeMatchSnapshot() { + mTransaction.setBuffer(mRootSurface, mSnapshot.getHardwareBuffer()) + .setColorSpace(mRootSurface, mSnapshot.getColorSpace()) + .apply(); + } + + private void drawSizeMismatchSnapshot() { + final HardwareBuffer buffer = mSnapshot.getHardwareBuffer(); + 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 = !isAspectRatioMatch(mFrame, mSnapshot); + + // Keep a reference to it such that it doesn't get destroyed when finalized. + SurfaceControl childSurfaceControl = new SurfaceControl.Builder(session) + .setName(mTitle + " - task-snapshot-surface") + .setBLASTLayer() + .setFormat(buffer.getFormat()) + .setParent(mRootSurface) + .setCallsite("TaskSnapshotWindow.drawSizeMismatchSnapshot") + .build(); + + final Rect frame; + // We can just show the surface here as it will still be hidden as the parent is + // still hidden. + mTransaction.show(childSurfaceControl); + if (aspectRatioMismatch) { + // Clip off ugly navigation bar. + final Rect crop = calculateSnapshotCrop(); + frame = calculateSnapshotFrame(crop); + mTransaction.setWindowCrop(childSurfaceControl, crop); + mTransaction.setPosition(childSurfaceControl, frame.left, frame.top); + sTmpSnapshotSize.set(crop); + sTmpDstFrame.set(frame); + } else { + frame = null; + sTmpSnapshotSize.set(0, 0, buffer.getWidth(), buffer.getHeight()); + sTmpDstFrame.set(mFrame); + sTmpDstFrame.offsetTo(0, 0); + } + + // Scale the mismatch dimensions to fill the task bounds + sSnapshotMatrix.setRectToRect(sTmpSnapshotSize, sTmpDstFrame, Matrix.ScaleToFit.FILL); + mTransaction.setMatrix(childSurfaceControl, sSnapshotMatrix, sTmpFloat9); + mTransaction.setColorSpace(childSurfaceControl, mSnapshot.getColorSpace()); + mTransaction.setBuffer(childSurfaceControl, mSnapshot.getHardwareBuffer()); + + if (aspectRatioMismatch) { + GraphicBuffer background = GraphicBuffer.create(mFrame.width(), mFrame.height(), + PixelFormat.RGBA_8888, + GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_HW_COMPOSER + | GraphicBuffer.USAGE_SW_WRITE_RARELY); + // TODO: Support this on HardwareBuffer + final Canvas c = background.lockCanvas(); + drawBackgroundAndBars(c, frame); + background.unlockCanvasAndPost(c); + mTransaction.setBuffer(mRootSurface, + HardwareBuffer.createFromGraphicBuffer(background)); + } + mTransaction.apply(); + childSurfaceControl.release(); + } + + /** + * Calculates the snapshot crop in snapshot coordinate space. + * + * @return crop rect in snapshot coordinate space. + */ + Rect calculateSnapshotCrop() { + final Rect rect = new Rect(); + final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer(); + rect.set(0, 0, snapshot.getWidth(), snapshot.getHeight()); + final Rect insets = mSnapshot.getContentInsets(); + + final float scaleX = (float) snapshot.getWidth() / mSnapshot.getTaskSize().x; + final float scaleY = (float) snapshot.getHeight() / mSnapshot.getTaskSize().y; + + // 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 * scaleX), + isTop ? 0 : (int) (insets.top * scaleY), + (int) (insets.right * scaleX), + (int) (insets.bottom * scaleY)); + return rect; + } + + /** + * Calculates the snapshot frame in window coordinate space from crop. + * + * @param crop rect that is in snapshot coordinate space. + */ + Rect calculateSnapshotFrame(Rect crop) { + final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer(); + final float scaleX = (float) snapshot.getWidth() / mSnapshot.getTaskSize().x; + final float scaleY = (float) snapshot.getHeight() / mSnapshot.getTaskSize().y; + + // Rescale the frame from snapshot to window coordinate space + final Rect frame = new Rect(0, 0, + (int) (crop.width() / scaleX + 0.5f), + (int) (crop.height() / scaleY + 0.5f) + ); + + // However, we also need to make space for the navigation bar on the left side. + frame.offset(mSystemBarInsets.left, 0); + return frame; + } + + /** + * Draw status bar and navigation bar background. + */ + 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(mSystemBarBackgroundPainter.mStatusBarColor) == 0xFF + ? statusBarHeight : 0, c.getWidth(), fillVertically + ? frame.bottom : c.getHeight(), sBackgroundPaint); + } + if (fillVertically) { + c.drawRect(0, frame.bottom, c.getWidth(), c.getHeight(), sBackgroundPaint); + } + mSystemBarBackgroundPainter.drawDecors(c, frame); + } + + /** + * Ask system bar background painter to draw status bar background. + * + */ + void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame) { + mSystemBarBackgroundPainter.drawStatusBarBackground(c, alreadyDrawnFrame, + mSystemBarBackgroundPainter.getStatusBarColorViewHeight()); + } + + /** + * Ask system bar background painter to draw navigation bar background. + * + */ + void drawNavigationBarBackground(Canvas c) { + mSystemBarBackgroundPainter.drawNavigationBarBackground(c); + } + } + + /** + * @return true if the aspect ratio match between a frame and a snapshot buffer. + */ + public static boolean isAspectRatioMatch(Rect frame, TaskSnapshot snapshot) { + if (frame.isEmpty()) { + return false; + } + final HardwareBuffer buffer = snapshot.getHardwareBuffer(); + return Math.abs( + ((float) buffer.getWidth() / buffer.getHeight()) + - ((float) frame.width() / frame.height())) <= 0.01f; + } + + /** + * Help method to draw the snapshot on a surface. + */ + public static void drawSnapshotOnSurface(StartingWindowInfo info, WindowManager.LayoutParams lp, + SurfaceControl rootSurface, TaskSnapshot snapshot, + Rect configBounds, Rect windowBounds, InsetsState topWindowInsetsState, + boolean releaseAfterDraw) { + if (windowBounds.isEmpty()) { + Log.e(TAG, "Unable to draw snapshot on an empty windowBounds"); + return; + } + final SnapshotSurface drawSurface = new SnapshotSurface( + rootSurface, snapshot, lp.getTitle(), configBounds); + + final WindowManager.LayoutParams attrs = info.topOpaqueWindowLayoutParams; + final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo; + final ActivityManager.TaskDescription taskDescription; + if (runningTaskInfo.taskDescription != null) { + taskDescription = runningTaskInfo.taskDescription; + } else { + taskDescription = new ActivityManager.TaskDescription(); + taskDescription.setBackgroundColor(WHITE); + } + drawSurface.initiateSystemBarPainter(lp.flags, lp.privateFlags, + attrs.insetsFlags.appearance, taskDescription, info.requestedVisibleTypes); + final Rect systemBarInsets = getSystemBarInsets(windowBounds, topWindowInsetsState); + drawSurface.setFrames(windowBounds, systemBarInsets); + drawSurface.drawSnapshot(releaseAfterDraw); + } + + /** + * Help method to create a layout parameters for a window. + */ + public static WindowManager.LayoutParams createLayoutParameters(StartingWindowInfo info, + CharSequence title, @WindowManager.LayoutParams.WindowType int windowType, + int pixelFormat, IBinder token) { + final WindowManager.LayoutParams attrs = info.topOpaqueWindowLayoutParams; + final WindowManager.LayoutParams mainWindowParams = info.mainWindowLayoutParams; + final InsetsState topWindowInsetsState = info.topOpaqueWindowInsetsState; + if (attrs == null || mainWindowParams == null || topWindowInsetsState == null) { + Log.w(TAG, "unable to create taskSnapshot surface "); + return null; + } + final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(); + + final int appearance = attrs.insetsFlags.appearance; + final int windowFlags = attrs.flags; + final int windowPrivateFlags = attrs.privateFlags; + + layoutParams.packageName = mainWindowParams.packageName; + layoutParams.windowAnimations = mainWindowParams.windowAnimations; + layoutParams.dimAmount = mainWindowParams.dimAmount; + layoutParams.type = windowType; + layoutParams.format = pixelFormat; + layoutParams.flags = (windowFlags & ~FLAG_INHERIT_EXCLUDES) + | FLAG_NOT_FOCUSABLE + | FLAG_NOT_TOUCHABLE; + // Setting as trusted overlay to let touches pass through. This is safe because this + // window is controlled by the system. + layoutParams.privateFlags = (windowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) + | PRIVATE_FLAG_TRUSTED_OVERLAY | PRIVATE_FLAG_USE_BLAST; + layoutParams.token = token; + layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT; + layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT; + layoutParams.insetsFlags.appearance = appearance; + layoutParams.insetsFlags.behavior = attrs.insetsFlags.behavior; + layoutParams.layoutInDisplayCutoutMode = attrs.layoutInDisplayCutoutMode; + layoutParams.setFitInsetsTypes(attrs.getFitInsetsTypes()); + layoutParams.setFitInsetsSides(attrs.getFitInsetsSides()); + layoutParams.setFitInsetsIgnoringVisibility(attrs.isFitInsetsIgnoringVisibility()); + + layoutParams.setTitle(title); + return layoutParams; + } + + static Rect getSystemBarInsets(Rect frame, InsetsState state) { + return state.calculateInsets(frame, WindowInsets.Type.systemBars(), + false /* ignoreVisibility */).toRect(); + } + + /** + * Helper class to draw the background of the system bars in regions the task snapshot isn't + * filling the window. + */ + public static class SystemBarBackgroundPainter { + 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 float mScale; + private final @WindowInsets.Type.InsetsType int mRequestedVisibleTypes; + private final Rect mSystemBarInsets = new Rect(); + + public SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int appearance, + ActivityManager.TaskDescription taskDescription, float scale, + @WindowInsets.Type.InsetsType int requestedVisibleTypes) { + mWindowFlags = windowFlags; + mWindowPrivateFlags = windowPrivateFlags; + 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(), appearance, + APPEARANCE_LIGHT_STATUS_BARS, + taskDescription.getEnsureStatusBarContrastWhenTransparent()); + mNavigationBarColor = DecorView.calculateBarColor(windowFlags, + FLAG_TRANSLUCENT_NAVIGATION, semiTransparent, + taskDescription.getNavigationBarColor(), appearance, + APPEARANCE_LIGHT_NAVIGATION_BARS, + taskDescription.getEnsureNavigationBarContrastWhenTransparent() + && context.getResources().getBoolean( + R.bool.config_navBarNeedsScrim)); + mStatusBarPaint.setColor(mStatusBarColor); + mNavigationBarPaint.setColor(mNavigationBarColor); + mRequestedVisibleTypes = requestedVisibleTypes; + } + + /** + * Set system bar insets. + */ + public void setInsets(Rect systemBarInsets) { + mSystemBarInsets.set(systemBarInsets); + } + + int getStatusBarColorViewHeight() { + final boolean forceBarBackground = + (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0; + if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible( + mRequestedVisibleTypes, mStatusBarColor, mWindowFlags, + forceBarBackground)) { + return (int) (mSystemBarInsets.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( + mRequestedVisibleTypes, mNavigationBarColor, mWindowFlags, + forceBarBackground); + } + + /** + * Draw bar colors to a canvas. + */ + public void drawDecors(Canvas c, @Nullable Rect alreadyDrawnFrame) { + drawStatusBarBackground(c, alreadyDrawnFrame, getStatusBarColorViewHeight()); + drawNavigationBarBackground(c); + } + + 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) (mSystemBarInsets.right * mScale); + final int left = alreadyDrawnFrame != null ? alreadyDrawnFrame.right : 0; + c.drawRect(left, 0, c.getWidth() - rightInset, statusBarHeight, + mStatusBarPaint); + } + } + + void drawNavigationBarBackground(Canvas c) { + final Rect navigationBarRect = new Rect(); + getNavigationBarRect(c.getWidth(), c.getHeight(), mSystemBarInsets, navigationBarRect, + mScale); + final boolean visible = isNavigationBarColorViewVisible(); + if (visible && Color.alpha(mNavigationBarColor) != 0 + && !navigationBarRect.isEmpty()) { + c.drawRect(navigationBarRect, mNavigationBarPaint); + } + } + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java b/core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java index 004df2a29e58..281d677dccb0 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java +++ b/core/tests/coretests/src/android/window/SnapshotDrawerUtilsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2022 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. @@ -14,22 +14,20 @@ * limitations under the License. */ -package com.android.wm.shell.startingsurface; +package android.window; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; - import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.app.ActivityManager.TaskDescription; import android.content.ComponentName; @@ -42,33 +40,28 @@ import android.hardware.HardwareBuffer; import android.view.Surface; import android.view.SurfaceControl; import android.view.WindowInsets; -import android.window.TaskSnapshot; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.wm.shell.ShellTestCase; -import com.android.wm.shell.TestShellExecutor; - import org.junit.Test; import org.junit.runner.RunWith; /** - * Test class for {@link TaskSnapshotWindow}. - * + * Test class for {@link SnapshotDrawerUtils}. */ @SmallTest @RunWith(AndroidJUnit4.class) -public class TaskSnapshotWindowTest extends ShellTestCase { +public class SnapshotDrawerUtilsTest { - private TaskSnapshotWindow mWindow; + private SnapshotDrawerUtils.SnapshotSurface mSnapshotSurface; private void setupSurface(int width, int height) { - setupSurface(width, height, new Rect(), 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, + setupSurface(width, height, new Rect(), FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, new Rect(0, 0, width, height)); } - private void setupSurface(int width, int height, Rect contentInsets, int sysuiVis, + private void setupSurface(int width, int height, Rect contentInsets, int windowFlags, Rect taskBounds) { // Previously when constructing TaskSnapshots for this test, scale was 1.0f, so to mimic // this behavior set the taskSize to be the same as the taskBounds width and height. The @@ -80,12 +73,13 @@ public class TaskSnapshotWindowTest extends ShellTestCase { Point taskSize = new Point(taskBounds.width(), taskBounds.height()); final TaskSnapshot snapshot = createTaskSnapshot(width, height, taskSize, contentInsets); - mWindow = new TaskSnapshotWindow(new SurfaceControl(), snapshot, "Test", - createTaskDescription(Color.WHITE, Color.RED, Color.BLUE), - 0 /* appearance */, windowFlags /* windowFlags */, 0 /* privateWindowFlags */, - taskBounds, ORIENTATION_PORTRAIT, ACTIVITY_TYPE_STANDARD, - WindowInsets.Type.defaultVisible(), null /* clearWindow */, - new TestShellExecutor()); + TaskDescription taskDescription = createTaskDescription(Color.WHITE, + Color.RED, Color.BLUE); + + mSnapshotSurface = new SnapshotDrawerUtils.SnapshotSurface( + new SurfaceControl(), snapshot, "Test", taskBounds); + mSnapshotSurface.initiateSystemBarPainter(windowFlags, 0, 0, + taskDescription, WindowInsets.Type.defaultVisible()); } private TaskSnapshot createTaskSnapshot(int width, int height, Point taskSize, @@ -101,8 +95,8 @@ public class TaskSnapshotWindowTest extends ShellTestCase { 0 /* systemUiVisibility */, false /* isTranslucent */, false /* hasImeSurface */); } - private static TaskDescription createTaskDescription(int background, int statusBar, - int navigationBar) { + private static TaskDescription createTaskDescription(int background, + int statusBar, int navigationBar) { final TaskDescription td = new TaskDescription(); td.setBackgroundColor(background); td.setStatusBarColor(statusBar); @@ -116,7 +110,7 @@ public class TaskSnapshotWindowTest extends ShellTestCase { final Canvas mockCanvas = mock(Canvas.class); when(mockCanvas.getWidth()).thenReturn(200); when(mockCanvas.getHeight()).thenReturn(100); - mWindow.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 200)); + mSnapshotSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 200)); verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any()); } @@ -126,7 +120,7 @@ public class TaskSnapshotWindowTest extends ShellTestCase { final Canvas mockCanvas = mock(Canvas.class); when(mockCanvas.getWidth()).thenReturn(100); when(mockCanvas.getHeight()).thenReturn(200); - mWindow.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 100)); + mSnapshotSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 100)); verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(100.0f), eq(200.0f), any()); } @@ -136,7 +130,7 @@ public class TaskSnapshotWindowTest extends ShellTestCase { final Canvas mockCanvas = mock(Canvas.class); when(mockCanvas.getWidth()).thenReturn(200); when(mockCanvas.getHeight()).thenReturn(200); - mWindow.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100)); + mSnapshotSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100)); verify(mockCanvas).drawRect(eq(100.0f), eq(0.0f), eq(200.0f), eq(100.0f), any()); verify(mockCanvas).drawRect(eq(0.0f), eq(100.0f), eq(200.0f), eq(200.0f), any()); } @@ -147,7 +141,7 @@ public class TaskSnapshotWindowTest extends ShellTestCase { final Canvas mockCanvas = mock(Canvas.class); when(mockCanvas.getWidth()).thenReturn(100); when(mockCanvas.getHeight()).thenReturn(100); - mWindow.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100)); + mSnapshotSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 100, 100)); verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any()); } @@ -157,76 +151,76 @@ public class TaskSnapshotWindowTest extends ShellTestCase { final Canvas mockCanvas = mock(Canvas.class); when(mockCanvas.getWidth()).thenReturn(100); when(mockCanvas.getHeight()).thenReturn(100); - mWindow.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 200)); + mSnapshotSurface.drawBackgroundAndBars(mockCanvas, new Rect(0, 0, 200, 200)); verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any()); } @Test public void testCalculateSnapshotCrop() { - setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 0, 100, 100)); - assertEquals(new Rect(0, 0, 100, 90), mWindow.calculateSnapshotCrop()); + setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, new Rect(0, 0, 100, 100)); + assertEquals(new Rect(0, 0, 100, 90), mSnapshotSurface.calculateSnapshotCrop()); } @Test public void testCalculateSnapshotCrop_taskNotOnTop() { - setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, 0, new Rect(0, 50, 100, 150)); - assertEquals(new Rect(0, 10, 100, 90), mWindow.calculateSnapshotCrop()); + setupSurface(100, 100, new Rect(0, 10, 0, 10), 0, new Rect(0, 50, 100, 150)); + assertEquals(new Rect(0, 10, 100, 90), mSnapshotSurface.calculateSnapshotCrop()); } @Test public void testCalculateSnapshotCrop_navBarLeft() { - setupSurface(100, 100, new Rect(10, 10, 0, 0), 0, 0, new Rect(0, 0, 100, 100)); - assertEquals(new Rect(10, 0, 100, 100), mWindow.calculateSnapshotCrop()); + setupSurface(100, 100, new Rect(10, 10, 0, 0), 0, new Rect(0, 0, 100, 100)); + assertEquals(new Rect(10, 0, 100, 100), mSnapshotSurface.calculateSnapshotCrop()); } @Test public void testCalculateSnapshotCrop_navBarRight() { - setupSurface(100, 100, new Rect(0, 10, 10, 0), 0, 0, new Rect(0, 0, 100, 100)); - assertEquals(new Rect(0, 0, 90, 100), mWindow.calculateSnapshotCrop()); + setupSurface(100, 100, new Rect(0, 10, 10, 0), 0, new Rect(0, 0, 100, 100)); + assertEquals(new Rect(0, 0, 90, 100), mSnapshotSurface.calculateSnapshotCrop()); } @Test public void testCalculateSnapshotCrop_waterfall() { - setupSurface(100, 100, new Rect(5, 10, 5, 10), 0, 0, new Rect(0, 0, 100, 100)); - assertEquals(new Rect(5, 0, 95, 90), mWindow.calculateSnapshotCrop()); + setupSurface(100, 100, new Rect(5, 10, 5, 10), 0, new Rect(0, 0, 100, 100)); + assertEquals(new Rect(5, 0, 95, 90), mSnapshotSurface.calculateSnapshotCrop()); } @Test public void testCalculateSnapshotFrame() { setupSurface(100, 100); final Rect insets = new Rect(0, 10, 0, 10); - mWindow.setFrames(new Rect(0, 0, 100, 100), insets); + mSnapshotSurface.setFrames(new Rect(0, 0, 100, 100), insets); assertEquals(new Rect(0, 0, 100, 80), - mWindow.calculateSnapshotFrame(new Rect(0, 10, 100, 90))); + mSnapshotSurface.calculateSnapshotFrame(new Rect(0, 10, 100, 90))); } @Test public void testCalculateSnapshotFrame_navBarLeft() { setupSurface(100, 100); final Rect insets = new Rect(10, 10, 0, 0); - mWindow.setFrames(new Rect(0, 0, 100, 100), insets); + mSnapshotSurface.setFrames(new Rect(0, 0, 100, 100), insets); assertEquals(new Rect(10, 0, 100, 90), - mWindow.calculateSnapshotFrame(new Rect(10, 10, 100, 100))); + mSnapshotSurface.calculateSnapshotFrame(new Rect(10, 10, 100, 100))); } @Test public void testCalculateSnapshotFrame_waterfall() { - setupSurface(100, 100, new Rect(5, 10, 5, 10), 0, 0, new Rect(0, 0, 100, 100)); + setupSurface(100, 100, new Rect(5, 10, 5, 10), 0, new Rect(0, 0, 100, 100)); final Rect insets = new Rect(0, 10, 0, 10); - mWindow.setFrames(new Rect(5, 0, 95, 100), insets); + mSnapshotSurface.setFrames(new Rect(5, 0, 95, 100), insets); assertEquals(new Rect(0, 0, 90, 90), - mWindow.calculateSnapshotFrame(new Rect(5, 0, 95, 90))); + mSnapshotSurface.calculateSnapshotFrame(new Rect(5, 0, 95, 90))); } @Test public void testDrawStatusBarBackground() { setupSurface(100, 100); final Rect insets = new Rect(0, 10, 10, 0); - mWindow.setFrames(new Rect(0, 0, 100, 100), insets); + mSnapshotSurface.setFrames(new Rect(0, 0, 100, 100), insets); final Canvas mockCanvas = mock(Canvas.class); when(mockCanvas.getWidth()).thenReturn(100); when(mockCanvas.getHeight()).thenReturn(100); - mWindow.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 50, 100)); + mSnapshotSurface.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 50, 100)); verify(mockCanvas).drawRect(eq(50.0f), eq(0.0f), eq(90.0f), eq(10.0f), any()); } @@ -234,11 +228,11 @@ public class TaskSnapshotWindowTest extends ShellTestCase { public void testDrawStatusBarBackground_nullFrame() { setupSurface(100, 100); final Rect insets = new Rect(0, 10, 10, 0); - mWindow.setFrames(new Rect(0, 0, 100, 100), insets); + mSnapshotSurface.setFrames(new Rect(0, 0, 100, 100), insets); final Canvas mockCanvas = mock(Canvas.class); when(mockCanvas.getWidth()).thenReturn(100); when(mockCanvas.getHeight()).thenReturn(100); - mWindow.drawStatusBarBackground(mockCanvas, null); + mSnapshotSurface.drawStatusBarBackground(mockCanvas, null); verify(mockCanvas).drawRect(eq(0.0f), eq(0.0f), eq(90.0f), eq(10.0f), any()); } @@ -246,50 +240,50 @@ public class TaskSnapshotWindowTest extends ShellTestCase { public void testDrawStatusBarBackground_nope() { setupSurface(100, 100); final Rect insets = new Rect(0, 10, 10, 0); - mWindow.setFrames(new Rect(0, 0, 100, 100), insets); + mSnapshotSurface.setFrames(new Rect(0, 0, 100, 100), insets); final Canvas mockCanvas = mock(Canvas.class); when(mockCanvas.getWidth()).thenReturn(100); when(mockCanvas.getHeight()).thenReturn(100); - mWindow.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 100, 100)); + mSnapshotSurface.drawStatusBarBackground(mockCanvas, new Rect(0, 0, 100, 100)); verify(mockCanvas, never()).drawRect(anyInt(), anyInt(), anyInt(), anyInt(), any()); } @Test public void testDrawNavigationBarBackground() { final Rect insets = new Rect(0, 10, 0, 10); - setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, + setupSurface(100, 100, insets, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, new Rect(0, 0, 100, 100)); - mWindow.setFrames(new Rect(0, 0, 100, 100), insets); + mSnapshotSurface.setFrames(new Rect(0, 0, 100, 100), insets); final Canvas mockCanvas = mock(Canvas.class); when(mockCanvas.getWidth()).thenReturn(100); when(mockCanvas.getHeight()).thenReturn(100); - mWindow.drawNavigationBarBackground(mockCanvas); + mSnapshotSurface.drawNavigationBarBackground(mockCanvas); verify(mockCanvas).drawRect(eq(new Rect(0, 90, 100, 100)), any()); } @Test public void testDrawNavigationBarBackground_left() { final Rect insets = new Rect(10, 10, 0, 0); - setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, + setupSurface(100, 100, insets, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, new Rect(0, 0, 100, 100)); - mWindow.setFrames(new Rect(0, 0, 100, 100), insets); + mSnapshotSurface.setFrames(new Rect(0, 0, 100, 100), insets); final Canvas mockCanvas = mock(Canvas.class); when(mockCanvas.getWidth()).thenReturn(100); when(mockCanvas.getHeight()).thenReturn(100); - mWindow.drawNavigationBarBackground(mockCanvas); + mSnapshotSurface.drawNavigationBarBackground(mockCanvas); verify(mockCanvas).drawRect(eq(new Rect(0, 0, 10, 100)), any()); } @Test public void testDrawNavigationBarBackground_right() { final Rect insets = new Rect(0, 10, 10, 0); - setupSurface(100, 100, insets, 0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, + setupSurface(100, 100, insets, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, new Rect(0, 0, 100, 100)); - mWindow.setFrames(new Rect(0, 0, 100, 100), insets); + mSnapshotSurface.setFrames(new Rect(0, 0, 100, 100), insets); final Canvas mockCanvas = mock(Canvas.class); when(mockCanvas.getWidth()).thenReturn(100); when(mockCanvas.getHeight()).thenReturn(100); - mWindow.drawNavigationBarBackground(mockCanvas); + mSnapshotSurface.drawNavigationBarBackground(mockCanvas); verify(mockCanvas).drawRect(eq(new Rect(90, 0, 100, 100)), any()); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java index 6ce981e25f5e..0a2027f28f5f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java @@ -16,8 +16,10 @@ package com.android.wm.shell.startingsurface; +import static android.content.Context.CONTEXT_RESTRICTED; import static android.os.Process.THREAD_PRIORITY_TOP_APP_BOOST; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; +import static android.view.Display.DEFAULT_DISPLAY; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; @@ -29,6 +31,7 @@ import android.annotation.ColorInt; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityManager; import android.app.ActivityThread; import android.content.BroadcastReceiver; import android.content.Context; @@ -48,9 +51,11 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; +import android.hardware.display.DisplayManager; import android.net.Uri; import android.os.Handler; import android.os.HandlerThread; +import android.os.IBinder; import android.os.SystemClock; import android.os.Trace; import android.os.UserHandle; @@ -58,7 +63,9 @@ import android.util.ArrayMap; import android.util.DisplayMetrics; import android.util.Slog; import android.view.ContextThemeWrapper; +import android.view.Display; import android.view.SurfaceControl; +import android.view.WindowManager; import android.window.SplashScreenView; import android.window.StartingWindowInfo; import android.window.StartingWindowInfo.StartingWindowType; @@ -134,6 +141,144 @@ public class SplashscreenContentDrawer { } /** + * Help method to create a layout parameters for a window. + */ + static Context createContext(Context initContext, StartingWindowInfo windowInfo, + int theme, @StartingWindowInfo.StartingWindowType int suggestType, + DisplayManager displayManager) { + final ActivityManager.RunningTaskInfo taskInfo = windowInfo.taskInfo; + final ActivityInfo activityInfo = windowInfo.targetActivityInfo != null + ? windowInfo.targetActivityInfo + : taskInfo.topActivityInfo; + if (activityInfo == null || activityInfo.packageName == null) { + return null; + } + + final int displayId = taskInfo.displayId; + final int taskId = taskInfo.taskId; + + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, + "addSplashScreen for package: %s with theme: %s for task: %d, suggestType: %d", + activityInfo.packageName, Integer.toHexString(theme), taskId, suggestType); + final Display display = displayManager.getDisplay(displayId); + if (display == null) { + // Can't show splash screen on requested display, so skip showing at all. + return null; + } + Context context = displayId == DEFAULT_DISPLAY + ? initContext : initContext.createDisplayContext(display); + if (context == null) { + return null; + } + if (theme != context.getThemeResId()) { + try { + context = context.createPackageContextAsUser(activityInfo.packageName, + CONTEXT_RESTRICTED, UserHandle.of(taskInfo.userId)); + context.setTheme(theme); + } catch (PackageManager.NameNotFoundException e) { + Slog.w(TAG, "Failed creating package context with package name " + + activityInfo.packageName + " for user " + taskInfo.userId, e); + return null; + } + } + + final Configuration taskConfig = taskInfo.getConfiguration(); + if (taskConfig.diffPublicOnly(context.getResources().getConfiguration()) != 0) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, + "addSplashScreen: creating context based on task Configuration %s", + taskConfig); + final Context overrideContext = context.createConfigurationContext(taskConfig); + overrideContext.setTheme(theme); + final TypedArray typedArray = overrideContext.obtainStyledAttributes( + com.android.internal.R.styleable.Window); + final int resId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0); + try { + if (resId != 0 && overrideContext.getDrawable(resId) != null) { + // We want to use the windowBackground for the override context if it is + // available, otherwise we use the default one to make sure a themed starting + // window is displayed for the app. + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, + "addSplashScreen: apply overrideConfig %s", + taskConfig); + context = overrideContext; + } + } catch (Resources.NotFoundException e) { + Slog.w(TAG, "failed creating starting window for overrideConfig at taskId: " + + taskId, e); + return null; + } + typedArray.recycle(); + } + return context; + } + + /** + * Creates the window layout parameters for splashscreen window. + */ + static WindowManager.LayoutParams createLayoutParameters(Context context, + StartingWindowInfo windowInfo, + @StartingWindowInfo.StartingWindowType int suggestType, + CharSequence title, int pixelFormat, IBinder appToken) { + final WindowManager.LayoutParams params = new WindowManager.LayoutParams( + WindowManager.LayoutParams.TYPE_APPLICATION_STARTING); + params.setFitInsetsSides(0); + params.setFitInsetsTypes(0); + params.format = pixelFormat; + int windowFlags = WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED + | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; + final TypedArray a = context.obtainStyledAttributes(R.styleable.Window); + if (a.getBoolean(R.styleable.Window_windowShowWallpaper, false)) { + windowFlags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; + } + if (suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) { + if (a.getBoolean(R.styleable.Window_windowDrawsSystemBarBackgrounds, false)) { + windowFlags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + } + } else { + windowFlags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + } + params.layoutInDisplayCutoutMode = a.getInt( + R.styleable.Window_windowLayoutInDisplayCutoutMode, + params.layoutInDisplayCutoutMode); + params.windowAnimations = a.getResourceId(R.styleable.Window_windowAnimationStyle, 0); + a.recycle(); + + final ActivityManager.RunningTaskInfo taskInfo = windowInfo.taskInfo; + final ActivityInfo activityInfo = windowInfo.targetActivityInfo != null + ? windowInfo.targetActivityInfo + : taskInfo.topActivityInfo; + final int displayId = taskInfo.displayId; + // Assumes it's safe to show starting windows of launched apps while + // the keyguard is being hidden. This is okay because starting windows never show + // secret information. + // TODO(b/113840485): Occluded may not only happen on default display + if (displayId == DEFAULT_DISPLAY && windowInfo.isKeyguardOccluded) { + windowFlags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; + } + + // Force the window flags: this is a fake window, so it is not really + // touchable or focusable by the user. We also add in the ALT_FOCUSABLE_IM + // flag because we do know that the next window will take input + // focus, so we want to get the IME window up on top of us right away. + // Touches will only pass through to the host activity window and will be blocked from + // passing to any other windows. + windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; + params.flags = windowFlags; + params.token = appToken; + params.packageName = activityInfo.packageName; + params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; + + if (!context.getResources().getCompatibilityInfo().supportsScreen()) { + params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW; + } + + params.setTitle("Splash Screen " + title); + return params; + } + /** * Create a SplashScreenView object. * * In order to speed up the splash screen view to show on first frame, preparing the @@ -248,6 +393,26 @@ public class SplashscreenContentDrawer { return null; } + /** + * Creates a SplashScreenView without read animatable icon and branding image. + */ + SplashScreenView makeSimpleSplashScreenContentView(Context context, + StartingWindowInfo info, int themeBGColor) { + updateDensity(); + mTmpAttrs.reset(); + final ActivityInfo ai = info.targetActivityInfo != null + ? info.targetActivityInfo + : info.taskInfo.topActivityInfo; + + final SplashViewBuilder builder = new SplashViewBuilder(context, ai); + final SplashScreenView view = builder + .setWindowBGColor(themeBGColor) + .chooseStyle(STARTING_WINDOW_TYPE_SPLASH_SCREEN) + .build(); + view.setNotCopyable(); + return view; + } + private SplashScreenView makeSplashScreenContentView(Context context, StartingWindowInfo info, @StartingWindowType int suggestType, Consumer<Runnable> uiThreadInitConsumer) { updateDensity(); @@ -263,7 +428,8 @@ public class SplashscreenContentDrawer { final int themeBGColor = legacyDrawable != null ? getBGColorFromCache(ai, () -> estimateWindowBGColor(legacyDrawable)) : getBGColorFromCache(ai, () -> peekWindowBGColor(context, mTmpAttrs)); - return new StartingWindowViewBuilder(context, ai) + + return new SplashViewBuilder(context, ai) .setWindowBGColor(themeBGColor) .overlayDrawable(legacyDrawable) .chooseStyle(suggestType) @@ -322,6 +488,14 @@ public class SplashscreenContentDrawer { private Drawable mSplashScreenIcon = null; private Drawable mBrandingImage = null; private int mIconBgColor = Color.TRANSPARENT; + + void reset() { + mWindowBgResId = 0; + mWindowBgColor = Color.TRANSPARENT; + mSplashScreenIcon = null; + mBrandingImage = null; + mIconBgColor = Color.TRANSPARENT; + } } /** @@ -351,7 +525,7 @@ public class SplashscreenContentDrawer { return appReadyDuration; } - private class StartingWindowViewBuilder { + private class SplashViewBuilder { private final Context mContext; private final ActivityInfo mActivityInfo; @@ -364,27 +538,28 @@ public class SplashscreenContentDrawer { /** @see #setAllowHandleSolidColor(boolean) **/ private boolean mAllowHandleSolidColor; - StartingWindowViewBuilder(@NonNull Context context, @NonNull ActivityInfo aInfo) { + SplashViewBuilder(@NonNull Context context, @NonNull ActivityInfo aInfo) { mContext = context; mActivityInfo = aInfo; } - StartingWindowViewBuilder setWindowBGColor(@ColorInt int background) { + SplashViewBuilder setWindowBGColor(@ColorInt int background) { mThemeColor = background; return this; } - StartingWindowViewBuilder overlayDrawable(Drawable overlay) { + SplashViewBuilder overlayDrawable(Drawable overlay) { mOverlayDrawable = overlay; return this; } - StartingWindowViewBuilder chooseStyle(int suggestType) { + SplashViewBuilder chooseStyle(int suggestType) { mSuggestType = suggestType; return this; } - StartingWindowViewBuilder setUiThreadInitConsumer(Consumer<Runnable> uiThreadInitTask) { + // Set up the UI thread for the View. + SplashViewBuilder setUiThreadInitConsumer(Consumer<Runnable> uiThreadInitTask) { mUiThreadInitTask = uiThreadInitTask; return this; } @@ -395,7 +570,7 @@ public class SplashscreenContentDrawer { * android.window.SplashScreen.OnExitAnimationListener#onSplashScreenExit(SplashScreenView)} * callback, effectively copying the {@link SplashScreenView} into the client process. */ - StartingWindowViewBuilder setAllowHandleSolidColor(boolean allowHandleSolidColor) { + SplashViewBuilder setAllowHandleSolidColor(boolean allowHandleSolidColor) { mAllowHandleSolidColor = allowHandleSolidColor; return this; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java index 7f6bfd23f72b..e419462012e3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java @@ -62,7 +62,7 @@ public class SplashscreenIconDrawableFactory { */ static Drawable[] makeIconDrawable(@ColorInt int backgroundColor, @ColorInt int themeColor, @NonNull Drawable foregroundDrawable, int srcIconSize, int iconSize, - boolean loadInDetail, Handler splashscreenWorkerHandler) { + boolean loadInDetail, Handler preDrawHandler) { Drawable foreground; Drawable background = null; boolean drawBackground = @@ -74,13 +74,13 @@ public class SplashscreenIconDrawableFactory { // If the icon is Adaptive, we already use the icon background. drawBackground = false; foreground = new ImmobileIconDrawable(foregroundDrawable, - srcIconSize, iconSize, loadInDetail, splashscreenWorkerHandler); + srcIconSize, iconSize, loadInDetail, preDrawHandler); } else { // Adaptive icon don't handle transparency so we draw the background of the adaptive // icon with the same color as the window background color instead of using two layers foreground = new ImmobileIconDrawable( new AdaptiveForegroundDrawable(foregroundDrawable), - srcIconSize, iconSize, loadInDetail, splashscreenWorkerHandler); + srcIconSize, iconSize, loadInDetail, preDrawHandler); } if (drawBackground) { @@ -91,9 +91,9 @@ public class SplashscreenIconDrawableFactory { } static Drawable[] makeLegacyIconDrawable(@NonNull Drawable iconDrawable, int srcIconSize, - int iconSize, boolean loadInDetail, Handler splashscreenWorkerHandler) { + int iconSize, boolean loadInDetail, Handler preDrawHandler) { return new Drawable[]{new ImmobileIconDrawable(iconDrawable, srcIconSize, iconSize, - loadInDetail, splashscreenWorkerHandler)}; + loadInDetail, preDrawHandler)}; } /** @@ -107,14 +107,14 @@ public class SplashscreenIconDrawableFactory { private Bitmap mIconBitmap; ImmobileIconDrawable(Drawable drawable, int srcIconSize, int iconSize, boolean loadInDetail, - Handler splashscreenWorkerHandler) { + Handler preDrawHandler) { // This icon has lower density, don't scale it. if (loadInDetail) { - splashscreenWorkerHandler.post(() -> preDrawIcon(drawable, iconSize)); + preDrawHandler.post(() -> preDrawIcon(drawable, iconSize)); } else { final float scale = (float) iconSize / srcIconSize; mMatrix.setScale(scale, scale); - splashscreenWorkerHandler.post(() -> preDrawIcon(drawable, srcIconSize)); + preDrawHandler.post(() -> preDrawIcon(drawable, srcIconSize)); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java index ff6f2b03c02c..ae3f8e98196f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java @@ -16,7 +16,6 @@ package com.android.wm.shell.startingsurface; -import static android.content.Context.CONTEXT_RESTRICTED; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Choreographer.CALLBACK_INSETS_ANIMATION; import static android.view.Display.DEFAULT_DISPLAY; @@ -32,8 +31,6 @@ import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; -import android.content.res.Configuration; -import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.PixelFormat; @@ -198,118 +195,21 @@ public class StartingSurfaceDrawer { if (activityInfo == null || activityInfo.packageName == null) { return; } - - final int displayId = taskInfo.displayId; - final int taskId = taskInfo.taskId; - // replace with the default theme if the application didn't set final int theme = getSplashScreenTheme(windowInfo.splashScreenThemeResId, activityInfo); - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, - "addSplashScreen for package: %s with theme: %s for task: %d, suggestType: %d", - activityInfo.packageName, Integer.toHexString(theme), taskId, suggestType); - final Display display = getDisplay(displayId); - if (display == null) { - // Can't show splash screen on requested display, so skip showing at all. - return; - } - Context context = displayId == DEFAULT_DISPLAY - ? mContext : mContext.createDisplayContext(display); + final Context context = SplashscreenContentDrawer.createContext(mContext, windowInfo, theme, + suggestType, mDisplayManager); if (context == null) { return; } - if (theme != context.getThemeResId()) { - try { - context = context.createPackageContextAsUser(activityInfo.packageName, - CONTEXT_RESTRICTED, UserHandle.of(taskInfo.userId)); - context.setTheme(theme); - } catch (PackageManager.NameNotFoundException e) { - Slog.w(TAG, "Failed creating package context with package name " - + activityInfo.packageName + " for user " + taskInfo.userId, e); - return; - } - } + final WindowManager.LayoutParams params = SplashscreenContentDrawer.createLayoutParameters( + context, windowInfo, suggestType, activityInfo.packageName, + suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN + ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT, appToken); - final Configuration taskConfig = taskInfo.getConfiguration(); - if (taskConfig.diffPublicOnly(context.getResources().getConfiguration()) != 0) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, - "addSplashScreen: creating context based on task Configuration %s", - taskConfig); - final Context overrideContext = context.createConfigurationContext(taskConfig); - overrideContext.setTheme(theme); - final TypedArray typedArray = overrideContext.obtainStyledAttributes( - com.android.internal.R.styleable.Window); - final int resId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0); - try { - if (resId != 0 && overrideContext.getDrawable(resId) != null) { - // We want to use the windowBackground for the override context if it is - // available, otherwise we use the default one to make sure a themed starting - // window is displayed for the app. - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, - "addSplashScreen: apply overrideConfig %s", - taskConfig); - context = overrideContext; - } - } catch (Resources.NotFoundException e) { - Slog.w(TAG, "failed creating starting window for overrideConfig at taskId: " - + taskId, e); - return; - } - typedArray.recycle(); - } - - final WindowManager.LayoutParams params = new WindowManager.LayoutParams( - WindowManager.LayoutParams.TYPE_APPLICATION_STARTING); - params.setFitInsetsSides(0); - params.setFitInsetsTypes(0); - params.format = suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN - ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT; - int windowFlags = WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED - | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; - final TypedArray a = context.obtainStyledAttributes(R.styleable.Window); - if (a.getBoolean(R.styleable.Window_windowShowWallpaper, false)) { - windowFlags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; - } - if (suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) { - if (a.getBoolean(R.styleable.Window_windowDrawsSystemBarBackgrounds, false)) { - windowFlags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; - } - } else { - windowFlags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; - } - params.layoutInDisplayCutoutMode = a.getInt( - R.styleable.Window_windowLayoutInDisplayCutoutMode, - params.layoutInDisplayCutoutMode); - params.windowAnimations = a.getResourceId(R.styleable.Window_windowAnimationStyle, 0); - a.recycle(); - - // Assumes it's safe to show starting windows of launched apps while - // the keyguard is being hidden. This is okay because starting windows never show - // secret information. - // TODO(b/113840485): Occluded may not only happen on default display - if (displayId == DEFAULT_DISPLAY && windowInfo.isKeyguardOccluded) { - windowFlags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; - } - - // Force the window flags: this is a fake window, so it is not really - // touchable or focusable by the user. We also add in the ALT_FOCUSABLE_IM - // flag because we do know that the next window will take input - // focus, so we want to get the IME window up on top of us right away. - // Touches will only pass through to the host activity window and will be blocked from - // passing to any other windows. - windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE - | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; - params.flags = windowFlags; - params.token = appToken; - params.packageName = activityInfo.packageName; - params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; - - if (!context.getResources().getCompatibilityInfo().supportsScreen()) { - params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW; - } - - params.setTitle("Splash Screen " + activityInfo.packageName); + final int displayId = taskInfo.displayId; + final int taskId = taskInfo.taskId; + final Display display = getDisplay(displayId); // TODO(b/173975965) tracking performance // Prepare the splash screen content view on splash screen worker thread in parallel, so the diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java index 3929e835cdaa..9d6711f42efe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java @@ -18,50 +18,16 @@ package com.android.wm.shell.startingsurface; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.graphics.Color.WHITE; -import static android.graphics.Color.alpha; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; -import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; -import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; -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.PRIVATE_FLAG_TRUSTED_OVERLAY; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST; 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.getNavigationBarRect; - import android.annotation.BinderThread; import android.annotation.NonNull; -import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManager.TaskDescription; -import android.app.ActivityThread; -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.GraphicBuffer; -import android.graphics.Matrix; import android.graphics.Paint; -import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; -import android.graphics.RectF; -import android.hardware.HardwareBuffer; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; @@ -73,20 +39,14 @@ import android.view.InputChannel; import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.SurfaceControl; -import android.view.SurfaceSession; import android.view.View; -import android.view.ViewGroup; -import android.view.WindowInsets; -import android.view.WindowInsets.Type.InsetsType; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.window.ClientWindowFrames; +import android.window.SnapshotDrawerUtils; import android.window.StartingWindowInfo; import android.window.TaskSnapshot; -import com.android.internal.R; -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.policy.DecorView; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.view.BaseIWindow; import com.android.wm.shell.common.ShellExecutor; @@ -100,27 +60,8 @@ import java.lang.ref.WeakReference; * @hide */ public class TaskSnapshotWindow { - /** - * 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. - */ - 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 String TAG = StartingWindowController.TAG; - private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s"; + private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId="; private static final long DELAY_REMOVAL_TIME_GENERAL = 100; /** @@ -133,25 +74,12 @@ public class TaskSnapshotWindow { private final Window mWindow; private final Runnable mClearWindowHandler; private final ShellExecutor mSplashScreenExecutor; - private final SurfaceControl mSurfaceControl; private final IWindowSession mSession; - private final Rect mTaskBounds; - private final Rect mFrame = new Rect(); - private final Rect mSystemBarInsets = new Rect(); - private TaskSnapshot mSnapshot; - private final RectF mTmpSnapshotSize = new RectF(); - private final RectF mTmpDstFrame = new RectF(); - private final CharSequence mTitle; private boolean mHasDrawn; - private boolean mSizeMismatch; private final Paint mBackgroundPaint = new Paint(); private final int mActivityType; - private final int mStatusBarColor; - private final SystemBarBackgroundPainter mSystemBarBackgroundPainter; private final int mOrientationOnCreation; - private final SurfaceControl.Transaction mTransaction; - private final Matrix mSnapshotMatrix = new Matrix(); - private final float[] mTmpFloat9 = new float[9]; + private final Runnable mScheduledRunnable = this::removeImmediately; private final boolean mHasImeSurface; @@ -163,42 +91,15 @@ public class TaskSnapshotWindow { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, "create taskSnapshot surface for task: %d", taskId); - final WindowManager.LayoutParams attrs = info.topOpaqueWindowLayoutParams; - final WindowManager.LayoutParams mainWindowParams = info.mainWindowLayoutParams; final InsetsState topWindowInsetsState = info.topOpaqueWindowInsetsState; - if (attrs == null || mainWindowParams == null || topWindowInsetsState == null) { - Slog.w(TAG, "unable to create taskSnapshot surface for task: " + taskId); + + final WindowManager.LayoutParams layoutParams = SnapshotDrawerUtils.createLayoutParameters( + info, TITLE_FORMAT + taskId, TYPE_APPLICATION_STARTING, + snapshot.getHardwareBuffer().getFormat(), appToken); + if (layoutParams == null) { + Slog.e(TAG, "TaskSnapshotWindow no layoutParams"); return null; } - final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(); - - final int appearance = attrs.insetsFlags.appearance; - final int windowFlags = attrs.flags; - final int windowPrivateFlags = attrs.privateFlags; - - layoutParams.packageName = mainWindowParams.packageName; - layoutParams.windowAnimations = mainWindowParams.windowAnimations; - layoutParams.dimAmount = mainWindowParams.dimAmount; - layoutParams.type = TYPE_APPLICATION_STARTING; - layoutParams.format = snapshot.getHardwareBuffer().getFormat(); - layoutParams.flags = (windowFlags & ~FLAG_INHERIT_EXCLUDES) - | FLAG_NOT_FOCUSABLE - | FLAG_NOT_TOUCHABLE; - // Setting as trusted overlay to let touches pass through. This is safe because this - // window is controlled by the system. - layoutParams.privateFlags = (windowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) - | PRIVATE_FLAG_TRUSTED_OVERLAY | PRIVATE_FLAG_USE_BLAST; - layoutParams.token = appToken; - layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT; - layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT; - layoutParams.insetsFlags.appearance = appearance; - layoutParams.insetsFlags.behavior = attrs.insetsFlags.behavior; - layoutParams.layoutInDisplayCutoutMode = attrs.layoutInDisplayCutoutMode; - layoutParams.setFitInsetsTypes(attrs.getFitInsetsTypes()); - layoutParams.setFitInsetsSides(attrs.getFitInsetsSides()); - layoutParams.setFitInsetsIgnoringVisibility(attrs.isFitInsetsIgnoringVisibility()); - - layoutParams.setTitle(String.format(TITLE_FORMAT, taskId)); final Point taskSize = snapshot.getTaskSize(); final Rect taskBounds = new Rect(0, 0, taskSize.x, taskSize.y); @@ -222,9 +123,8 @@ public class TaskSnapshotWindow { } final TaskSnapshotWindow snapshotSurface = new TaskSnapshotWindow( - surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, appearance, - windowFlags, windowPrivateFlags, taskBounds, orientation, activityType, - info.requestedVisibleTypes, clearWindowHandler, splashScreenExecutor); + snapshot, taskDescription, orientation, activityType, + clearWindowHandler, splashScreenExecutor); final Window window = snapshotSurface.mWindow; final InsetsState tmpInsetsState = new InsetsState(); @@ -255,33 +155,25 @@ public class TaskSnapshotWindow { snapshotSurface.clearWindowSynced(); } - final Rect systemBarInsets = getSystemBarInsets(tmpFrames.frame, topWindowInsetsState); - snapshotSurface.setFrames(tmpFrames.frame, systemBarInsets); - snapshotSurface.drawSnapshot(); + SnapshotDrawerUtils.drawSnapshotOnSurface(info, layoutParams, surfaceControl, snapshot, + taskBounds, tmpFrames.frame, topWindowInsetsState, true /* releaseAfterDraw */); + snapshotSurface.mHasDrawn = true; + snapshotSurface.reportDrawn(); + return snapshotSurface; } - public TaskSnapshotWindow(SurfaceControl surfaceControl, - TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription, - int appearance, int windowFlags, int windowPrivateFlags, Rect taskBounds, - int currentOrientation, int activityType, @InsetsType int requestedVisibleTypes, - Runnable clearWindowHandler, ShellExecutor splashScreenExecutor) { + public TaskSnapshotWindow(TaskSnapshot snapshot, TaskDescription taskDescription, + int currentOrientation, int activityType, Runnable clearWindowHandler, + ShellExecutor splashScreenExecutor) { mSplashScreenExecutor = splashScreenExecutor; mSession = WindowManagerGlobal.getWindowSession(); mWindow = new Window(); mWindow.setSession(mSession); - mSurfaceControl = surfaceControl; - mSnapshot = snapshot; - mTitle = title; int backgroundColor = taskDescription.getBackgroundColor(); mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE); - mTaskBounds = taskBounds; - mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags, - windowPrivateFlags, appearance, taskDescription, 1f, requestedVisibleTypes); - mStatusBarColor = taskDescription.getStatusBarColor(); mOrientationOnCreation = currentOrientation; mActivityType = activityType; - mTransaction = new SurfaceControl.Transaction(); mClearWindowHandler = clearWindowHandler; mHasImeSurface = snapshot.hasImeSurface(); } @@ -294,23 +186,6 @@ public class TaskSnapshotWindow { return mHasImeSurface; } - /** - * Ask system bar background painter to draw status bar background. - * @hide - */ - public void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame) { - mSystemBarBackgroundPainter.drawStatusBarBackground(c, alreadyDrawnFrame, - mSystemBarBackgroundPainter.getStatusBarColorViewHeight()); - } - - /** - * Ask system bar background painter to draw navigation bar background. - * @hide - */ - public void drawNavigationBarBackground(Canvas c) { - mSystemBarBackgroundPainter.drawNavigationBarBackground(c); - } - void scheduleRemove(boolean deferRemoveForIme) { // Show the latest content as soon as possible for unlocking to home. if (mActivityType == ACTIVITY_TYPE_HOME) { @@ -338,178 +213,6 @@ public class TaskSnapshotWindow { } /** - * Set frame size. - * @hide - */ - public void setFrames(Rect frame, Rect systemBarInsets) { - mFrame.set(frame); - mSystemBarInsets.set(systemBarInsets); - final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer(); - mSizeMismatch = (mFrame.width() != snapshot.getWidth() - || mFrame.height() != snapshot.getHeight()); - mSystemBarBackgroundPainter.setInsets(systemBarInsets); - } - - static Rect getSystemBarInsets(Rect frame, InsetsState state) { - return state.calculateInsets(frame, WindowInsets.Type.systemBars(), - false /* ignoreVisibility */).toRect(); - } - - private void drawSnapshot() { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, - "Drawing snapshot surface sizeMismatch=%b", 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(); - } - mHasDrawn = true; - reportDrawn(); - - // In case window manager leaks us, make sure we don't retain the snapshot. - if (mSnapshot.getHardwareBuffer() != null) { - mSnapshot.getHardwareBuffer().close(); - } - mSnapshot = null; - mSurfaceControl.release(); - } - - private void drawSizeMatchSnapshot() { - mTransaction.setBuffer(mSurfaceControl, mSnapshot.getHardwareBuffer()) - .setColorSpace(mSurfaceControl, mSnapshot.getColorSpace()) - .apply(); - } - - private void drawSizeMismatchSnapshot() { - final HardwareBuffer buffer = mSnapshot.getHardwareBuffer(); - 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. - SurfaceControl childSurfaceControl = new SurfaceControl.Builder(session) - .setName(mTitle + " - task-snapshot-surface") - .setBLASTLayer() - .setFormat(buffer.getFormat()) - .setParent(mSurfaceControl) - .setCallsite("TaskSnapshotWindow.drawSizeMismatchSnapshot") - .build(); - - final Rect frame; - // We can just show the surface here as it will still be hidden as the parent is - // still hidden. - mTransaction.show(childSurfaceControl); - if (aspectRatioMismatch) { - // Clip off ugly navigation bar. - final Rect crop = calculateSnapshotCrop(); - frame = calculateSnapshotFrame(crop); - mTransaction.setWindowCrop(childSurfaceControl, crop); - mTransaction.setPosition(childSurfaceControl, frame.left, frame.top); - mTmpSnapshotSize.set(crop); - mTmpDstFrame.set(frame); - } else { - frame = null; - mTmpSnapshotSize.set(0, 0, buffer.getWidth(), buffer.getHeight()); - mTmpDstFrame.set(mFrame); - mTmpDstFrame.offsetTo(0, 0); - } - - // Scale the mismatch dimensions to fill the task bounds - mSnapshotMatrix.setRectToRect(mTmpSnapshotSize, mTmpDstFrame, Matrix.ScaleToFit.FILL); - mTransaction.setMatrix(childSurfaceControl, mSnapshotMatrix, mTmpFloat9); - mTransaction.setColorSpace(childSurfaceControl, mSnapshot.getColorSpace()); - mTransaction.setBuffer(childSurfaceControl, mSnapshot.getHardwareBuffer()); - - if (aspectRatioMismatch) { - GraphicBuffer background = GraphicBuffer.create(mFrame.width(), mFrame.height(), - PixelFormat.RGBA_8888, - GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_HW_COMPOSER - | GraphicBuffer.USAGE_SW_WRITE_RARELY); - // TODO: Support this on HardwareBuffer - final Canvas c = background.lockCanvas(); - drawBackgroundAndBars(c, frame); - background.unlockCanvasAndPost(c); - mTransaction.setBuffer(mSurfaceControl, - HardwareBuffer.createFromGraphicBuffer(background)); - } - mTransaction.apply(); - childSurfaceControl.release(); - } - - /** - * Calculates the snapshot crop in snapshot coordinate space. - * - * @return crop rect in snapshot coordinate space. - */ - public Rect calculateSnapshotCrop() { - final Rect rect = new Rect(); - final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer(); - rect.set(0, 0, snapshot.getWidth(), snapshot.getHeight()); - final Rect insets = mSnapshot.getContentInsets(); - - final float scaleX = (float) snapshot.getWidth() / mSnapshot.getTaskSize().x; - final float scaleY = (float) snapshot.getHeight() / mSnapshot.getTaskSize().y; - - // 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 * scaleX), - isTop ? 0 : (int) (insets.top * scaleY), - (int) (insets.right * scaleX), - (int) (insets.bottom * scaleY)); - return rect; - } - - /** - * Calculates the snapshot frame in window coordinate space from crop. - * - * @param crop rect that is in snapshot coordinate space. - */ - public Rect calculateSnapshotFrame(Rect crop) { - final HardwareBuffer snapshot = mSnapshot.getHardwareBuffer(); - final float scaleX = (float) snapshot.getWidth() / mSnapshot.getTaskSize().x; - final float scaleY = (float) snapshot.getHeight() / mSnapshot.getTaskSize().y; - - // Rescale the frame from snapshot to window coordinate space - final Rect frame = new Rect(0, 0, - (int) (crop.width() / scaleX + 0.5f), - (int) (crop.height() / scaleY + 0.5f) - ); - - // However, we also need to make space for the navigation bar on the left side. - frame.offset(mSystemBarInsets.left, 0); - return frame; - } - - /** - * Draw status bar and navigation bar background. - * @hide - */ - public 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); - } - - /** * Clear window from drawer, must be post on main executor. */ private void clearWindowSynced() { @@ -557,92 +260,4 @@ public class TaskSnapshotWindow { }); } } - - /** - * 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 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 float mScale; - private final @InsetsType int mRequestedVisibleTypes; - private final Rect mSystemBarInsets = new Rect(); - - SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int appearance, - TaskDescription taskDescription, float scale, - @InsetsType int requestedVisibleTypes) { - mWindowFlags = windowFlags; - mWindowPrivateFlags = windowPrivateFlags; - 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(), appearance, - APPEARANCE_LIGHT_STATUS_BARS, - taskDescription.getEnsureStatusBarContrastWhenTransparent()); - mNavigationBarColor = DecorView.calculateBarColor(windowFlags, - FLAG_TRANSLUCENT_NAVIGATION, semiTransparent, - taskDescription.getNavigationBarColor(), appearance, - APPEARANCE_LIGHT_NAVIGATION_BARS, - taskDescription.getEnsureNavigationBarContrastWhenTransparent() - && context.getResources().getBoolean(R.bool.config_navBarNeedsScrim)); - mStatusBarPaint.setColor(mStatusBarColor); - mNavigationBarPaint.setColor(mNavigationBarColor); - mRequestedVisibleTypes = requestedVisibleTypes; - } - - void setInsets(Rect systemBarInsets) { - mSystemBarInsets.set(systemBarInsets); - } - - int getStatusBarColorViewHeight() { - final boolean forceBarBackground = - (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0; - if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible( - mRequestedVisibleTypes, mStatusBarColor, mWindowFlags, forceBarBackground)) { - return (int) (mSystemBarInsets.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( - mRequestedVisibleTypes, mNavigationBarColor, mWindowFlags, forceBarBackground); - } - - void drawDecors(Canvas c, @Nullable Rect alreadyDrawnFrame) { - drawStatusBarBackground(c, alreadyDrawnFrame, getStatusBarColorViewHeight()); - drawNavigationBarBackground(c); - } - - 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) (mSystemBarInsets.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(), mSystemBarInsets, navigationBarRect, - mScale); - final boolean visible = isNavigationBarColorViewVisible(); - if (visible && Color.alpha(mNavigationBarColor) != 0 && !navigationBarRect.isEmpty()) { - c.drawRect(navigationBarRect, mNavigationBarPaint); - } - } - } } diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index 29c98b9f8b01..c1b9e662df9d 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -16,29 +16,14 @@ package com.android.server.wm; -import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; -import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; -import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; -import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS; - -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.getNavigationBarRect; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.ActivityManager; -import android.app.ActivityThread; -import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.RecordingCanvas; @@ -59,12 +44,11 @@ import android.view.WindowInsets.Type; import android.view.WindowInsetsController.Appearance; import android.view.WindowManager.LayoutParams; import android.window.ScreenCapture; +import android.window.SnapshotDrawerUtils; import android.window.TaskSnapshot; -import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; -import com.android.internal.policy.DecorView; import com.android.server.policy.WindowManagerPolicy.ScreenOffListener; import com.android.server.wm.utils.InsetUtils; @@ -585,7 +569,8 @@ class TaskSnapshotController { final Rect taskBounds = task.getBounds(); final InsetsState insetsState = mainWindow.getInsetsStateWithVisibilityOverride(); final Rect systemBarInsets = getSystemBarInsets(mainWindow.getFrame(), insetsState); - final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags, + final SnapshotDrawerUtils.SystemBarBackgroundPainter decorPainter = + new SnapshotDrawerUtils.SystemBarBackgroundPainter(attrs.flags, attrs.privateFlags, attrs.insetsFlags.appearance, task.getTaskDescription(), mHighResTaskSnapshotScale, mainWindow.getRequestedVisibleTypes()); final int taskWidth = taskBounds.width(); @@ -599,7 +584,7 @@ class TaskSnapshotController { final RecordingCanvas c = node.start(width, height); c.drawColor(color); decorPainter.setInsets(systemBarInsets); - decorPainter.drawDecors(c /* statusBarExcludeFrame */); + decorPainter.drawDecors(c /* statusBarExcludeFrame */, null /* alreadyDrawnFrame */); node.end(c); final Bitmap hwBitmap = ThreadedRenderer.createHardwareBitmap(node, width, height); if (hwBitmap == null) { @@ -736,92 +721,4 @@ class TaskSnapshotController { pw.println(prefix + "mTaskSnapshotEnabled=" + mTaskSnapshotEnabled); mCache.dump(pw, prefix); } - - /** - * 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 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 float mScale; - private final @Type.InsetsType int mRequestedVisibleTypes; - private final Rect mSystemBarInsets = new Rect(); - - SystemBarBackgroundPainter(int windowFlags, int windowPrivateFlags, int appearance, - ActivityManager.TaskDescription taskDescription, float scale, - @Type.InsetsType int requestedVisibleTypes) { - mWindowFlags = windowFlags; - mWindowPrivateFlags = windowPrivateFlags; - 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(), appearance, - APPEARANCE_LIGHT_STATUS_BARS, - taskDescription.getEnsureStatusBarContrastWhenTransparent()); - mNavigationBarColor = DecorView.calculateBarColor(windowFlags, - FLAG_TRANSLUCENT_NAVIGATION, semiTransparent, - taskDescription.getNavigationBarColor(), appearance, - APPEARANCE_LIGHT_NAVIGATION_BARS, - taskDescription.getEnsureNavigationBarContrastWhenTransparent() - && context.getResources().getBoolean(R.bool.config_navBarNeedsScrim)); - mStatusBarPaint.setColor(mStatusBarColor); - mNavigationBarPaint.setColor(mNavigationBarColor); - mRequestedVisibleTypes = requestedVisibleTypes; - } - - void setInsets(Rect systemBarInsets) { - mSystemBarInsets.set(systemBarInsets); - } - - int getStatusBarColorViewHeight() { - final boolean forceBarBackground = - (mWindowPrivateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0; - if (STATUS_BAR_COLOR_VIEW_ATTRIBUTES.isVisible( - mRequestedVisibleTypes, mStatusBarColor, mWindowFlags, forceBarBackground)) { - return (int) (mSystemBarInsets.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( - mRequestedVisibleTypes, mNavigationBarColor, mWindowFlags, forceBarBackground); - } - - void drawDecors(Canvas c) { - drawStatusBarBackground(c, getStatusBarColorViewHeight()); - drawNavigationBarBackground(c); - } - - @VisibleForTesting - void drawStatusBarBackground(Canvas c, - int statusBarHeight) { - if (statusBarHeight > 0 && Color.alpha(mStatusBarColor) != 0) { - final int rightInset = (int) (mSystemBarInsets.right * mScale); - c.drawRect(0, 0, c.getWidth() - rightInset, statusBarHeight, mStatusBarPaint); - } - } - - @VisibleForTesting - void drawNavigationBarBackground(Canvas c) { - final Rect navigationBarRect = new Rect(); - getNavigationBarRect(c.getWidth(), c.getHeight(), mSystemBarInsets, navigationBarRect, - mScale); - final boolean visible = isNavigationBarColorViewVisible(); - if (visible && Color.alpha(mNavigationBarColor) != 0 && !navigationBarRect.isEmpty()) { - c.drawRect(navigationBarRect, mNavigationBarPaint); - } - } - } } |