diff --git a/core/java/android/window/ITaskOrganizer.aidl b/core/java/android/window/ITaskOrganizer.aidl index bf9d3c2eefaf9..fd86769293a6e 100644 --- a/core/java/android/window/ITaskOrganizer.aidl +++ b/core/java/android/window/ITaskOrganizer.aidl @@ -34,8 +34,9 @@ oneway interface ITaskOrganizer { * has create a starting window for the Task. * * @param info The information about the Task that's available + * @param appToken Token of the application being started. */ - void addStartingWindow(in StartingWindowInfo info); + void addStartingWindow(in StartingWindowInfo info, IBinder appToken); /** * Called when the Task want to remove the starting window. diff --git a/core/java/android/window/IWindowlessStartingSurfaceCallback.aidl b/core/java/android/window/IWindowlessStartingSurfaceCallback.aidl deleted file mode 100644 index a0813565b2566..0000000000000 --- a/core/java/android/window/IWindowlessStartingSurfaceCallback.aidl +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2023 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 android.view.SurfaceControl; - -/** - * Interface to be invoked when a windowless starting surface added. - * - * @param addedSurface The starting surface. - * {@hide} - */ -interface IWindowlessStartingSurfaceCallback { - void onSurfaceAdded(in SurfaceControl addedSurface); -} diff --git a/core/java/android/window/SnapshotDrawerUtils.java b/core/java/android/window/SnapshotDrawerUtils.java index 071c20f25e5c2..1a58fd5566096 100644 --- a/core/java/android/window/SnapshotDrawerUtils.java +++ b/core/java/android/window/SnapshotDrawerUtils.java @@ -328,21 +328,6 @@ public class SnapshotDrawerUtils { - ((float) frame.width() / frame.height())) <= 0.01f; } - /** - * Get or create a TaskDescription from a RunningTaskInfo. - */ - public static ActivityManager.TaskDescription getOrCreateTaskDescription( - ActivityManager.RunningTaskInfo runningTaskInfo) { - final ActivityManager.TaskDescription taskDescription; - if (runningTaskInfo.taskDescription != null) { - taskDescription = runningTaskInfo.taskDescription; - } else { - taskDescription = new ActivityManager.TaskDescription(); - taskDescription.setBackgroundColor(WHITE); - } - return taskDescription; - } - /** * Help method to draw the snapshot on a surface. */ @@ -359,8 +344,13 @@ public class SnapshotDrawerUtils { final WindowManager.LayoutParams attrs = info.topOpaqueWindowLayoutParams; final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo; - final ActivityManager.TaskDescription taskDescription = - getOrCreateTaskDescription(runningTaskInfo); + 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); diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java index 451acbe84a605..1b64e613ed660 100644 --- a/core/java/android/window/StartingWindowInfo.java +++ b/core/java/android/window/StartingWindowInfo.java @@ -22,12 +22,9 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.TaskInfo; import android.content.pm.ActivityInfo; -import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; -import android.os.RemoteException; import android.view.InsetsState; -import android.view.SurfaceControl; import android.view.WindowInsets; import android.view.WindowInsets.Type.InsetsType; import android.view.WindowManager; @@ -62,8 +59,6 @@ public final class StartingWindowInfo implements Parcelable { /** @hide **/ public static final int STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN = 4; - public static final int STARTING_WINDOW_TYPE_WINDOWLESS = 5; - /** * @hide */ @@ -72,8 +67,7 @@ public final class StartingWindowInfo implements Parcelable { STARTING_WINDOW_TYPE_SPLASH_SCREEN, STARTING_WINDOW_TYPE_SNAPSHOT, STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN, - STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN, - STARTING_WINDOW_TYPE_WINDOWLESS + STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN }) public @interface StartingWindowType {} @@ -124,7 +118,6 @@ public final class StartingWindowInfo implements Parcelable { TYPE_PARAMETER_ACTIVITY_CREATED, TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN, TYPE_PARAMETER_ALLOW_HANDLE_SOLID_COLOR_SCREEN, - TYPE_PARAMETER_WINDOWLESS, TYPE_PARAMETER_LEGACY_SPLASH_SCREEN }) public @interface StartingTypeParams {} @@ -158,12 +151,6 @@ public final class StartingWindowInfo implements Parcelable { * @hide */ public static final int TYPE_PARAMETER_ALLOW_HANDLE_SOLID_COLOR_SCREEN = 0x00000080; - - /** - * Windowless surface - */ - public static final int TYPE_PARAMETER_WINDOWLESS = 0x00000100; - /** * Application is allowed to use the legacy splash screen * @hide @@ -195,33 +182,7 @@ public final class StartingWindowInfo implements Parcelable { */ public TaskSnapshot taskSnapshot; - @InsetsType public int requestedVisibleTypes = WindowInsets.Type.defaultVisible(); - - /** - * App token where the starting window should add to. - */ - public IBinder appToken; - - public IWindowlessStartingSurfaceCallback windowlessStartingSurfaceCallback; - - /** - * The root surface where windowless surface should attach on. - */ - public SurfaceControl rootSurface; - - /** - * Notify windowless surface is created. - * @param addedSurface Created surface. - */ - public void notifyAddComplete(SurfaceControl addedSurface) { - if (windowlessStartingSurfaceCallback != null) { - try { - windowlessStartingSurfaceCallback.onSurfaceAdded(addedSurface); - } catch (RemoteException e) { - // - } - } - } + public @InsetsType int requestedVisibleTypes = WindowInsets.Type.defaultVisible(); public StartingWindowInfo() { @@ -255,9 +216,6 @@ public final class StartingWindowInfo implements Parcelable { dest.writeBoolean(isKeyguardOccluded); dest.writeTypedObject(taskSnapshot, flags); dest.writeInt(requestedVisibleTypes); - dest.writeStrongBinder(appToken); - dest.writeStrongInterface(windowlessStartingSurfaceCallback); - dest.writeTypedObject(rootSurface, flags); } void readFromParcel(@NonNull Parcel source) { @@ -272,10 +230,6 @@ public final class StartingWindowInfo implements Parcelable { isKeyguardOccluded = source.readBoolean(); taskSnapshot = source.readTypedObject(TaskSnapshot.CREATOR); requestedVisibleTypes = source.readInt(); - appToken = source.readStrongBinder(); - windowlessStartingSurfaceCallback = IWindowlessStartingSurfaceCallback.Stub - .asInterface(source.readStrongBinder()); - rootSurface = source.readTypedObject(SurfaceControl.CREATOR); } @Override diff --git a/core/java/android/window/StartingWindowRemovalInfo.java b/core/java/android/window/StartingWindowRemovalInfo.java index 518123600b9ae..384dacfe89ed6 100644 --- a/core/java/android/window/StartingWindowRemovalInfo.java +++ b/core/java/android/window/StartingWindowRemovalInfo.java @@ -67,16 +67,6 @@ public final class StartingWindowRemovalInfo implements Parcelable { */ public float roundedCornerRadius; - /** - * Remove windowless surface. - */ - public boolean windowlessSurface; - - /** - * Remove immediately. - */ - public boolean removeImmediately; - public StartingWindowRemovalInfo() { } @@ -97,8 +87,6 @@ public final class StartingWindowRemovalInfo implements Parcelable { playRevealAnimation = source.readBoolean(); deferRemoveForIme = source.readBoolean(); roundedCornerRadius = source.readFloat(); - windowlessSurface = source.readBoolean(); - removeImmediately = source.readBoolean(); } @Override @@ -109,8 +97,6 @@ public final class StartingWindowRemovalInfo implements Parcelable { dest.writeBoolean(playRevealAnimation); dest.writeBoolean(deferRemoveForIme); dest.writeFloat(roundedCornerRadius); - dest.writeBoolean(windowlessSurface); - dest.writeBoolean(removeImmediately); } @Override @@ -119,9 +105,7 @@ public final class StartingWindowRemovalInfo implements Parcelable { + " frame=" + mainFrame + " playRevealAnimation=" + playRevealAnimation + " roundedCornerRadius=" + roundedCornerRadius - + " deferRemoveForIme=" + deferRemoveForIme - + " windowlessSurface=" + windowlessSurface - + " removeImmediately=" + removeImmediately + "}"; + + " deferRemoveForIme=" + deferRemoveForIme + "}"; } public static final @android.annotation.NonNull Creator CREATOR = diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java index d4728c1187d79..02878f8ae72bf 100644 --- a/core/java/android/window/TaskOrganizer.java +++ b/core/java/android/window/TaskOrganizer.java @@ -92,10 +92,13 @@ public class TaskOrganizer extends WindowOrganizer { * has create a starting window for the Task. * * @param info The information about the Task that's available + * @param appToken Token of the application being started. + * context to for resources * @hide */ @BinderThread - public void addStartingWindow(@NonNull StartingWindowInfo info) {} + public void addStartingWindow(@NonNull StartingWindowInfo info, + @NonNull IBinder appToken) {} /** * Called when the Task want to remove the starting window. @@ -294,8 +297,9 @@ public class TaskOrganizer extends WindowOrganizer { private final ITaskOrganizer mInterface = new ITaskOrganizer.Stub() { @Override - public void addStartingWindow(StartingWindowInfo windowInfo) { - mExecutor.execute(() -> TaskOrganizer.this.addStartingWindow(windowInfo)); + public void addStartingWindow(StartingWindowInfo windowInfo, + IBinder appToken) { + mExecutor.execute(() -> TaskOrganizer.this.addStartingWindow(windowInfo, appToken)); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index b6fd0bbafc71c..585f81c81a36b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -428,9 +428,9 @@ public class ShellTaskOrganizer extends TaskOrganizer implements } @Override - public void addStartingWindow(StartingWindowInfo info) { + public void addStartingWindow(StartingWindowInfo info, IBinder appToken) { if (mStartingWindow != null) { - mStartingWindow.addStartingWindow(info); + mStartingWindow.addStartingWindow(info, appToken); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/AbsSplashWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/AbsSplashWindowCreator.java deleted file mode 100644 index 1ddd8f9a3a146..0000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/AbsSplashWindowCreator.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.startingsurface; - -import android.content.Context; -import android.content.pm.ActivityInfo; -import android.hardware.display.DisplayManager; -import android.view.Display; - -import com.android.wm.shell.common.ShellExecutor; - -// abstract class to create splash screen window(or windowless window) -abstract class AbsSplashWindowCreator { - protected static final String TAG = StartingWindowController.TAG; - protected final SplashscreenContentDrawer mSplashscreenContentDrawer; - protected final Context mContext; - protected final DisplayManager mDisplayManager; - protected final ShellExecutor mSplashScreenExecutor; - protected final StartingSurfaceDrawer.StartingWindowRecordManager mStartingWindowRecordManager; - - private StartingSurface.SysuiProxy mSysuiProxy; - - AbsSplashWindowCreator(SplashscreenContentDrawer contentDrawer, Context context, - ShellExecutor splashScreenExecutor, DisplayManager displayManager, - StartingSurfaceDrawer.StartingWindowRecordManager startingWindowRecordManager) { - mSplashscreenContentDrawer = contentDrawer; - mContext = context; - mSplashScreenExecutor = splashScreenExecutor; - mDisplayManager = displayManager; - mStartingWindowRecordManager = startingWindowRecordManager; - } - - int getSplashScreenTheme(int splashScreenThemeResId, ActivityInfo activityInfo) { - return splashScreenThemeResId != 0 - ? splashScreenThemeResId - : activityInfo.getThemeResource() != 0 ? activityInfo.getThemeResource() - : com.android.internal.R.style.Theme_DeviceDefault_DayNight; - } - - protected Display getDisplay(int displayId) { - return mDisplayManager.getDisplay(displayId); - } - - void setSysuiProxy(StartingSurface.SysuiProxy sysuiProxy) { - mSysuiProxy = sysuiProxy; - } - - protected void requestTopUi(boolean requestTopUi) { - if (mSysuiProxy != null) { - mSysuiProxy.requestTopUi(requestTopUi, TAG); - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SnapshotWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SnapshotWindowCreator.java deleted file mode 100644 index 20c4d5ae5f581..0000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SnapshotWindowCreator.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.startingsurface; - -import android.window.StartingWindowInfo; -import android.window.TaskSnapshot; - -import com.android.wm.shell.common.ShellExecutor; - -class SnapshotWindowCreator { - private final ShellExecutor mMainExecutor; - private final StartingSurfaceDrawer.StartingWindowRecordManager - mStartingWindowRecordManager; - - SnapshotWindowCreator(ShellExecutor mainExecutor, - StartingSurfaceDrawer.StartingWindowRecordManager startingWindowRecordManager) { - mMainExecutor = mainExecutor; - mStartingWindowRecordManager = startingWindowRecordManager; - } - - void makeTaskSnapshotWindow(StartingWindowInfo startingWindowInfo, TaskSnapshot snapshot) { - final int taskId = startingWindowInfo.taskInfo.taskId; - // Remove any existing starting window for this task before adding. - mStartingWindowRecordManager.removeWindow(taskId, true); - final TaskSnapshotWindow surface = TaskSnapshotWindow.create(startingWindowInfo, - startingWindowInfo.appToken, snapshot, mMainExecutor, - () -> mStartingWindowRecordManager.removeWindow(taskId, true)); - if (surface != null) { - final SnapshotWindowRecord tView = new SnapshotWindowRecord(surface, - startingWindowInfo.taskInfo.topActivityType, mMainExecutor); - mStartingWindowRecordManager.addRecord(taskId, tView); - } - } - - private static class SnapshotWindowRecord extends StartingSurfaceDrawer.SnapshotRecord { - private final TaskSnapshotWindow mTaskSnapshotWindow; - - SnapshotWindowRecord(TaskSnapshotWindow taskSnapshotWindow, - int activityType, ShellExecutor removeExecutor) { - super(activityType, removeExecutor); - mTaskSnapshotWindow = taskSnapshotWindow; - mBGColor = mTaskSnapshotWindow.getBackgroundColor(); - } - - @Override - protected void removeImmediately() { - super.removeImmediately(); - mTaskSnapshotWindow.removeImmediately(); - } - - @Override - protected boolean hasImeSurface() { - return mTaskSnapshotWindow.hasImeSurface(); - } - } -} 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 79cd891741d67..ebb957b2201bf 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 @@ -24,6 +24,9 @@ import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLA import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; +import static com.android.wm.shell.startingsurface.StartingSurfaceDrawer.MAX_ANIMATION_DURATION; +import static com.android.wm.shell.startingsurface.StartingSurfaceDrawer.MINIMAL_ANIMATION_DURATION; + import android.annotation.ColorInt; import android.annotation.IntDef; import android.annotation.NonNull; @@ -93,25 +96,6 @@ import java.util.function.UnaryOperator; public class SplashscreenContentDrawer { private static final String TAG = StartingWindowController.TAG; - /** - * The minimum duration during which the splash screen is shown when the splash screen icon is - * animated. - */ - static final long MINIMAL_ANIMATION_DURATION = 400L; - - /** - * Allow the icon style splash screen to be displayed for longer to give time for the animation - * to finish, i.e. the extra buffer time to keep the splash screen if the animation is slightly - * longer than the {@link #MINIMAL_ANIMATION_DURATION} duration. - */ - static final long TIME_WINDOW_DURATION = 100L; - - /** - * The maximum duration during which the splash screen will be shown if the application is ready - * to show before the icon animation finishes. - */ - static final long MAX_ANIMATION_DURATION = MINIMAL_ANIMATION_DURATION + TIME_WINDOW_DURATION; - // The acceptable area ratio of foreground_icon_area/background_icon_area, if there is an // icon which it's non-transparent foreground area is similar to it's background area, then // do not enlarge the foreground drawable. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java deleted file mode 100644 index 4db81e232f205..0000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java +++ /dev/null @@ -1,506 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.startingsurface; - -import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; -import static android.view.Choreographer.CALLBACK_INSETS_ANIMATION; -import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN; - -import android.annotation.Nullable; -import android.app.ActivityManager; -import android.app.ActivityTaskManager; -import android.app.ActivityThread; -import android.app.TaskInfo; -import android.content.Context; -import android.content.pm.ActivityInfo; -import android.content.pm.IPackageManager; -import android.content.pm.PackageManager; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.graphics.PixelFormat; -import android.hardware.display.DisplayManager; -import android.os.IBinder; -import android.os.RemoteCallback; -import android.os.RemoteException; -import android.os.SystemClock; -import android.os.Trace; -import android.os.UserHandle; -import android.util.Slog; -import android.util.SparseArray; -import android.view.Choreographer; -import android.view.Display; -import android.view.SurfaceControlViewHost; -import android.view.View; -import android.view.WindowInsetsController; -import android.view.WindowManager; -import android.view.WindowManagerGlobal; -import android.widget.FrameLayout; -import android.window.SplashScreenView; -import android.window.StartingWindowInfo; -import android.window.StartingWindowRemovalInfo; - -import com.android.internal.R; -import com.android.internal.protolog.common.ProtoLog; -import com.android.internal.util.ContrastColorUtil; -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.protolog.ShellProtoLogGroup; - -import java.util.function.Supplier; - -/** - * A class which able to draw splash screen as the starting window for a task. - * - * In order to speed up, there will use two threads to creating a splash screen in parallel. - * Right now we are still using PhoneWindow to create splash screen window, so the view is added to - * the ViewRootImpl, and those view won't be draw immediately because the ViewRootImpl will call - * scheduleTraversal to register a callback from Choreographer, so the drawing result of the view - * can synchronize on each frame. - * - * The bad thing is that we cannot decide when would Choreographer#doFrame happen, and drawing - * the AdaptiveIconDrawable object can be time consuming, so we use the splash-screen background - * thread to draw the AdaptiveIconDrawable object to a Bitmap and cache it to a BitmapShader after - * the SplashScreenView just created, once we get the BitmapShader then the #draw call can be very - * quickly. - * - * So basically we are using the spare time to prepare the SplashScreenView while splash screen - * thread is waiting for - * 1. WindowManager#addView(binder call to WM), - * 2. Choreographer#doFrame happen(uncertain time for next frame, depends on device), - * 3. Session#relayout(another binder call to WM which under Choreographer#doFrame, but will - * always happen before #draw). - * Because above steps are running on splash-screen thread, so pre-draw the BitmapShader on - * splash-screen background tread can make they execute in parallel, which ensure it is faster then - * to draw the AdaptiveIconDrawable when receive callback from Choreographer#doFrame. - * - * Here is the sequence to compare the difference between using single and two thread. - * - * Single thread: - * => makeSplashScreenContentView -> WM#addView .. waiting for Choreographer#doFrame -> relayout - * -> draw -> AdaptiveIconDrawable#draw - * - * Two threads: - * => makeSplashScreenContentView -> cachePaint(=AdaptiveIconDrawable#draw) - * => WM#addView -> .. waiting for Choreographer#doFrame -> relayout -> draw -> (draw the Paint - * directly). - */ -class SplashscreenWindowCreator extends AbsSplashWindowCreator { - private static final int LIGHT_BARS_MASK = - WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS - | WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; - - private final WindowManagerGlobal mWindowManagerGlobal; - private Choreographer mChoreographer; - - /** - * Records of {@link SurfaceControlViewHost} where the splash screen icon animation is - * rendered and that have not yet been removed by their client. - */ - private final SparseArray mAnimatedSplashScreenSurfaceHosts = - new SparseArray<>(1); - - SplashscreenWindowCreator(SplashscreenContentDrawer contentDrawer, Context context, - ShellExecutor splashScreenExecutor, DisplayManager displayManager, - StartingSurfaceDrawer.StartingWindowRecordManager startingWindowRecordManager) { - super(contentDrawer, context, splashScreenExecutor, displayManager, - startingWindowRecordManager); - mSplashScreenExecutor.execute(() -> mChoreographer = Choreographer.getInstance()); - mWindowManagerGlobal = WindowManagerGlobal.getInstance(); - } - - void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, - @StartingWindowInfo.StartingWindowType int suggestType) { - final ActivityManager.RunningTaskInfo taskInfo = windowInfo.taskInfo; - final ActivityInfo activityInfo = windowInfo.targetActivityInfo != null - ? windowInfo.targetActivityInfo - : taskInfo.topActivityInfo; - if (activityInfo == null || activityInfo.packageName == null) { - return; - } - // replace with the default theme if the application didn't set - final int theme = getSplashScreenTheme(windowInfo.splashScreenThemeResId, activityInfo); - final Context context = SplashscreenContentDrawer.createContext(mContext, windowInfo, theme, - suggestType, mDisplayManager); - if (context == null) { - return; - } - final WindowManager.LayoutParams params = SplashscreenContentDrawer.createLayoutParameters( - context, windowInfo, suggestType, activityInfo.packageName, - suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN - ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT, windowInfo.appToken); - - 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 - // content view won't be blocked by binder call like addWindow and relayout. - // 1. Trigger splash screen worker thread to create SplashScreenView before/while - // Session#addWindow. - // 2. Synchronize the SplashscreenView to splash screen thread before Choreographer start - // traversal, which will call Session#relayout on splash screen thread. - // 3. Pre-draw the BitmapShader if the icon is immobile on splash screen worker thread, at - // the same time the splash screen thread should be executing Session#relayout. Blocking the - // traversal -> draw on splash screen thread until the BitmapShader of the icon is ready. - - // Record whether create splash screen view success, notify to current thread after - // create splash screen view finished. - final SplashScreenViewSupplier viewSupplier = new SplashScreenViewSupplier(); - final FrameLayout rootLayout = new FrameLayout( - mSplashscreenContentDrawer.createViewContextWrapper(context)); - rootLayout.setPadding(0, 0, 0, 0); - rootLayout.setFitsSystemWindows(false); - final Runnable setViewSynchronized = () -> { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addSplashScreenView"); - // waiting for setContentView before relayoutWindow - SplashScreenView contentView = viewSupplier.get(); - final StartingSurfaceDrawer.StartingWindowRecord sRecord = - mStartingWindowRecordManager.getRecord(taskId); - final SplashWindowRecord record = sRecord instanceof SplashWindowRecord - ? (SplashWindowRecord) sRecord : null; - // If record == null, either the starting window added fail or removed already. - // Do not add this view if the token is mismatch. - if (record != null && windowInfo.appToken == record.mAppToken) { - // if view == null then creation of content view was failed. - if (contentView != null) { - try { - rootLayout.addView(contentView); - } catch (RuntimeException e) { - Slog.w(TAG, "failed set content view to starting window " - + "at taskId: " + taskId, e); - contentView = null; - } - } - record.setSplashScreenView(contentView); - } - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - }; - requestTopUi(true); - mSplashscreenContentDrawer.createContentView(context, suggestType, windowInfo, - viewSupplier::setView, viewSupplier::setUiThreadInitTask); - try { - if (addWindow(taskId, windowInfo.appToken, rootLayout, display, params, suggestType)) { - // We use the splash screen worker thread to create SplashScreenView while adding - // the window, as otherwise Choreographer#doFrame might be delayed on this thread. - // And since Choreographer#doFrame won't happen immediately after adding the window, - // if the view is not added to the PhoneWindow on the first #doFrame, the view will - // not be rendered on the first frame. So here we need to synchronize the view on - // the window before first round relayoutWindow, which will happen after insets - // animation. - mChoreographer.postCallback(CALLBACK_INSETS_ANIMATION, setViewSynchronized, null); - final SplashWindowRecord record = - (SplashWindowRecord) mStartingWindowRecordManager.getRecord(taskId); - if (record != null) { - record.parseAppSystemBarColor(context); - // Block until we get the background color. - final SplashScreenView contentView = viewSupplier.get(); - if (suggestType != STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) { - contentView.addOnAttachStateChangeListener( - new View.OnAttachStateChangeListener() { - @Override - public void onViewAttachedToWindow(View v) { - final int lightBarAppearance = - ContrastColorUtil.isColorLight( - contentView.getInitBackgroundColor()) - ? LIGHT_BARS_MASK : 0; - contentView.getWindowInsetsController() - .setSystemBarsAppearance( - lightBarAppearance, LIGHT_BARS_MASK); - } - - @Override - public void onViewDetachedFromWindow(View v) { - } - }); - } - } - } else { - // release the icon view host - final SplashScreenView contentView = viewSupplier.get(); - if (contentView.getSurfaceHost() != null) { - SplashScreenView.releaseIconHost(contentView.getSurfaceHost()); - } - } - } catch (RuntimeException e) { - // don't crash if something else bad happens, for example a - // failure loading resources because we are loading from an app - // on external storage that has been unmounted. - Slog.w(TAG, "failed creating starting window at taskId: " + taskId, e); - } - } - - int estimateTaskBackgroundColor(TaskInfo taskInfo) { - if (taskInfo.topActivityInfo == null) { - return Color.TRANSPARENT; - } - final ActivityInfo activityInfo = taskInfo.topActivityInfo; - final String packageName = activityInfo.packageName; - final int userId = taskInfo.userId; - final Context windowContext; - try { - windowContext = mContext.createPackageContextAsUser( - packageName, Context.CONTEXT_RESTRICTED, UserHandle.of(userId)); - } catch (PackageManager.NameNotFoundException e) { - Slog.w(TAG, "Failed creating package context with package name " - + packageName + " for user " + taskInfo.userId, e); - return Color.TRANSPARENT; - } - try { - final IPackageManager packageManager = ActivityThread.getPackageManager(); - final String splashScreenThemeName = packageManager.getSplashScreenTheme(packageName, - userId); - final int splashScreenThemeId = splashScreenThemeName != null - ? windowContext.getResources().getIdentifier(splashScreenThemeName, null, null) - : 0; - - final int theme = getSplashScreenTheme(splashScreenThemeId, activityInfo); - - if (theme != windowContext.getThemeResId()) { - windowContext.setTheme(theme); - } - return mSplashscreenContentDrawer.estimateTaskBackgroundColor(windowContext); - } catch (RuntimeException | RemoteException e) { - Slog.w(TAG, "failed get starting window background color at taskId: " - + taskInfo.taskId, e); - } - return Color.TRANSPARENT; - } - - /** - * Called when the Task wants to copy the splash screen. - */ - public void copySplashScreenView(int taskId) { - final StartingSurfaceDrawer.StartingWindowRecord record = - mStartingWindowRecordManager.getRecord(taskId); - final SplashWindowRecord preView = record instanceof SplashWindowRecord - ? (SplashWindowRecord) record : null; - SplashScreenView.SplashScreenViewParcelable parcelable; - SplashScreenView splashScreenView = preView != null ? preView.mSplashView : null; - if (splashScreenView != null && splashScreenView.isCopyable()) { - parcelable = new SplashScreenView.SplashScreenViewParcelable(splashScreenView); - parcelable.setClientCallback( - new RemoteCallback((bundle) -> mSplashScreenExecutor.execute( - () -> onAppSplashScreenViewRemoved(taskId, false)))); - splashScreenView.onCopied(); - mAnimatedSplashScreenSurfaceHosts.append(taskId, splashScreenView.getSurfaceHost()); - } else { - parcelable = null; - } - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, - "Copying splash screen window view for task: %d with parcelable %b", - taskId, parcelable != null); - ActivityTaskManager.getInstance().onSplashScreenViewCopyFinished(taskId, parcelable); - } - - /** - * Called when the {@link SplashScreenView} is removed from the client Activity view's hierarchy - * or when the Activity is clean up. - * - * @param taskId The Task id on which the splash screen was attached - */ - public void onAppSplashScreenViewRemoved(int taskId) { - onAppSplashScreenViewRemoved(taskId, true /* fromServer */); - } - - /** - * @param fromServer If true, this means the removal was notified by the server. This is only - * used for debugging purposes. - * @see #onAppSplashScreenViewRemoved(int) - */ - private void onAppSplashScreenViewRemoved(int taskId, boolean fromServer) { - SurfaceControlViewHost viewHost = - mAnimatedSplashScreenSurfaceHosts.get(taskId); - if (viewHost == null) { - return; - } - mAnimatedSplashScreenSurfaceHosts.remove(taskId); - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, - "%s the splash screen. Releasing SurfaceControlViewHost for task: %d", - fromServer ? "Server cleaned up" : "App removed", taskId); - SplashScreenView.releaseIconHost(viewHost); - } - - protected boolean addWindow(int taskId, IBinder appToken, View view, Display display, - WindowManager.LayoutParams params, - @StartingWindowInfo.StartingWindowType int suggestType) { - boolean shouldSaveView = true; - final Context context = view.getContext(); - try { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addRootView"); - mWindowManagerGlobal.addView(view, params, display, - null /* parentWindow */, context.getUserId()); - } catch (WindowManager.BadTokenException e) { - // ignore - Slog.w(TAG, appToken + " already running, starting window not displayed. " - + e.getMessage()); - shouldSaveView = false; - } finally { - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - if (view.getParent() == null) { - Slog.w(TAG, "view not successfully added to wm, removing view"); - mWindowManagerGlobal.removeView(view, true /* immediate */); - shouldSaveView = false; - } - } - if (shouldSaveView) { - mStartingWindowRecordManager.removeWindow(taskId, true); - saveSplashScreenRecord(appToken, taskId, view, suggestType); - } - return shouldSaveView; - } - - private void saveSplashScreenRecord(IBinder appToken, int taskId, View view, - @StartingWindowInfo.StartingWindowType int suggestType) { - final SplashWindowRecord tView = - new SplashWindowRecord(appToken, view, suggestType); - mStartingWindowRecordManager.addRecord(taskId, tView); - } - - private void removeWindowInner(View decorView, boolean hideView) { - requestTopUi(false); - if (hideView) { - decorView.setVisibility(View.GONE); - } - mWindowManagerGlobal.removeView(decorView, false /* immediate */); - } - - private static class SplashScreenViewSupplier implements Supplier { - private SplashScreenView mView; - private boolean mIsViewSet; - private Runnable mUiThreadInitTask; - void setView(SplashScreenView view) { - synchronized (this) { - mView = view; - mIsViewSet = true; - notify(); - } - } - - void setUiThreadInitTask(Runnable initTask) { - synchronized (this) { - mUiThreadInitTask = initTask; - } - } - - @Override - @Nullable - public SplashScreenView get() { - synchronized (this) { - while (!mIsViewSet) { - try { - wait(); - } catch (InterruptedException ignored) { - } - } - if (mUiThreadInitTask != null) { - mUiThreadInitTask.run(); - mUiThreadInitTask = null; - } - return mView; - } - } - } - - private class SplashWindowRecord extends StartingSurfaceDrawer.StartingWindowRecord { - private final IBinder mAppToken; - private final View mRootView; - @StartingWindowInfo.StartingWindowType private final int mSuggestType; - private final long mCreateTime; - - private boolean mSetSplashScreen; - private SplashScreenView mSplashView; - private int mSystemBarAppearance; - private boolean mDrawsSystemBarBackgrounds; - - SplashWindowRecord(IBinder appToken, View decorView, - @StartingWindowInfo.StartingWindowType int suggestType) { - mAppToken = appToken; - mRootView = decorView; - mSuggestType = suggestType; - mCreateTime = SystemClock.uptimeMillis(); - } - - void setSplashScreenView(SplashScreenView splashScreenView) { - if (mSetSplashScreen) { - return; - } - mSplashView = splashScreenView; - mBGColor = mSplashView.getInitBackgroundColor(); - mSetSplashScreen = true; - } - - void parseAppSystemBarColor(Context context) { - final TypedArray a = context.obtainStyledAttributes(R.styleable.Window); - mDrawsSystemBarBackgrounds = a.getBoolean( - R.styleable.Window_windowDrawsSystemBarBackgrounds, false); - if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) { - mSystemBarAppearance |= WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; - } - if (a.getBoolean(R.styleable.Window_windowLightNavigationBar, false)) { - mSystemBarAppearance |= WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; - } - a.recycle(); - } - - // Reset the system bar color which set by splash screen, make it align to the app. - void clearSystemBarColor() { - if (mRootView == null || !mRootView.isAttachedToWindow()) { - return; - } - if (mRootView.getLayoutParams() instanceof WindowManager.LayoutParams) { - final WindowManager.LayoutParams lp = - (WindowManager.LayoutParams) mRootView.getLayoutParams(); - if (mDrawsSystemBarBackgrounds) { - lp.flags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; - } else { - lp.flags &= ~WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; - } - mRootView.setLayoutParams(lp); - } - mRootView.getWindowInsetsController().setSystemBarsAppearance( - mSystemBarAppearance, LIGHT_BARS_MASK); - } - - @Override - public void removeIfPossible(StartingWindowRemovalInfo info, boolean immediately) { - if (mRootView != null) { - if (mSplashView != null) { - clearSystemBarColor(); - if (immediately - || mSuggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) { - removeWindowInner(mRootView, false); - } else { - if (info.playRevealAnimation) { - mSplashscreenContentDrawer.applyExitAnimation(mSplashView, - info.windowAnimationLeash, info.mainFrame, - () -> removeWindowInner(mRootView, true), - mCreateTime, info.roundedCornerRadius); - } else { - // the SplashScreenView has been copied to client, hide the view to skip - // default exit animation - removeWindowInner(mRootView, true); - } - } - } else { - // shouldn't happen - Slog.e(TAG, "Found empty splash screen, remove!"); - removeWindowInner(mRootView, false); - } - } - } - } -} 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 ff06db370d1ae..4f07bfeacce52 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,80 +16,169 @@ package com.android.wm.shell.startingsurface; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; +import static android.view.Choreographer.CALLBACK_INSETS_ANIMATION; 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_SNAPSHOT; -import android.annotation.CallSuper; +import android.annotation.Nullable; +import android.app.ActivityManager.RunningTaskInfo; +import android.app.ActivityTaskManager; +import android.app.ActivityThread; import android.app.TaskInfo; -import android.app.WindowConfiguration; import android.content.Context; -import android.content.res.Configuration; +import android.content.pm.ActivityInfo; +import android.content.pm.IPackageManager; +import android.content.pm.PackageManager; +import android.content.res.TypedArray; import android.graphics.Color; +import android.graphics.PixelFormat; import android.hardware.display.DisplayManager; +import android.os.IBinder; +import android.os.RemoteCallback; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.Trace; +import android.os.UserHandle; +import android.util.Slog; import android.util.SparseArray; -import android.view.IWindow; -import android.view.SurfaceControl; -import android.view.SurfaceSession; +import android.view.Choreographer; +import android.view.Display; +import android.view.SurfaceControlViewHost; +import android.view.View; +import android.view.WindowInsetsController; import android.view.WindowManager; -import android.view.WindowlessWindowManager; +import android.view.WindowManagerGlobal; +import android.widget.FrameLayout; import android.window.SplashScreenView; +import android.window.SplashScreenView.SplashScreenViewParcelable; import android.window.StartingWindowInfo; import android.window.StartingWindowInfo.StartingWindowType; import android.window.StartingWindowRemovalInfo; import android.window.TaskSnapshot; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.util.ContrastColorUtil; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ShellSplashscreenThread; import com.android.wm.shell.protolog.ShellProtoLogGroup; +import java.util.function.Supplier; + /** * A class which able to draw splash screen or snapshot as the starting window for a task. + * + * In order to speed up, there will use two threads to creating a splash screen in parallel. + * Right now we are still using PhoneWindow to create splash screen window, so the view is added to + * the ViewRootImpl, and those view won't be draw immediately because the ViewRootImpl will call + * scheduleTraversal to register a callback from Choreographer, so the drawing result of the view + * can synchronize on each frame. + * + * The bad thing is that we cannot decide when would Choreographer#doFrame happen, and drawing + * the AdaptiveIconDrawable object can be time consuming, so we use the splash-screen background + * thread to draw the AdaptiveIconDrawable object to a Bitmap and cache it to a BitmapShader after + * the SplashScreenView just created, once we get the BitmapShader then the #draw call can be very + * quickly. + * + * So basically we are using the spare time to prepare the SplashScreenView while splash screen + * thread is waiting for + * 1. WindowManager#addView(binder call to WM), + * 2. Choreographer#doFrame happen(uncertain time for next frame, depends on device), + * 3. Session#relayout(another binder call to WM which under Choreographer#doFrame, but will + * always happen before #draw). + * Because above steps are running on splash-screen thread, so pre-draw the BitmapShader on + * splash-screen background tread can make they execute in parallel, which ensure it is faster then + * to draw the AdaptiveIconDrawable when receive callback from Choreographer#doFrame. + * + * Here is the sequence to compare the difference between using single and two thread. + * + * Single thread: + * => makeSplashScreenContentView -> WM#addView .. waiting for Choreographer#doFrame -> relayout + * -> draw -> AdaptiveIconDrawable#draw + * + * Two threads: + * => makeSplashScreenContentView -> cachePaint(=AdaptiveIconDrawable#draw) + * => WM#addView -> .. waiting for Choreographer#doFrame -> relayout -> draw -> (draw the Paint + * directly). */ @ShellSplashscreenThread public class StartingSurfaceDrawer { + private static final String TAG = StartingWindowController.TAG; + private final Context mContext; + private final DisplayManager mDisplayManager; private final ShellExecutor mSplashScreenExecutor; @VisibleForTesting final SplashscreenContentDrawer mSplashscreenContentDrawer; - @VisibleForTesting - final SplashscreenWindowCreator mSplashscreenWindowCreator; - private final SnapshotWindowCreator mSnapshotWindowCreator; - private final WindowlessSplashWindowCreator mWindowlessSplashWindowCreator; - private final WindowlessSnapshotWindowCreator mWindowlessSnapshotWindowCreator; + private Choreographer mChoreographer; + private final WindowManagerGlobal mWindowManagerGlobal; + private StartingSurface.SysuiProxy mSysuiProxy; + private final StartingWindowRemovalInfo mTmpRemovalInfo = new StartingWindowRemovalInfo(); + + private static final int LIGHT_BARS_MASK = + WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS + | WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; + /** + * The minimum duration during which the splash screen is shown when the splash screen icon is + * animated. + */ + static final long MINIMAL_ANIMATION_DURATION = 400L; + + /** + * Allow the icon style splash screen to be displayed for longer to give time for the animation + * to finish, i.e. the extra buffer time to keep the splash screen if the animation is slightly + * longer than the {@link #MINIMAL_ANIMATION_DURATION} duration. + */ + static final long TIME_WINDOW_DURATION = 100L; + + /** + * The maximum duration during which the splash screen will be shown if the application is ready + * to show before the icon animation finishes. + */ + static final long MAX_ANIMATION_DURATION = MINIMAL_ANIMATION_DURATION + TIME_WINDOW_DURATION; - @VisibleForTesting - final StartingWindowRecordManager mWindowRecords = new StartingWindowRecordManager(); - // Windowless surface could co-exist with starting window in a task. - @VisibleForTesting - final StartingWindowRecordManager mWindowlessRecords = new StartingWindowRecordManager(); /** * @param splashScreenExecutor The thread used to control add and remove starting window. */ public StartingSurfaceDrawer(Context context, ShellExecutor splashScreenExecutor, IconProvider iconProvider, TransactionPool pool) { + mContext = context; + mDisplayManager = mContext.getSystemService(DisplayManager.class); mSplashScreenExecutor = splashScreenExecutor; - final DisplayManager displayManager = context.getSystemService(DisplayManager.class); - mSplashscreenContentDrawer = new SplashscreenContentDrawer(context, iconProvider, pool); - displayManager.getDisplay(DEFAULT_DISPLAY); + mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext, iconProvider, pool); + mSplashScreenExecutor.execute(() -> mChoreographer = Choreographer.getInstance()); + mWindowManagerGlobal = WindowManagerGlobal.getInstance(); + mDisplayManager.getDisplay(DEFAULT_DISPLAY); + } - mSplashscreenWindowCreator = new SplashscreenWindowCreator(mSplashscreenContentDrawer, - context, splashScreenExecutor, displayManager, mWindowRecords); - mSnapshotWindowCreator = new SnapshotWindowCreator(splashScreenExecutor, - mWindowRecords); - mWindowlessSplashWindowCreator = new WindowlessSplashWindowCreator( - mSplashscreenContentDrawer, context, splashScreenExecutor, displayManager, - mWindowlessRecords, pool); - mWindowlessSnapshotWindowCreator = new WindowlessSnapshotWindowCreator( - mWindowlessRecords, context, displayManager, mSplashscreenContentDrawer, pool); + @VisibleForTesting + final SparseArray mStartingWindowRecords = new SparseArray<>(); + + /** + * Records of {@link SurfaceControlViewHost} where the splash screen icon animation is + * rendered and that have not yet been removed by their client. + */ + private final SparseArray mAnimatedSplashScreenSurfaceHosts = + new SparseArray<>(1); + + private Display getDisplay(int displayId) { + return mDisplayManager.getDisplay(displayId); + } + + int getSplashScreenTheme(int splashScreenThemeResId, ActivityInfo activityInfo) { + return splashScreenThemeResId != 0 + ? splashScreenThemeResId + : activityInfo.getThemeResource() != 0 ? activityInfo.getThemeResource() + : com.android.internal.R.style.Theme_DeviceDefault_DayNight; } void setSysuiProxy(StartingSurface.SysuiProxy sysuiProxy) { - mSplashscreenWindowCreator.setSysuiProxy(sysuiProxy); - mWindowlessSplashWindowCreator.setSysuiProxy(sysuiProxy); + mSysuiProxy = sysuiProxy; } /** @@ -97,55 +186,231 @@ public class StartingSurfaceDrawer { * * @param suggestType The suggestion type to draw the splash screen. */ - void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, + void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, IBinder appToken, @StartingWindowType int suggestType) { - mSplashscreenWindowCreator.addSplashScreenStartingWindow(windowInfo, suggestType); + final RunningTaskInfo taskInfo = windowInfo.taskInfo; + final ActivityInfo activityInfo = windowInfo.targetActivityInfo != null + ? windowInfo.targetActivityInfo + : taskInfo.topActivityInfo; + if (activityInfo == null || activityInfo.packageName == null) { + return; + } + // replace with the default theme if the application didn't set + final int theme = getSplashScreenTheme(windowInfo.splashScreenThemeResId, activityInfo); + final Context context = SplashscreenContentDrawer.createContext(mContext, windowInfo, theme, + suggestType, mDisplayManager); + if (context == null) { + 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 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 + // content view won't be blocked by binder call like addWindow and relayout. + // 1. Trigger splash screen worker thread to create SplashScreenView before/while + // Session#addWindow. + // 2. Synchronize the SplashscreenView to splash screen thread before Choreographer start + // traversal, which will call Session#relayout on splash screen thread. + // 3. Pre-draw the BitmapShader if the icon is immobile on splash screen worker thread, at + // the same time the splash screen thread should be executing Session#relayout. Blocking the + // traversal -> draw on splash screen thread until the BitmapShader of the icon is ready. + + // Record whether create splash screen view success, notify to current thread after + // create splash screen view finished. + final SplashScreenViewSupplier viewSupplier = new SplashScreenViewSupplier(); + final FrameLayout rootLayout = new FrameLayout( + mSplashscreenContentDrawer.createViewContextWrapper(context)); + rootLayout.setPadding(0, 0, 0, 0); + rootLayout.setFitsSystemWindows(false); + final Runnable setViewSynchronized = () -> { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addSplashScreenView"); + // waiting for setContentView before relayoutWindow + SplashScreenView contentView = viewSupplier.get(); + final StartingWindowRecord record = mStartingWindowRecords.get(taskId); + // If record == null, either the starting window added fail or removed already. + // Do not add this view if the token is mismatch. + if (record != null && appToken == record.mAppToken) { + // if view == null then creation of content view was failed. + if (contentView != null) { + try { + rootLayout.addView(contentView); + } catch (RuntimeException e) { + Slog.w(TAG, "failed set content view to starting window " + + "at taskId: " + taskId, e); + contentView = null; + } + } + record.setSplashScreenView(contentView); + } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + }; + if (mSysuiProxy != null) { + mSysuiProxy.requestTopUi(true, TAG); + } + mSplashscreenContentDrawer.createContentView(context, suggestType, windowInfo, + viewSupplier::setView, viewSupplier::setUiThreadInitTask); + try { + if (addWindow(taskId, appToken, rootLayout, display, params, suggestType)) { + // We use the splash screen worker thread to create SplashScreenView while adding + // the window, as otherwise Choreographer#doFrame might be delayed on this thread. + // And since Choreographer#doFrame won't happen immediately after adding the window, + // if the view is not added to the PhoneWindow on the first #doFrame, the view will + // not be rendered on the first frame. So here we need to synchronize the view on + // the window before first round relayoutWindow, which will happen after insets + // animation. + mChoreographer.postCallback(CALLBACK_INSETS_ANIMATION, setViewSynchronized, null); + final StartingWindowRecord record = mStartingWindowRecords.get(taskId); + record.parseAppSystemBarColor(context); + // Block until we get the background color. + final SplashScreenView contentView = viewSupplier.get(); + if (suggestType != STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) { + contentView.addOnAttachStateChangeListener( + new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + final int lightBarAppearance = ContrastColorUtil.isColorLight( + contentView.getInitBackgroundColor()) + ? LIGHT_BARS_MASK : 0; + contentView.getWindowInsetsController().setSystemBarsAppearance( + lightBarAppearance, LIGHT_BARS_MASK); + } + + @Override + public void onViewDetachedFromWindow(View v) { + } + }); + } + record.mBGColor = contentView.getInitBackgroundColor(); + } else { + // release the icon view host + final SplashScreenView contentView = viewSupplier.get(); + if (contentView.getSurfaceHost() != null) { + SplashScreenView.releaseIconHost(contentView.getSurfaceHost()); + } + } + } catch (RuntimeException e) { + // don't crash if something else bad happens, for example a + // failure loading resources because we are loading from an app + // on external storage that has been unmounted. + Slog.w(TAG, "failed creating starting window at taskId: " + taskId, e); + } } int getStartingWindowBackgroundColorForTask(int taskId) { - final StartingWindowRecord startingWindowRecord = mWindowRecords.getRecord(taskId); + final StartingWindowRecord startingWindowRecord = mStartingWindowRecords.get(taskId); if (startingWindowRecord == null) { return Color.TRANSPARENT; } - return startingWindowRecord.getBGColor(); + return startingWindowRecord.mBGColor; + } + + private static class SplashScreenViewSupplier implements Supplier { + private SplashScreenView mView; + private boolean mIsViewSet; + private Runnable mUiThreadInitTask; + void setView(SplashScreenView view) { + synchronized (this) { + mView = view; + mIsViewSet = true; + notify(); + } + } + + void setUiThreadInitTask(Runnable initTask) { + synchronized (this) { + mUiThreadInitTask = initTask; + } + } + + @Override + @Nullable + public SplashScreenView get() { + synchronized (this) { + while (!mIsViewSet) { + try { + wait(); + } catch (InterruptedException ignored) { + } + } + if (mUiThreadInitTask != null) { + mUiThreadInitTask.run(); + mUiThreadInitTask = null; + } + return mView; + } + } } int estimateTaskBackgroundColor(TaskInfo taskInfo) { - return mSplashscreenWindowCreator.estimateTaskBackgroundColor(taskInfo); + if (taskInfo.topActivityInfo == null) { + return Color.TRANSPARENT; + } + final ActivityInfo activityInfo = taskInfo.topActivityInfo; + final String packageName = activityInfo.packageName; + final int userId = taskInfo.userId; + final Context windowContext; + try { + windowContext = mContext.createPackageContextAsUser( + packageName, Context.CONTEXT_RESTRICTED, UserHandle.of(userId)); + } catch (PackageManager.NameNotFoundException e) { + Slog.w(TAG, "Failed creating package context with package name " + + packageName + " for user " + taskInfo.userId, e); + return Color.TRANSPARENT; + } + try { + final IPackageManager packageManager = ActivityThread.getPackageManager(); + final String splashScreenThemeName = packageManager.getSplashScreenTheme(packageName, + userId); + final int splashScreenThemeId = splashScreenThemeName != null + ? windowContext.getResources().getIdentifier(splashScreenThemeName, null, null) + : 0; + + final int theme = getSplashScreenTheme(splashScreenThemeId, activityInfo); + + if (theme != windowContext.getThemeResId()) { + windowContext.setTheme(theme); + } + return mSplashscreenContentDrawer.estimateTaskBackgroundColor(windowContext); + } catch (RuntimeException | RemoteException e) { + Slog.w(TAG, "failed get starting window background color at taskId: " + + taskInfo.taskId, e); + } + return Color.TRANSPARENT; } /** * Called when a task need a snapshot starting window. */ - void makeTaskSnapshotWindow(StartingWindowInfo startingWindowInfo, TaskSnapshot snapshot) { - mSnapshotWindowCreator.makeTaskSnapshotWindow(startingWindowInfo, snapshot); + void makeTaskSnapshotWindow(StartingWindowInfo startingWindowInfo, IBinder appToken, + TaskSnapshot snapshot) { + final int taskId = startingWindowInfo.taskInfo.taskId; + // Remove any existing starting window for this task before adding. + removeWindowNoAnimate(taskId); + final TaskSnapshotWindow surface = TaskSnapshotWindow.create(startingWindowInfo, appToken, + snapshot, mSplashScreenExecutor, () -> removeWindowNoAnimate(taskId)); + if (surface == null) { + return; + } + final StartingWindowRecord tView = new StartingWindowRecord(appToken, + null/* decorView */, surface, STARTING_WINDOW_TYPE_SNAPSHOT); + mStartingWindowRecords.put(taskId, tView); } /** * Called when the content of a task is ready to show, starting window can be removed. */ public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) { - if (removalInfo.windowlessSurface) { - mWindowlessRecords.removeWindow(removalInfo, removalInfo.removeImmediately); - } else { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, - "Task start finish, remove starting surface for task: %d", - removalInfo.taskId); - mWindowRecords.removeWindow(removalInfo, removalInfo.removeImmediately); - } - } - - /** - * Create a windowless starting surface and attach to the root surface. - */ - void addWindowlessStartingSurface(StartingWindowInfo windowInfo) { - if (windowInfo.taskSnapshot != null) { - mWindowlessSnapshotWindowCreator.makeTaskSnapshotWindow(windowInfo, - windowInfo.rootSurface, windowInfo.taskSnapshot, mSplashScreenExecutor); - } else { - mWindowlessSplashWindowCreator.addSplashScreenStartingWindow( - windowInfo, windowInfo.rootSurface); - } + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, + "Task start finish, remove starting surface for task: %d", + removalInfo.taskId); + removeWindowSynced(removalInfo, false /* immediately */); } /** @@ -154,15 +419,37 @@ public class StartingSurfaceDrawer { public void clearAllWindows() { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, "Clear all starting windows immediately"); - mWindowRecords.clearAllWindows(); - mWindowlessRecords.clearAllWindows(); + final int taskSize = mStartingWindowRecords.size(); + final int[] taskIds = new int[taskSize]; + for (int i = taskSize - 1; i >= 0; --i) { + taskIds[i] = mStartingWindowRecords.keyAt(i); + } + for (int i = taskSize - 1; i >= 0; --i) { + removeWindowNoAnimate(taskIds[i]); + } } /** * Called when the Task wants to copy the splash screen. */ public void copySplashScreenView(int taskId) { - mSplashscreenWindowCreator.copySplashScreenView(taskId); + final StartingWindowRecord preView = mStartingWindowRecords.get(taskId); + SplashScreenViewParcelable parcelable; + SplashScreenView splashScreenView = preView != null ? preView.mContentView : null; + if (splashScreenView != null && splashScreenView.isCopyable()) { + parcelable = new SplashScreenViewParcelable(splashScreenView); + parcelable.setClientCallback( + new RemoteCallback((bundle) -> mSplashScreenExecutor.execute( + () -> onAppSplashScreenViewRemoved(taskId, false)))); + splashScreenView.onCopied(); + mAnimatedSplashScreenSurfaceHosts.append(taskId, splashScreenView.getSurfaceHost()); + } else { + parcelable = null; + } + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, + "Copying splash screen window view for task: %d with parcelable %b", + taskId, parcelable != null); + ActivityTaskManager.getInstance().onSplashScreenViewCopyFinished(taskId, parcelable); } /** @@ -172,148 +459,195 @@ public class StartingSurfaceDrawer { * @param taskId The Task id on which the splash screen was attached */ public void onAppSplashScreenViewRemoved(int taskId) { - mSplashscreenWindowCreator.onAppSplashScreenViewRemoved(taskId); + onAppSplashScreenViewRemoved(taskId, true /* fromServer */); + } + + /** + * @param fromServer If true, this means the removal was notified by the server. This is only + * used for debugging purposes. + * @see #onAppSplashScreenViewRemoved(int) + */ + private void onAppSplashScreenViewRemoved(int taskId, boolean fromServer) { + SurfaceControlViewHost viewHost = + mAnimatedSplashScreenSurfaceHosts.get(taskId); + if (viewHost == null) { + return; + } + mAnimatedSplashScreenSurfaceHosts.remove(taskId); + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, + "%s the splash screen. Releasing SurfaceControlViewHost for task: %d", + fromServer ? "Server cleaned up" : "App removed", taskId); + SplashScreenView.releaseIconHost(viewHost); + } + + protected boolean addWindow(int taskId, IBinder appToken, View view, Display display, + WindowManager.LayoutParams params, @StartingWindowType int suggestType) { + boolean shouldSaveView = true; + final Context context = view.getContext(); + try { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addRootView"); + mWindowManagerGlobal.addView(view, params, display, + null /* parentWindow */, context.getUserId()); + } catch (WindowManager.BadTokenException e) { + // ignore + Slog.w(TAG, appToken + " already running, starting window not displayed. " + + e.getMessage()); + shouldSaveView = false; + } finally { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + if (view.getParent() == null) { + Slog.w(TAG, "view not successfully added to wm, removing view"); + mWindowManagerGlobal.removeView(view, true /* immediate */); + shouldSaveView = false; + } + } + if (shouldSaveView) { + removeWindowNoAnimate(taskId); + saveSplashScreenRecord(appToken, taskId, view, suggestType); + } + return shouldSaveView; + } + + @VisibleForTesting + void saveSplashScreenRecord(IBinder appToken, int taskId, View view, + @StartingWindowType int suggestType) { + final StartingWindowRecord tView = new StartingWindowRecord(appToken, view, + null/* TaskSnapshotWindow */, suggestType); + mStartingWindowRecords.put(taskId, tView); + } + + private void removeWindowNoAnimate(int taskId) { + mTmpRemovalInfo.taskId = taskId; + removeWindowSynced(mTmpRemovalInfo, true /* immediately */); } void onImeDrawnOnTask(int taskId) { - onImeDrawnOnTask(mWindowRecords, taskId); - onImeDrawnOnTask(mWindowlessRecords, taskId); - } - - private void onImeDrawnOnTask(StartingWindowRecordManager records, int taskId) { - final StartingSurfaceDrawer.StartingWindowRecord sRecord = - records.getRecord(taskId); - final SnapshotRecord record = sRecord instanceof SnapshotRecord - ? (SnapshotRecord) sRecord : null; - if (record != null && record.hasImeSurface()) { - records.removeWindow(taskId, true); + final StartingWindowRecord record = mStartingWindowRecords.get(taskId); + if (record != null && record.mTaskSnapshotWindow != null + && record.mTaskSnapshotWindow.hasImeSurface()) { + removeWindowNoAnimate(taskId); } } - static class WindowlessStartingWindow extends WindowlessWindowManager { - SurfaceControl mChildSurface; + protected void removeWindowSynced(StartingWindowRemovalInfo removalInfo, boolean immediately) { + final int taskId = removalInfo.taskId; + final StartingWindowRecord record = mStartingWindowRecords.get(taskId); + if (record != null) { + if (record.mDecorView != null) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, + "Removing splash screen window for task: %d", taskId); + if (record.mContentView != null) { + record.clearSystemBarColor(); + if (immediately + || record.mSuggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) { + removeWindowInner(record.mDecorView, false); + } else { + if (removalInfo.playRevealAnimation) { + mSplashscreenContentDrawer.applyExitAnimation(record.mContentView, + removalInfo.windowAnimationLeash, removalInfo.mainFrame, + () -> removeWindowInner(record.mDecorView, true), + record.mCreateTime, removalInfo.roundedCornerRadius); + } else { + // the SplashScreenView has been copied to client, hide the view to skip + // default exit animation + removeWindowInner(record.mDecorView, true); + } + } + } else { + // shouldn't happen + Slog.e(TAG, "Found empty splash screen, remove!"); + removeWindowInner(record.mDecorView, false); + } - WindowlessStartingWindow(Configuration c, SurfaceControl rootSurface) { - super(c, rootSurface, null /* hostInputToken */); - } - - @Override - protected SurfaceControl getParentSurface(IWindow window, - WindowManager.LayoutParams attrs) { - final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession()) - .setContainerLayer() - .setName("Windowless window") - .setHidden(false) - .setParent(mRootSurface) - .setCallsite("WindowlessStartingWindow#attachToParentSurface"); - mChildSurface = builder.build(); - try (SurfaceControl.Transaction t = new SurfaceControl.Transaction()) { - t.setLayer(mChildSurface, Integer.MAX_VALUE); - t.apply(); } - return mChildSurface; - } - } - abstract static class StartingWindowRecord { - protected int mBGColor; - abstract void removeIfPossible(StartingWindowRemovalInfo info, boolean immediately); - int getBGColor() { - return mBGColor; - } - } - - abstract static class SnapshotRecord extends StartingWindowRecord { - private static final long DELAY_REMOVAL_TIME_GENERAL = 100; - /** - * The max delay time in milliseconds for removing the task snapshot window with IME - * visible. - * Ideally the delay time will be shorter when receiving - * {@link StartingSurfaceDrawer#onImeDrawnOnTask(int)}. - */ - private static final long MAX_DELAY_REMOVAL_TIME_IME_VISIBLE = 600; - private final Runnable mScheduledRunnable = this::removeImmediately; - - @WindowConfiguration.ActivityType protected final int mActivityType; - protected final ShellExecutor mRemoveExecutor; - - SnapshotRecord(int activityType, ShellExecutor removeExecutor) { - mActivityType = activityType; - mRemoveExecutor = removeExecutor; - } - - @Override - public final void removeIfPossible(StartingWindowRemovalInfo info, boolean immediately) { - if (immediately) { - removeImmediately(); - } else { - scheduleRemove(info.deferRemoveForIme); + if (record.mTaskSnapshotWindow != null) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, + "Removing task snapshot window for %d", taskId); + if (immediately) { + record.mTaskSnapshotWindow.removeImmediately(); + } else { + record.mTaskSnapshotWindow.scheduleRemove(removalInfo.deferRemoveForIme); + } } + mStartingWindowRecords.remove(taskId); + } + } + + private void removeWindowInner(View decorView, boolean hideView) { + if (mSysuiProxy != null) { + mSysuiProxy.requestTopUi(false, TAG); + } + if (hideView) { + decorView.setVisibility(View.GONE); + } + mWindowManagerGlobal.removeView(decorView, false /* immediate */); + } + + /** + * Record the view or surface for a starting window. + */ + private static class StartingWindowRecord { + private final IBinder mAppToken; + private final View mDecorView; + private final TaskSnapshotWindow mTaskSnapshotWindow; + private SplashScreenView mContentView; + private boolean mSetSplashScreen; + @StartingWindowType private int mSuggestType; + private int mBGColor; + private final long mCreateTime; + private int mSystemBarAppearance; + private boolean mDrawsSystemBarBackgrounds; + + StartingWindowRecord(IBinder appToken, View decorView, + TaskSnapshotWindow taskSnapshotWindow, @StartingWindowType int suggestType) { + mAppToken = appToken; + mDecorView = decorView; + mTaskSnapshotWindow = taskSnapshotWindow; + if (mTaskSnapshotWindow != null) { + mBGColor = mTaskSnapshotWindow.getBackgroundColor(); + } + mSuggestType = suggestType; + mCreateTime = SystemClock.uptimeMillis(); } - void scheduleRemove(boolean deferRemoveForIme) { - // Show the latest content as soon as possible for unlocking to home. - if (mActivityType == ACTIVITY_TYPE_HOME) { - removeImmediately(); + private void setSplashScreenView(SplashScreenView splashScreenView) { + if (mSetSplashScreen) { return; } - mRemoveExecutor.removeCallbacks(mScheduledRunnable); - final long delayRemovalTime = hasImeSurface() && deferRemoveForIme - ? MAX_DELAY_REMOVAL_TIME_IME_VISIBLE - : DELAY_REMOVAL_TIME_GENERAL; - mRemoveExecutor.executeDelayed(mScheduledRunnable, delayRemovalTime); - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, - "Defer removing snapshot surface in %d", delayRemovalTime); + mContentView = splashScreenView; + mSetSplashScreen = true; } - protected abstract boolean hasImeSurface(); - - @CallSuper - protected void removeImmediately() { - mRemoveExecutor.removeCallbacks(mScheduledRunnable); - } - } - - static class StartingWindowRecordManager { - private final StartingWindowRemovalInfo mTmpRemovalInfo = new StartingWindowRemovalInfo(); - private final SparseArray mStartingWindowRecords = - new SparseArray<>(); - - void clearAllWindows() { - final int taskSize = mStartingWindowRecords.size(); - final int[] taskIds = new int[taskSize]; - for (int i = taskSize - 1; i >= 0; --i) { - taskIds[i] = mStartingWindowRecords.keyAt(i); + private void parseAppSystemBarColor(Context context) { + final TypedArray a = context.obtainStyledAttributes(R.styleable.Window); + mDrawsSystemBarBackgrounds = a.getBoolean( + R.styleable.Window_windowDrawsSystemBarBackgrounds, false); + if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) { + mSystemBarAppearance |= WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; } - for (int i = taskSize - 1; i >= 0; --i) { - removeWindow(taskIds[i], true); + if (a.getBoolean(R.styleable.Window_windowLightNavigationBar, false)) { + mSystemBarAppearance |= WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; } + a.recycle(); } - void addRecord(int taskId, StartingWindowRecord record) { - mStartingWindowRecords.put(taskId, record); - } - - void removeWindow(StartingWindowRemovalInfo removeInfo, boolean immediately) { - final int taskId = removeInfo.taskId; - final StartingWindowRecord record = mStartingWindowRecords.get(taskId); - if (record != null) { - record.removeIfPossible(removeInfo, immediately); - mStartingWindowRecords.remove(taskId); + // Reset the system bar color which set by splash screen, make it align to the app. + private void clearSystemBarColor() { + if (mDecorView == null || !mDecorView.isAttachedToWindow()) { + return; } - } - - void removeWindow(int taskId, boolean immediately) { - mTmpRemovalInfo.taskId = taskId; - removeWindow(mTmpRemovalInfo, immediately); - } - - StartingWindowRecord getRecord(int taskId) { - return mStartingWindowRecords.get(taskId); - } - - @VisibleForTesting - int recordSize() { - return mStartingWindowRecords.size(); + if (mDecorView.getLayoutParams() instanceof WindowManager.LayoutParams) { + final WindowManager.LayoutParams lp = + (WindowManager.LayoutParams) mDecorView.getLayoutParams(); + if (mDrawsSystemBarBackgrounds) { + lp.flags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + } else { + lp.flags &= ~WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + } + mDecorView.setLayoutParams(lp); + } + mDecorView.getWindowInsetsController().setSystemBarsAppearance( + mSystemBarAppearance, LIGHT_BARS_MASK); } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java index bec4ba3bf0d15..be2e79342d074 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java @@ -21,7 +21,6 @@ import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; -import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_WINDOWLESS; import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_STARTING_WINDOW; @@ -30,6 +29,7 @@ import android.app.ActivityManager.RunningTaskInfo; import android.app.TaskInfo; import android.content.Context; import android.graphics.Color; +import android.os.IBinder; import android.os.Trace; import android.util.SparseIntArray; import android.window.StartingWindowInfo; @@ -152,23 +152,22 @@ public class StartingWindowController implements RemoteCallable { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addStartingWindow"); final int suggestionType = mStartingWindowTypeAlgorithm.getSuggestedWindowType( windowInfo); final RunningTaskInfo runningTaskInfo = windowInfo.taskInfo; - if (suggestionType == STARTING_WINDOW_TYPE_WINDOWLESS) { - mStartingSurfaceDrawer.addWindowlessStartingSurface(windowInfo); - } else if (isSplashScreenType(suggestionType)) { - mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, suggestionType); + if (isSplashScreenType(suggestionType)) { + mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken, + suggestionType); } else if (suggestionType == STARTING_WINDOW_TYPE_SNAPSHOT) { final TaskSnapshot snapshot = windowInfo.taskSnapshot; - mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, snapshot); + mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken, + snapshot); } - if (suggestionType != STARTING_WINDOW_TYPE_NONE - && suggestionType != STARTING_WINDOW_TYPE_WINDOWLESS) { + if (suggestionType != STARTING_WINDOW_TYPE_NONE) { int taskId = runningTaskInfo.taskId; int color = mStartingSurfaceDrawer .getStartingWindowBackgroundColorForTask(taskId); @@ -219,13 +218,11 @@ public class StartingWindowController implements RemoteCallable mStartingSurfaceDrawer.removeStartingWindow( removalInfo)); - if (!removalInfo.windowlessSurface) { - mSplashScreenExecutor.executeDelayed(() -> { - synchronized (mTaskBackgroundColors) { - mTaskBackgroundColors.delete(removalInfo.taskId); - } - }, TASK_BG_COLOR_RETAIN_TIME_MS); - } + mSplashScreenExecutor.executeDelayed(() -> { + synchronized (mTaskBackgroundColors) { + mTaskBackgroundColors.delete(removalInfo.taskId); + } + }, TASK_BG_COLOR_RETAIN_TIME_MS); } /** 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 c964df1452e08..a05ed4f24a082 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 @@ -16,6 +16,7 @@ package com.android.wm.shell.startingsurface; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.graphics.Color.WHITE; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; @@ -62,14 +63,24 @@ public class TaskSnapshotWindow { private static final String TAG = StartingWindowController.TAG; private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId="; + private static final long DELAY_REMOVAL_TIME_GENERAL = 100; + /** + * The max delay time in milliseconds for removing the task snapshot window with IME visible. + * Ideally the delay time will be shorter when receiving + * {@link StartingSurfaceDrawer#onImeDrawnOnTask(int)}. + */ + private static final long MAX_DELAY_REMOVAL_TIME_IME_VISIBLE = 600; + private final Window mWindow; private final Runnable mClearWindowHandler; private final ShellExecutor mSplashScreenExecutor; private final IWindowSession mSession; private boolean mHasDrawn; private final Paint mBackgroundPaint = new Paint(); + private final int mActivityType; private final int mOrientationOnCreation; + private final Runnable mScheduledRunnable = this::removeImmediately; private final boolean mHasImeSurface; static TaskSnapshotWindow create(StartingWindowInfo info, IBinder appToken, @@ -93,6 +104,7 @@ public class TaskSnapshotWindow { final Point taskSize = snapshot.getTaskSize(); final Rect taskBounds = new Rect(0, 0, taskSize.x, taskSize.y); final int orientation = snapshot.getOrientation(); + final int activityType = runningTaskInfo.topActivityType; final int displayId = runningTaskInfo.displayId; final IWindowSession session = WindowManagerGlobal.getWindowSession(); @@ -102,11 +114,16 @@ public class TaskSnapshotWindow { final InsetsSourceControl.Array tmpControls = new InsetsSourceControl.Array(); final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration(); - final TaskDescription taskDescription = - SnapshotDrawerUtils.getOrCreateTaskDescription(runningTaskInfo); + final TaskDescription taskDescription; + if (runningTaskInfo.taskDescription != null) { + taskDescription = runningTaskInfo.taskDescription; + } else { + taskDescription = new TaskDescription(); + taskDescription.setBackgroundColor(WHITE); + } final TaskSnapshotWindow snapshotSurface = new TaskSnapshotWindow( - snapshot, taskDescription, orientation, + snapshot, taskDescription, orientation, activityType, clearWindowHandler, splashScreenExecutor); final Window window = snapshotSurface.mWindow; @@ -136,8 +153,6 @@ public class TaskSnapshotWindow { Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } catch (RemoteException e) { snapshotSurface.clearWindowSynced(); - Slog.w(TAG, "Failed to relayout snapshot starting window"); - return null; } SnapshotDrawerUtils.drawSnapshotOnSurface(info, layoutParams, surfaceControl, snapshot, @@ -149,7 +164,7 @@ public class TaskSnapshotWindow { } public TaskSnapshotWindow(TaskSnapshot snapshot, TaskDescription taskDescription, - int currentOrientation, Runnable clearWindowHandler, + int currentOrientation, int activityType, Runnable clearWindowHandler, ShellExecutor splashScreenExecutor) { mSplashScreenExecutor = splashScreenExecutor; mSession = WindowManagerGlobal.getWindowSession(); @@ -158,6 +173,7 @@ public class TaskSnapshotWindow { int backgroundColor = taskDescription.getBackgroundColor(); mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE); mOrientationOnCreation = currentOrientation; + mActivityType = activityType; mClearWindowHandler = clearWindowHandler; mHasImeSurface = snapshot.hasImeSurface(); } @@ -170,7 +186,23 @@ public class TaskSnapshotWindow { return mHasImeSurface; } + void scheduleRemove(boolean deferRemoveForIme) { + // Show the latest content as soon as possible for unlocking to home. + if (mActivityType == ACTIVITY_TYPE_HOME) { + removeImmediately(); + return; + } + mSplashScreenExecutor.removeCallbacks(mScheduledRunnable); + final long delayRemovalTime = mHasImeSurface && deferRemoveForIme + ? MAX_DELAY_REMOVAL_TIME_IME_VISIBLE + : DELAY_REMOVAL_TIME_GENERAL; + mSplashScreenExecutor.executeDelayed(mScheduledRunnable, delayRemovalTime); + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, + "Defer removing snapshot surface in %d", delayRemovalTime); + } + void removeImmediately() { + mSplashScreenExecutor.removeCallbacks(mScheduledRunnable); try { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, "Removing taskSnapshot surface, mHasDrawn=%b", mHasDrawn); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java deleted file mode 100644 index 1445478855016..0000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.startingsurface; - -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; -import android.app.ActivityManager; -import android.content.Context; -import android.graphics.Point; -import android.graphics.Rect; -import android.hardware.display.DisplayManager; -import android.view.Display; -import android.view.InsetsState; -import android.view.SurfaceControl; -import android.view.SurfaceControlViewHost; -import android.view.WindowManager; -import android.widget.FrameLayout; -import android.window.SnapshotDrawerUtils; -import android.window.StartingWindowInfo; -import android.window.TaskSnapshot; - -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.common.TransactionPool; - -class WindowlessSnapshotWindowCreator { - private static final int DEFAULT_FADEOUT_DURATION = 233; - private final StartingSurfaceDrawer.StartingWindowRecordManager - mStartingWindowRecordManager; - private final DisplayManager mDisplayManager; - private final Context mContext; - private final SplashscreenContentDrawer mSplashscreenContentDrawer; - private final TransactionPool mTransactionPool; - - WindowlessSnapshotWindowCreator( - StartingSurfaceDrawer.StartingWindowRecordManager startingWindowRecordManager, - Context context, - DisplayManager displayManager, SplashscreenContentDrawer splashscreenContentDrawer, - TransactionPool transactionPool) { - mStartingWindowRecordManager = startingWindowRecordManager; - mContext = context; - mDisplayManager = displayManager; - mSplashscreenContentDrawer = splashscreenContentDrawer; - mTransactionPool = transactionPool; - } - - void makeTaskSnapshotWindow(StartingWindowInfo info, SurfaceControl rootSurface, - TaskSnapshot snapshot, ShellExecutor removeExecutor) { - final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo; - final int taskId = runningTaskInfo.taskId; - final String title = "Windowless Snapshot " + taskId; - final WindowManager.LayoutParams lp = SnapshotDrawerUtils.createLayoutParameters( - info, title, TYPE_APPLICATION_OVERLAY, snapshot.getHardwareBuffer().getFormat(), - null /* token */); - if (lp == null) { - return; - } - final Display display = mDisplayManager.getDisplay(runningTaskInfo.displayId); - final StartingSurfaceDrawer.WindowlessStartingWindow wlw = - new StartingSurfaceDrawer.WindowlessStartingWindow( - runningTaskInfo.configuration, rootSurface); - final SurfaceControlViewHost mViewHost = new SurfaceControlViewHost( - mContext, display, wlw, "WindowlessSnapshotWindowCreator"); - final Point taskSize = snapshot.getTaskSize(); - final Rect snapshotBounds = new Rect(0, 0, taskSize.x, taskSize.y); - final Rect windowBounds = runningTaskInfo.configuration.windowConfiguration.getBounds(); - final InsetsState topWindowInsetsState = info.topOpaqueWindowInsetsState; - final FrameLayout rootLayout = new FrameLayout( - mSplashscreenContentDrawer.createViewContextWrapper(mContext)); - mViewHost.setView(rootLayout, lp); - SnapshotDrawerUtils.drawSnapshotOnSurface(info, lp, wlw.mChildSurface, snapshot, - snapshotBounds, windowBounds, topWindowInsetsState, false /* releaseAfterDraw */); - - final ActivityManager.TaskDescription taskDescription = - SnapshotDrawerUtils.getOrCreateTaskDescription(runningTaskInfo); - - final SnapshotWindowRecord record = new SnapshotWindowRecord(mViewHost, wlw.mChildSurface, - taskDescription.getBackgroundColor(), snapshot.hasImeSurface(), - runningTaskInfo.topActivityType, removeExecutor); - mStartingWindowRecordManager.addRecord(taskId, record); - info.notifyAddComplete(wlw.mChildSurface); - } - - private class SnapshotWindowRecord extends StartingSurfaceDrawer.SnapshotRecord { - private SurfaceControlViewHost mViewHost; - private SurfaceControl mChildSurface; - private final boolean mHasImeSurface; - - SnapshotWindowRecord(SurfaceControlViewHost viewHost, SurfaceControl childSurface, - int bgColor, boolean hasImeSurface, int activityType, - ShellExecutor removeExecutor) { - super(activityType, removeExecutor); - mViewHost = viewHost; - mChildSurface = childSurface; - mBGColor = bgColor; - mHasImeSurface = hasImeSurface; - } - - @Override - protected void removeImmediately() { - super.removeImmediately(); - fadeoutThenRelease(); - } - - void fadeoutThenRelease() { - final ValueAnimator fadeOutAnimator = ValueAnimator.ofFloat(1f, 0f); - fadeOutAnimator.setDuration(DEFAULT_FADEOUT_DURATION); - final SurfaceControl.Transaction t = mTransactionPool.acquire(); - fadeOutAnimator.addUpdateListener(animation -> { - if (mChildSurface == null || !mChildSurface.isValid()) { - fadeOutAnimator.cancel(); - return; - } - t.setAlpha(mChildSurface, (float) animation.getAnimatedValue()); - t.apply(); - }); - - fadeOutAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - if (mChildSurface == null || !mChildSurface.isValid()) { - fadeOutAnimator.cancel(); - } - } - - @Override - public void onAnimationEnd(Animator animation) { - mTransactionPool.release(t); - if (mChildSurface != null) { - final SurfaceControl.Transaction t = mTransactionPool.acquire(); - t.remove(mChildSurface).apply(); - mTransactionPool.release(t); - mChildSurface = null; - } - if (mViewHost != null) { - mViewHost.release(); - mViewHost = null; - } - } - }); - fadeOutAnimator.start(); - } - - @Override - protected boolean hasImeSurface() { - return mHasImeSurface; - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSplashWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSplashWindowCreator.java deleted file mode 100644 index 12a0d4054b4d1..0000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSplashWindowCreator.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.startingsurface; - -import static android.graphics.Color.WHITE; -import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; - -import android.app.ActivityManager; -import android.content.Context; -import android.content.pm.ActivityInfo; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.hardware.display.DisplayManager; -import android.os.Binder; -import android.os.SystemClock; -import android.view.Display; -import android.view.SurfaceControl; -import android.view.SurfaceControlViewHost; -import android.view.WindowManager; -import android.widget.FrameLayout; -import android.window.SplashScreenView; -import android.window.StartingWindowInfo; -import android.window.StartingWindowRemovalInfo; - -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.common.TransactionPool; - -class WindowlessSplashWindowCreator extends AbsSplashWindowCreator { - - private final TransactionPool mTransactionPool; - - WindowlessSplashWindowCreator(SplashscreenContentDrawer contentDrawer, - Context context, - ShellExecutor splashScreenExecutor, - DisplayManager displayManager, - StartingSurfaceDrawer.StartingWindowRecordManager startingWindowRecordManager, - TransactionPool pool) { - super(contentDrawer, context, splashScreenExecutor, displayManager, - startingWindowRecordManager); - mTransactionPool = pool; - } - - void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, SurfaceControl rootSurface) { - final ActivityManager.RunningTaskInfo taskInfo = windowInfo.taskInfo; - final ActivityInfo activityInfo = windowInfo.targetActivityInfo != null - ? windowInfo.targetActivityInfo - : taskInfo.topActivityInfo; - if (activityInfo == null || activityInfo.packageName == null) { - return; - } - - final int displayId = taskInfo.displayId; - final Display display = mDisplayManager.getDisplay(displayId); - if (display == null) { - // Can't show splash screen on requested display, so skip showing at all. - return; - } - final Context myContext = SplashscreenContentDrawer.createContext(mContext, windowInfo, - 0 /* theme */, STARTING_WINDOW_TYPE_SPLASH_SCREEN, mDisplayManager); - if (myContext == null) { - return; - } - final StartingSurfaceDrawer.WindowlessStartingWindow wlw = - new StartingSurfaceDrawer.WindowlessStartingWindow( - taskInfo.configuration, rootSurface); - final SurfaceControlViewHost viewHost = new SurfaceControlViewHost( - myContext, display, wlw, "WindowlessSplashWindowCreator"); - final String title = "Windowless Splash " + taskInfo.taskId; - final WindowManager.LayoutParams lp = SplashscreenContentDrawer.createLayoutParameters( - myContext, windowInfo, STARTING_WINDOW_TYPE_SPLASH_SCREEN, title, - PixelFormat.TRANSLUCENT, new Binder()); - final Rect windowBounds = taskInfo.configuration.windowConfiguration.getBounds(); - lp.width = windowBounds.width(); - lp.height = windowBounds.height(); - final ActivityManager.TaskDescription taskDescription; - if (taskInfo.taskDescription != null) { - taskDescription = taskInfo.taskDescription; - } else { - taskDescription = new ActivityManager.TaskDescription(); - taskDescription.setBackgroundColor(WHITE); - } - - final FrameLayout rootLayout = new FrameLayout( - mSplashscreenContentDrawer.createViewContextWrapper(mContext)); - viewHost.setView(rootLayout, lp); - - final int bgColor = taskDescription.getBackgroundColor(); - final SplashScreenView splashScreenView = mSplashscreenContentDrawer - .makeSimpleSplashScreenContentView(myContext, windowInfo, bgColor); - rootLayout.addView(splashScreenView); - final SplashWindowRecord record = new SplashWindowRecord(viewHost, splashScreenView, - wlw.mChildSurface, bgColor); - mStartingWindowRecordManager.addRecord(taskInfo.taskId, record); - windowInfo.notifyAddComplete(wlw.mChildSurface); - } - - private class SplashWindowRecord extends StartingSurfaceDrawer.StartingWindowRecord { - private SurfaceControlViewHost mViewHost; - private final long mCreateTime; - private SurfaceControl mChildSurface; - private final SplashScreenView mSplashView; - - SplashWindowRecord(SurfaceControlViewHost viewHost, SplashScreenView splashView, - SurfaceControl childSurface, int bgColor) { - mViewHost = viewHost; - mSplashView = splashView; - mChildSurface = childSurface; - mBGColor = bgColor; - mCreateTime = SystemClock.uptimeMillis(); - } - - @Override - public void removeIfPossible(StartingWindowRemovalInfo info, boolean immediately) { - if (!immediately) { - mSplashscreenContentDrawer.applyExitAnimation(mSplashView, - info.windowAnimationLeash, info.mainFrame, - this::release, mCreateTime, 0 /* roundedCornerRadius */); - } else { - release(); - } - } - - void release() { - if (mChildSurface != null) { - final SurfaceControl.Transaction t = mTransactionPool.acquire(); - t.remove(mChildSurface).apply(); - mTransactionPool.release(t); - mChildSurface = null; - } - if (mViewHost != null) { - mViewHost.release(); - mViewHost = null; - } - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java index 72fc8686f6487..bb43d7c1a0908 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java @@ -22,7 +22,6 @@ import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; -import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_WINDOWLESS; import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED; import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_DRAWN; import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT; @@ -31,7 +30,6 @@ import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK; import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING; import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH; import static android.window.StartingWindowInfo.TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN; -import static android.window.StartingWindowInfo.TYPE_PARAMETER_WINDOWLESS; import android.window.StartingWindowInfo; @@ -57,7 +55,6 @@ public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgor final boolean legacySplashScreen = ((parameter & TYPE_PARAMETER_LEGACY_SPLASH_SCREEN) != 0); final boolean activityDrawn = (parameter & TYPE_PARAMETER_ACTIVITY_DRAWN) != 0; - final boolean windowlessSurface = (parameter & TYPE_PARAMETER_WINDOWLESS) != 0; final boolean topIsHome = windowInfo.taskInfo.topActivityType == ACTIVITY_TYPE_HOME; ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, @@ -70,15 +67,10 @@ public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgor + "isSolidColorSplashScreen=%b, " + "legacySplashScreen=%b, " + "activityDrawn=%b, " - + "windowless=%b, " + "topIsHome=%b", newTask, taskSwitch, processRunning, allowTaskSnapshot, activityCreated, - isSolidColorSplashScreen, legacySplashScreen, activityDrawn, windowlessSurface, - topIsHome); + isSolidColorSplashScreen, legacySplashScreen, activityDrawn, topIsHome); - if (windowlessSurface) { - return STARTING_WINDOW_TYPE_WINDOWLESS; - } if (!topIsHome) { if (!processRunning || newTask || (taskSwitch && !activityCreated)) { return getSplashscreenType(isSolidColorSplashScreen, legacySplashScreen); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java index bf62acfc47a1d..11fda8bf7bbc6 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java @@ -24,8 +24,8 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; -import static com.android.wm.shell.startingsurface.SplashscreenContentDrawer.MAX_ANIMATION_DURATION; -import static com.android.wm.shell.startingsurface.SplashscreenContentDrawer.MINIMAL_ANIMATION_DURATION; +import static com.android.wm.shell.startingsurface.StartingSurfaceDrawer.MAX_ANIMATION_DURATION; +import static com.android.wm.shell.startingsurface.StartingSurfaceDrawer.MINIMAL_ANIMATION_DURATION; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; @@ -56,9 +56,11 @@ import android.os.IBinder; import android.os.Looper; import android.os.UserHandle; import android.testing.TestableContext; +import android.view.Display; import android.view.IWindowSession; import android.view.InsetsState; import android.view.Surface; +import android.view.View; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.WindowMetrics; @@ -104,7 +106,36 @@ public class StartingSurfaceDrawerTests extends ShellTestCase { private ShellExecutor mTestExecutor; private final TestableContext mTestContext = new TestContext( InstrumentationRegistry.getInstrumentation().getTargetContext()); - StartingSurfaceDrawer mStartingSurfaceDrawer; + TestStartingSurfaceDrawer mStartingSurfaceDrawer; + + static final class TestStartingSurfaceDrawer extends StartingSurfaceDrawer{ + int mAddWindowForTask = 0; + + TestStartingSurfaceDrawer(Context context, ShellExecutor splashScreenExecutor, + IconProvider iconProvider, TransactionPool pool) { + super(context, splashScreenExecutor, iconProvider, pool); + } + + @Override + protected boolean addWindow(int taskId, IBinder appToken, View view, Display display, + WindowManager.LayoutParams params, int suggestType) { + // listen for addView + mAddWindowForTask = taskId; + saveSplashScreenRecord(appToken, taskId, view, suggestType); + // Do not wait for background color + return false; + } + + @Override + protected void removeWindowSynced(StartingWindowRemovalInfo removalInfo, + boolean immediately) { + // listen for removeView + if (mAddWindowForTask == removalInfo.taskId) { + mAddWindowForTask = 0; + } + mStartingWindowRecords.remove(removalInfo.taskId); + } + } private static class TestContext extends TestableContext { TestContext(Context context) { @@ -134,51 +165,44 @@ public class StartingSurfaceDrawerTests extends ShellTestCase { doReturn(metrics).when(mMockWindowManager).getMaximumWindowMetrics(); doNothing().when(mMockWindowManager).addView(any(), any()); mTestExecutor = new HandlerExecutor(mTestHandler); - mStartingSurfaceDrawer = new StartingSurfaceDrawer(mTestContext, mTestExecutor, - mIconProvider, mTransactionPool); mStartingSurfaceDrawer = spy( - new StartingSurfaceDrawer(mTestContext, mTestExecutor, mIconProvider, + new TestStartingSurfaceDrawer(mTestContext, mTestExecutor, mIconProvider, mTransactionPool)); - spyOn(mStartingSurfaceDrawer.mSplashscreenWindowCreator); - spyOn(mStartingSurfaceDrawer.mWindowRecords); - spyOn(mStartingSurfaceDrawer.mWindowlessRecords); } @Test public void testAddSplashScreenSurface() { final int taskId = 1; final StartingWindowInfo windowInfo = - createWindowInfo(taskId, android.R.style.Theme, mBinder); - mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, + createWindowInfo(taskId, android.R.style.Theme); + mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder, STARTING_WINDOW_TYPE_SPLASH_SCREEN); waitHandlerIdle(mTestHandler); - verify(mStartingSurfaceDrawer.mSplashscreenWindowCreator).addWindow( - eq(taskId), eq(mBinder), any(), any(), any(), + verify(mStartingSurfaceDrawer).addWindow(eq(taskId), eq(mBinder), any(), any(), any(), eq(STARTING_WINDOW_TYPE_SPLASH_SCREEN)); + assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, taskId); StartingWindowRemovalInfo removalInfo = new StartingWindowRemovalInfo(); removalInfo.taskId = windowInfo.taskInfo.taskId; mStartingSurfaceDrawer.removeStartingWindow(removalInfo); waitHandlerIdle(mTestHandler); - verify(mStartingSurfaceDrawer.mWindowRecords).removeWindow(any(), eq(false)); - assertEquals(mStartingSurfaceDrawer.mWindowRecords.recordSize(), 0); + verify(mStartingSurfaceDrawer).removeWindowSynced(any(), eq(false)); + assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, 0); } @Test public void testFallbackDefaultTheme() { final int taskId = 1; final StartingWindowInfo windowInfo = - createWindowInfo(taskId, 0, mBinder); + createWindowInfo(taskId, 0); final int[] theme = new int[1]; doAnswer(invocation -> theme[0] = (Integer) invocation.callRealMethod()) - .when(mStartingSurfaceDrawer.mSplashscreenWindowCreator) - .getSplashScreenTheme(eq(0), any()); + .when(mStartingSurfaceDrawer).getSplashScreenTheme(eq(0), any()); - mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, + mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder, STARTING_WINDOW_TYPE_SPLASH_SCREEN); waitHandlerIdle(mTestHandler); - verify(mStartingSurfaceDrawer.mSplashscreenWindowCreator) - .getSplashScreenTheme(eq(0), any()); + verify(mStartingSurfaceDrawer).getSplashScreenTheme(eq(0), any()); assertNotEquals(theme[0], 0); } @@ -217,7 +241,7 @@ public class StartingSurfaceDrawerTests extends ShellTestCase { public void testRemoveTaskSnapshotWithImeSurfaceWhenOnImeDrawn() throws Exception { final int taskId = 1; final StartingWindowInfo windowInfo = - createWindowInfo(taskId, android.R.style.Theme, mBinder); + createWindowInfo(taskId, android.R.style.Theme); TaskSnapshot snapshot = createTaskSnapshot(100, 100, new Point(100, 100), new Rect(0, 0, 0, 50), true /* hasImeSurface */); final IWindowSession session = WindowManagerGlobal.getWindowSession(); @@ -246,7 +270,7 @@ public class StartingSurfaceDrawerTests extends ShellTestCase { when(TaskSnapshotWindow.create(eq(windowInfo), eq(mBinder), eq(snapshot), any(), any())).thenReturn(mockSnapshotWindow); // Simulate a task snapshot window created with IME snapshot shown. - mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, snapshot); + mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, mBinder, snapshot); waitHandlerIdle(mTestHandler); // Verify the task snapshot with IME snapshot will be removed when received the real IME @@ -254,36 +278,27 @@ public class StartingSurfaceDrawerTests extends ShellTestCase { // makeTaskSnapshotWindow shall call removeWindowSynced before there add a new // StartingWindowRecord for the task. mStartingSurfaceDrawer.onImeDrawnOnTask(1); - verify(mStartingSurfaceDrawer.mWindowRecords, times(2)) - .removeWindow(any(), eq(true)); + verify(mStartingSurfaceDrawer, times(2)) + .removeWindowSynced(any(), eq(true)); } } @Test public void testClearAllWindows() { final int taskId = 1; - mStartingSurfaceDrawer.mWindowRecords.addRecord(taskId, - new StartingSurfaceDrawer.StartingWindowRecord() { - @Override - public void removeIfPossible(StartingWindowRemovalInfo info, - boolean immediately) { + final StartingWindowInfo windowInfo = + createWindowInfo(taskId, android.R.style.Theme); + mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder, + STARTING_WINDOW_TYPE_SPLASH_SCREEN); + waitHandlerIdle(mTestHandler); + verify(mStartingSurfaceDrawer).addWindow(eq(taskId), eq(mBinder), any(), any(), any(), + eq(STARTING_WINDOW_TYPE_SPLASH_SCREEN)); + assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, taskId); - } - }); - mStartingSurfaceDrawer.mWindowlessRecords.addRecord(taskId, - new StartingSurfaceDrawer.StartingWindowRecord() { - @Override - public void removeIfPossible(StartingWindowRemovalInfo info, - boolean immediately) { - - } - }); mStartingSurfaceDrawer.clearAllWindows(); waitHandlerIdle(mTestHandler); - verify(mStartingSurfaceDrawer.mWindowRecords).removeWindow(any(), eq(true)); - assertEquals(mStartingSurfaceDrawer.mWindowRecords.recordSize(), 0); - verify(mStartingSurfaceDrawer.mWindowlessRecords).removeWindow(any(), eq(true)); - assertEquals(mStartingSurfaceDrawer.mWindowlessRecords.recordSize(), 0); + verify(mStartingSurfaceDrawer).removeWindowSynced(any(), eq(true)); + assertEquals(mStartingSurfaceDrawer.mStartingWindowRecords.size(), 0); } @Test @@ -336,7 +351,7 @@ public class StartingSurfaceDrawerTests extends ShellTestCase { longAppDuration, longAppDuration)); } - private StartingWindowInfo createWindowInfo(int taskId, int themeResId, IBinder appToken) { + private StartingWindowInfo createWindowInfo(int taskId, int themeResId) { StartingWindowInfo windowInfo = new StartingWindowInfo(); final ActivityInfo info = new ActivityInfo(); info.applicationInfo = new ApplicationInfo(); @@ -345,7 +360,6 @@ public class StartingSurfaceDrawerTests extends ShellTestCase { final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo(); taskInfo.topActivityInfo = info; taskInfo.taskId = taskId; - windowInfo.appToken = appToken; windowInfo.targetActivityInfo = info; windowInfo.taskInfo = taskInfo; windowInfo.topOpaqueWindowInsetsState = new InsetsState(); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index cc81d526af5e1..0a833f4f2dbac 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3512,10 +3512,7 @@ class Task extends TaskFragment { info.isKeyguardOccluded = mAtmService.mKeyguardController.isDisplayOccluded(DEFAULT_DISPLAY); - info.startingWindowTypeParameter = activity.mStartingData != null - ? activity.mStartingData.mTypeParams - : (StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED - | StartingWindowInfo.TYPE_PARAMETER_WINDOWLESS); + info.startingWindowTypeParameter = activity.mStartingData.mTypeParams; if ((info.startingWindowTypeParameter & StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED) != 0) { final WindowState topMainWin = getWindow(w -> w.mAttrs.type == TYPE_BASE_APPLICATION); diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index b13136534de36..3a30e4b0cf1f4 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -16,7 +16,6 @@ package com.android.server.wm; -import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.TaskInfo.cameraCompatControlStateToString; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; @@ -44,7 +43,6 @@ import android.view.Display; import android.view.SurfaceControl; import android.window.ITaskOrganizer; import android.window.ITaskOrganizerController; -import android.window.IWindowlessStartingSurfaceCallback; import android.window.SplashScreenView; import android.window.StartingWindowInfo; import android.window.StartingWindowRemovalInfo; @@ -658,10 +656,9 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { info.splashScreenThemeResId = launchTheme; } info.taskSnapshot = taskSnapshot; - info.appToken = activity.token; // make this happen prior than prepare surface try { - lastOrganizer.addStartingWindow(info); + lastOrganizer.addStartingWindow(info, activity.token); } catch (RemoteException e) { Slog.e(TAG, "Exception sending onTaskStart callback", e); return false; @@ -707,55 +704,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } } - /** - * Create a starting surface which attach on a given surface. - * @param activity Target activity, this isn't necessary to be the top activity. - * @param root The root surface which the created surface will attach on. - * @param taskSnapshot Whether to draw snapshot. - * @param callback Called when surface is drawn and attached to the root surface. - * @return The taskId, this is a token and should be used to remove the surface, even if - * the task was removed from hierarchy. - */ - int addWindowlessStartingSurface(Task task, ActivityRecord activity, SurfaceControl root, - TaskSnapshot taskSnapshot, IWindowlessStartingSurfaceCallback callback) { - final Task rootTask = task.getRootTask(); - if (rootTask == null) { - return INVALID_TASK_ID; - } - final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast(); - if (lastOrganizer == null) { - return INVALID_TASK_ID; - } - final StartingWindowInfo info = task.getStartingWindowInfo(activity); - info.taskInfo.taskDescription = activity.taskDescription; - info.taskSnapshot = taskSnapshot; - info.windowlessStartingSurfaceCallback = callback; - info.rootSurface = root; - try { - lastOrganizer.addStartingWindow(info); - } catch (RemoteException e) { - Slog.e(TAG, "Exception sending addWindowlessStartingSurface ", e); - return INVALID_TASK_ID; - } - return task.mTaskId; - } - - void removeWindowlessStartingSurface(int taskId, boolean immediately) { - final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast(); - if (lastOrganizer == null || taskId == 0) { - return; - } - final StartingWindowRemovalInfo removalInfo = new StartingWindowRemovalInfo(); - removalInfo.taskId = taskId; - removalInfo.windowlessSurface = true; - removalInfo.removeImmediately = immediately; - try { - lastOrganizer.removeStartingWindow(removalInfo); - } catch (RemoteException e) { - Slog.e(TAG, "Exception sending removeWindowlessStartingSurface ", e); - } - } - boolean copySplashScreenView(Task task) { final Task rootTask = task.getRootTask(); if (rootTask == null) { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index d7e4c5523eee9..169586e2d9dcc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -1023,7 +1023,7 @@ public class WindowOrganizerTests extends WindowTestsBase { RunningTaskInfo mInfo; @Override - public void addStartingWindow(StartingWindowInfo info) { } + public void addStartingWindow(StartingWindowInfo info, IBinder appToken) { } @Override public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) { } @Override diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index fafe90a5df55d..323894ca76dfd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -1583,10 +1583,10 @@ class WindowTestsBase extends SystemServiceTestsBase { } @Override - public void addStartingWindow(StartingWindowInfo info) { + public void addStartingWindow(StartingWindowInfo info, IBinder appToken) { synchronized (mWMService.mGlobalLock) { final ActivityRecord activity = mWMService.mRoot.getActivityRecord( - info.appToken); + appToken); IWindow iWindow = mock(IWindow.class); doReturn(mock(IBinder.class)).when(iWindow).asBinder(); final WindowState window = WindowTestsBase.createWindow(null, @@ -1596,8 +1596,8 @@ class WindowTestsBase extends SystemServiceTestsBase { iWindow, mPowerManagerWrapper); activity.mStartingWindow = window; - mAppWindowMap.put(info.appToken, window); - mTaskAppMap.put(info.taskInfo.taskId, info.appToken); + mAppWindowMap.put(appToken, window); + mTaskAppMap.put(info.taskInfo.taskId, appToken); } if (mRunnableWhenAddingSplashScreen != null) { mRunnableWhenAddingSplashScreen.run();