Merge "Revert "Introduce Windowless starting window."" into udc-dev
diff --git a/core/java/android/window/ITaskOrganizer.aidl b/core/java/android/window/ITaskOrganizer.aidl
index bf9d3c2..fd86769 100644
--- a/core/java/android/window/ITaskOrganizer.aidl
+++ b/core/java/android/window/ITaskOrganizer.aidl
@@ -34,8 +34,9 @@
* 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 a081356..0000000
--- 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 071c20f..1a58fd5 100644
--- a/core/java/android/window/SnapshotDrawerUtils.java
+++ b/core/java/android/window/SnapshotDrawerUtils.java
@@ -329,21 +329,6 @@
}
/**
- * 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.
*/
public static void drawSnapshotOnSurface(StartingWindowInfo info, WindowManager.LayoutParams lp,
@@ -359,8 +344,13 @@
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 451acbe..1b64e61 100644
--- a/core/java/android/window/StartingWindowInfo.java
+++ b/core/java/android/window/StartingWindowInfo.java
@@ -22,12 +22,9 @@
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 @@
/** @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 @@
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 @@
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 @@
* @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 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 @@
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 @@
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 5181236..384dacf 100644
--- a/core/java/android/window/StartingWindowRemovalInfo.java
+++ b/core/java/android/window/StartingWindowRemovalInfo.java
@@ -67,16 +67,6 @@
*/
public float roundedCornerRadius;
- /**
- * Remove windowless surface.
- */
- public boolean windowlessSurface;
-
- /**
- * Remove immediately.
- */
- public boolean removeImmediately;
-
public StartingWindowRemovalInfo() {
}
@@ -97,8 +87,6 @@
playRevealAnimation = source.readBoolean();
deferRemoveForIme = source.readBoolean();
roundedCornerRadius = source.readFloat();
- windowlessSurface = source.readBoolean();
- removeImmediately = source.readBoolean();
}
@Override
@@ -109,8 +97,6 @@
dest.writeBoolean(playRevealAnimation);
dest.writeBoolean(deferRemoveForIme);
dest.writeFloat(roundedCornerRadius);
- dest.writeBoolean(windowlessSurface);
- dest.writeBoolean(removeImmediately);
}
@Override
@@ -119,9 +105,7 @@
+ " frame=" + mainFrame
+ " playRevealAnimation=" + playRevealAnimation
+ " roundedCornerRadius=" + roundedCornerRadius
- + " deferRemoveForIme=" + deferRemoveForIme
- + " windowlessSurface=" + windowlessSurface
- + " removeImmediately=" + removeImmediately + "}";
+ + " deferRemoveForIme=" + deferRemoveForIme + "}";
}
public static final @android.annotation.NonNull Creator<StartingWindowRemovalInfo> CREATOR =
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index d4728c1..02878f8 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -92,10 +92,13 @@
* 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 @@
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 b6fd0bb..585f81c 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 @@
}
@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 1ddd8f9..0000000
--- 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 20c4d5a..0000000
--- 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 79cd891..ebb957b 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_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 @@
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 4db81e2..0000000
--- 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<SurfaceControlViewHost> 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<SplashScreenView> {
- 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 ff06db3..4f07bfe 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();
- @VisibleForTesting
- final StartingWindowRecordManager mWindowRecords = new StartingWindowRecordManager();
- // Windowless surface could co-exist with starting window in a task.
- @VisibleForTesting
- final StartingWindowRecordManager mWindowlessRecords = new StartingWindowRecordManager();
+ 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;
+
/**
* @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<StartingWindowRecord> 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<SurfaceControlViewHost> 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 @@
*
* @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<SplashScreenView> {
+ 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 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 @@
* @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<StartingWindowRecord> 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 bec4ba3..be2e793 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_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.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 @@
/**
* Called when a task need a starting window.
*/
- public void addStartingWindow(StartingWindowInfo windowInfo) {
+ public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) {
mSplashScreenExecutor.execute(() -> {
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 void removeStartingWindow(StartingWindowRemovalInfo removalInfo) {
mSplashScreenExecutor.execute(() -> 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 c964df1..a05ed4f 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 @@
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 @@
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 @@
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 @@
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 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 @@
int backgroundColor = taskDescription.getBackgroundColor();
mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
mOrientationOnCreation = currentOrientation;
+ mActivityType = activityType;
mClearWindowHandler = clearWindowHandler;
mHasImeSurface = snapshot.hasImeSurface();
}
@@ -170,7 +186,23 @@
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 1445478..0000000
--- 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 12a0d40..0000000
--- 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 72fc8686..bb43d7c 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_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_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 @@
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 @@
+ "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 bf62acf..11fda8b 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.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.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 @@
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 @@
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 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 @@
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 @@
// 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 @@
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 @@
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 cc81d52..0a833f4 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 @@
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 b131365..3a30e4b 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.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 @@
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 @@
}
}
- /**
- * 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 d7e4c55..169586e 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 @@
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 fafe90a..323894ca 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 @@
}
@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 @@
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();