diff options
11 files changed, 216 insertions, 19 deletions
diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 5fa75dd68ce0..adc30d459678 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -3185,6 +3185,7 @@ package android.window { method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.app.ActivityManager.RunningTaskInfo> getChildTasks(@NonNull android.window.WindowContainerToken, @NonNull int[]); method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public android.window.WindowContainerToken getImeTarget(int); method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.app.ActivityManager.RunningTaskInfo> getRootTasks(int, @NonNull int[]); + method @BinderThread public void onAppSplashScreenViewRemoved(int); method @BinderThread public void onBackPressedOnTaskRoot(@NonNull android.app.ActivityManager.RunningTaskInfo); method @BinderThread public void onTaskAppeared(@NonNull android.app.ActivityManager.RunningTaskInfo, @NonNull android.view.SurfaceControl); method @BinderThread public void onTaskInfoChanged(@NonNull android.app.ActivityManager.RunningTaskInfo); diff --git a/core/java/android/window/ITaskOrganizer.aidl b/core/java/android/window/ITaskOrganizer.aidl index 3eb35c2c5e8a..8b8dba89ea67 100644 --- a/core/java/android/window/ITaskOrganizer.aidl +++ b/core/java/android/window/ITaskOrganizer.aidl @@ -52,6 +52,11 @@ oneway interface ITaskOrganizer { void copySplashScreenView(int taskId); /** + * Called when the Task removed the splash screen. + */ + void onAppSplashScreenViewRemoved(int taskId); + + /** * A callback when the Task is available for the registered organizer. The client is responsible * for releasing the SurfaceControl in the callback. For non-root tasks, the leash may initially * be hidden so it is up to the organizer to show this task. diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java index 6772afeb0270..4a3bf91645f2 100644 --- a/core/java/android/window/SplashScreenView.java +++ b/core/java/android/window/SplashScreenView.java @@ -34,8 +34,10 @@ import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import android.os.RemoteCallback; import android.os.Trace; import android.util.AttributeSet; import android.util.Log; @@ -76,7 +78,7 @@ import java.time.Instant; */ public final class SplashScreenView extends FrameLayout { private static final String TAG = SplashScreenView.class.getSimpleName(); - private static final boolean DEBUG = false; + private static final boolean DEBUG = Build.IS_DEBUGGABLE; private static final int LIGHT_BARS_MASK = WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS @@ -85,6 +87,7 @@ public final class SplashScreenView extends FrameLayout { | FLAG_TRANSLUCENT_NAVIGATION | FLAG_TRANSLUCENT_STATUS; private boolean mNotCopyable; + private boolean mIsCopied; private int mInitBackgroundColor; private int mInitIconBackgroundColor; private View mIconView; @@ -103,6 +106,10 @@ public final class SplashScreenView extends FrameLayout { private SurfaceControlViewHost.SurfacePackage mSurfacePackage; @Nullable private SurfaceView mSurfaceView; + @Nullable + private SurfaceControlViewHost mSurfaceHost; + @Nullable + private RemoteCallback mClientCallback; // cache original window and status private Window mWindow; @@ -127,6 +134,7 @@ public final class SplashScreenView extends FrameLayout { private Bitmap mParceledIconBitmap; private Drawable mIconDrawable; private SurfaceControlViewHost.SurfacePackage mSurfacePackage; + private RemoteCallback mClientCallback; private int mBrandingImageWidth; private int mBrandingImageHeight; private Drawable mBrandingDrawable; @@ -161,6 +169,7 @@ public final class SplashScreenView extends FrameLayout { } mIconAnimationStart = Instant.ofEpochMilli(parcelable.mIconAnimationStartMillis); mIconAnimationDuration = Duration.ofMillis(parcelable.mIconAnimationDurationMillis); + mClientCallback = parcelable.mClientCallback; if (DEBUG) { Log.d(TAG, String.format("Building from parcel drawable: %s", mIconDrawable)); } @@ -228,6 +237,7 @@ public final class SplashScreenView extends FrameLayout { view.mInitBackgroundColor = mBackgroundColor; view.mInitIconBackgroundColor = mIconBackground; view.setBackgroundColor(mBackgroundColor); + view.mClientCallback = mClientCallback; view.mBrandingImageView = view.findViewById(R.id.splashscreen_branding_view); @@ -285,7 +295,8 @@ public final class SplashScreenView extends FrameLayout { if (mSurfacePackage == null) { if (DEBUG) { Log.d(TAG, - "Creating Original SurfacePackage. SurfaceView: " + surfaceView); + "SurfaceControlViewHost created on thread " + + Thread.currentThread().getId()); } SurfaceControlViewHost viewHost = new SurfaceControlViewHost(mContext, @@ -297,6 +308,7 @@ public final class SplashScreenView extends FrameLayout { SurfaceControlViewHost.SurfacePackage surfacePackage = viewHost.getSurfacePackage(); surfaceView.setChildSurfacePackage(surfacePackage); view.mSurfacePackage = surfacePackage; + view.mSurfaceHost = viewHost; view.mSurfacePackageCopy = new SurfaceControlViewHost.SurfacePackage( surfacePackage); } else { @@ -357,6 +369,7 @@ public final class SplashScreenView extends FrameLayout { * @hide */ public void onCopied() { + mIsCopied = true; if (mSurfaceView == null) { return; } @@ -369,6 +382,12 @@ public final class SplashScreenView extends FrameLayout { mSurfacePackage = null; } + /** @hide **/ + @Nullable + public SurfaceControlViewHost getSurfaceHost() { + return mSurfaceHost; + } + @Override public void setAlpha(float alpha) { super.setAlpha(alpha); @@ -407,8 +426,7 @@ public final class SplashScreenView extends FrameLayout { if (DEBUG) { mSurfacePackage.getSurfaceControl().addOnReparentListener( (transaction, parent) -> Log.e(TAG, - String.format("SurfacePackage'surface reparented.\n Parent: %s", - parent), new Throwable())); + String.format("SurfacePackage'surface reparented to %s", parent))); Log.d(TAG, "Transferring surface " + mSurfaceView.toString()); } mSurfaceView.setChildSurfacePackage(mSurfacePackage); @@ -466,9 +484,36 @@ public final class SplashScreenView extends FrameLayout { mHasRemoved = true; } + /** @hide **/ + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + releaseAnimationSurfaceHost(); + } + + private void releaseAnimationSurfaceHost() { + if (mSurfaceHost != null && !mIsCopied) { + final SurfaceControlViewHost finalSurfaceHost = mSurfaceHost; + mSurfaceHost = null; + finalSurfaceHost.getView().post(() -> { + if (DEBUG) { + Log.d(TAG, + "Shell removed splash screen." + + " Releasing SurfaceControlViewHost on thread #" + + Thread.currentThread().getId()); + } + finalSurfaceHost.release(); + }); + } else if (mSurfacePackage != null && mSurfaceHost == null) { + mSurfacePackage = null; + mClientCallback.sendResult(null); + } + } + /** * Called when this view is attached to an activity. This also makes SystemUI colors * transparent so the content of splash screen view can draw fully. + * * @hide */ public void attachHostActivityAndSetSystemUIColors(Activity activity, Window window) { @@ -585,6 +630,7 @@ public final class SplashScreenView extends FrameLayout { private long mIconAnimationDurationMillis; private SurfaceControlViewHost.SurfacePackage mSurfacePackage; + private RemoteCallback mClientCallback; public SplashScreenViewParcelable(SplashScreenView view) { mIconSize = view.mIconView.getWidth(); @@ -641,6 +687,7 @@ public final class SplashScreenView extends FrameLayout { mIconAnimationDurationMillis = source.readLong(); mIconBackground = source.readInt(); mSurfacePackage = source.readTypedObject(SurfaceControlViewHost.SurfacePackage.CREATOR); + mClientCallback = source.readTypedObject(RemoteCallback.CREATOR); } @Override @@ -660,6 +707,7 @@ public final class SplashScreenView extends FrameLayout { dest.writeLong(mIconAnimationDurationMillis); dest.writeInt(mIconBackground); dest.writeTypedObject(mSurfacePackage, flags); + dest.writeTypedObject(mClientCallback, flags); } public static final @NonNull Parcelable.Creator<SplashScreenViewParcelable> CREATOR = @@ -697,5 +745,13 @@ public final class SplashScreenView extends FrameLayout { int getIconBackground() { return mIconBackground; } + + /** + * Sets the {@link RemoteCallback} that will be called by the client to notify the shell + * of the removal of the {@link SplashScreenView}. + */ + public void setClientCallback(@NonNull RemoteCallback clientCallback) { + mClientCallback = clientCallback; + } } } diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java index 3340cf4fb707..73995491668a 100644 --- a/core/java/android/window/TaskOrganizer.java +++ b/core/java/android/window/TaskOrganizer.java @@ -117,6 +117,16 @@ public class TaskOrganizer extends WindowOrganizer { public void copySplashScreenView(int taskId) {} /** + * Notify the shell ({@link com.android.wm.shell.ShellTaskOrganizer} that the client has + * removed the splash screen view. + * @see com.android.wm.shell.ShellTaskOrganizer#onAppSplashScreenViewRemoved(int) + * @see SplashScreenView#remove() + */ + @BinderThread + public void onAppSplashScreenViewRemoved(int taskId) { + } + + /** * Called when a task with the registered windowing mode can be controlled by this task * organizer. For non-root tasks, the leash may initially be hidden so it is up to the organizer * to show this task. @@ -236,11 +246,16 @@ public class TaskOrganizer extends WindowOrganizer { } @Override - public void copySplashScreenView(int taskId) { + public void copySplashScreenView(int taskId) { mExecutor.execute(() -> TaskOrganizer.this.copySplashScreenView(taskId)); } @Override + public void onAppSplashScreenViewRemoved(int taskId) { + mExecutor.execute(() -> TaskOrganizer.this.onAppSplashScreenViewRemoved(taskId)); + } + + @Override public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { mExecutor.execute(() -> TaskOrganizer.this.onTaskAppeared(taskInfo, leash)); } diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 3c9086dde021..ac5e2d0fcacb 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -961,6 +961,12 @@ "group": "WM_DEBUG_APP_TRANSITIONS", "at": "com\/android\/server\/wm\/AppTransitionController.java" }, + "-1003678883": { + "message": "Cleaning splash screen token=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_STARTING_WINDOW", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "-1003060523": { "message": "Finish needs to pause: %s", "level": "VERBOSE", 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 f7fb63d9ab98..4b1955e56a6c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -325,6 +325,13 @@ public class ShellTaskOrganizer extends TaskOrganizer { } @Override + public void onAppSplashScreenViewRemoved(int taskId) { + if (mStartingWindow != null) { + mStartingWindow.onAppSplashScreenViewRemoved(taskId); + } + } + + @Override public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { synchronized (mLock) { onTaskAppeared(new TaskAppearedInfo(taskInfo, leash)); 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 4d33cb0452dc..46db35a6e29f 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 @@ -35,6 +35,7 @@ import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.hardware.display.DisplayManager; import android.os.IBinder; +import android.os.RemoteCallback; import android.os.Trace; import android.os.UserHandle; import android.util.Slog; @@ -42,6 +43,7 @@ import android.util.SparseArray; import android.view.Choreographer; import android.view.Display; import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; import android.view.View; import android.view.WindowManager; import android.widget.FrameLayout; @@ -121,6 +123,13 @@ public class StartingSurfaceDrawer { private 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); + /** Obtain proper context for showing splash screen on the provided display. */ private Context getDisplayContext(Context context, int displayId) { if (displayId == DEFAULT_DISPLAY) { @@ -386,25 +395,58 @@ public class StartingSurfaceDrawer { /** * Called when the Task wants to copy the splash screen. - * @param taskId */ public void copySplashScreenView(int taskId) { final StartingWindowRecord preView = mStartingWindowRecords.get(taskId); SplashScreenViewParcelable parcelable; - if (preView != null && preView.mContentView != null - && preView.mContentView.isCopyable()) { - parcelable = new SplashScreenViewParcelable(preView.mContentView); - preView.mContentView.onCopied(); + 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; } if (DEBUG_SPLASH_SCREEN) { Slog.v(TAG, "Copying splash screen window view for task: " + taskId - + " parcelable? " + parcelable); + + " parcelable: " + parcelable); } 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); + if (DEBUG_SPLASH_SCREEN) { + String reason = fromServer ? "Server cleaned up" : "App removed"; + Slog.v(TAG, reason + "the splash screen. Releasing SurfaceControlViewHost for task:" + + taskId); + } + viewHost.getView().post(viewHost::release); + } + protected boolean addWindow(int taskId, IBinder appToken, View view, WindowManager wm, WindowManager.LayoutParams params) { boolean shouldSaveView = true; 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 cffc789106cb..9c1dde925762 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 @@ -150,6 +150,14 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo } /** + * @see StartingSurfaceDrawer#onAppSplashScreenViewRemoved(int) + */ + public void onAppSplashScreenViewRemoved(int taskId) { + mSplashScreenExecutor.execute( + () -> mStartingSurfaceDrawer.onAppSplashScreenViewRemoved(taskId)); + } + + /** * Called when the content of a task is ready to show, starting window can be removed. */ public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 45da45aafaba..8cc6a42be349 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -309,6 +309,7 @@ import android.view.animation.Animation; import android.window.IRemoteTransition; import android.window.SizeConfigurationBuckets; import android.window.SplashScreen; +import android.window.SplashScreenView; import android.window.SplashScreenView.SplashScreenViewParcelable; import android.window.TaskSnapshot; import android.window.WindowContainerToken; @@ -716,25 +717,29 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A boolean startingMoved; boolean mHandleExitSplashScreen; - @TransferSplashScreenState int mTransferringSplashScreenState = - TRANSFER_SPLASH_SCREEN_IDLE; + @TransferSplashScreenState + int mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_IDLE; - // Idle, can be triggered to do transfer if needed. + /** Idle, can be triggered to do transfer if needed. */ static final int TRANSFER_SPLASH_SCREEN_IDLE = 0; - // requesting a copy from shell. + + /** Requesting a copy from shell. */ static final int TRANSFER_SPLASH_SCREEN_COPYING = 1; - // attach the splash screen view to activity. + + /** Attach the splash screen view to activity. */ static final int TRANSFER_SPLASH_SCREEN_ATTACH_TO_CLIENT = 2; - // client has taken over splash screen view. + + /** Client has taken over splash screen view. */ static final int TRANSFER_SPLASH_SCREEN_FINISH = 3; - @IntDef(prefix = { "TRANSFER_SPLASH_SCREEN_" }, value = { + @IntDef(prefix = {"TRANSFER_SPLASH_SCREEN_"}, value = { TRANSFER_SPLASH_SCREEN_IDLE, TRANSFER_SPLASH_SCREEN_COPYING, TRANSFER_SPLASH_SCREEN_ATTACH_TO_CLIENT, TRANSFER_SPLASH_SCREEN_FINISH, }) - @interface TransferSplashScreenState {} + @interface TransferSplashScreenState { + } // How long we wait until giving up transfer splash screen. private static final int TRANSFER_SPLASH_SCREEN_TIMEOUT = 2000; @@ -2196,6 +2201,23 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A removeStartingWindowAnimation(false /* prepareAnimation */); } + /** + * Notify the shell ({@link com.android.wm.shell.ShellTaskOrganizer} it should clean up any + * remaining reference to this {@link ActivityRecord}'s splash screen. + * @see com.android.wm.shell.ShellTaskOrganizer#onAppSplashScreenViewRemoved(int) + * @see SplashScreenView#remove() + */ + void cleanUpSplashScreen() { + // We only clean up the splash screen if we were supposed to handle it. If it was + // transferred to another activity, the next one will handle the clean up. + if (mHandleExitSplashScreen && !startingMoved + && (mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_FINISH + || mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_IDLE)) { + ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Cleaning splash screen token=%s", this); + mAtmService.mTaskOrganizerController.onAppSplashScreenViewRemoved(getTask()); + } + } + void removeStartingWindow() { removeStartingWindowAnimation(true /* prepareAnimation */); } @@ -3343,6 +3365,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A task.cleanUpActivityReferences(this); clearLastParentBeforePip(); + // Clean up the splash screen if it was still displayed. + cleanUpSplashScreen(); + deferRelaunchUntilPaused = false; frozenBeforeDestroy = false; diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index f23028f6f67a..3a2ca80f2e12 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -43,6 +43,7 @@ import android.util.proto.ProtoOutputStream; import android.view.SurfaceControl; import android.window.ITaskOrganizer; import android.window.ITaskOrganizerController; +import android.window.SplashScreenView; import android.window.StartingWindowInfo; import android.window.TaskAppearedInfo; import android.window.TaskSnapshot; @@ -215,6 +216,14 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } } + void onAppSplashScreenViewRemoved(Task task) { + try { + mTaskOrganizer.onAppSplashScreenViewRemoved(task.mTaskId); + } catch (RemoteException e) { + Slog.e(TAG, "Exception sending onAppSplashScreenViewRemoved callback", e); + } + } + SurfaceControl prepareLeash(Task task, String reason) { return new SurfaceControl(task.getSurfaceControl(), reason); } @@ -314,6 +323,10 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { mOrganizer.copySplashScreenView(t); } + public void onAppSplashScreenViewRemoved(Task t) { + mOrganizer.onAppSplashScreenViewRemoved(t); + } + /** * Register this task with this state, but doesn't trigger the task appeared callback to * the organizer. @@ -566,6 +579,22 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return true; } + /** + * Notify the shell ({@link com.android.wm.shell.ShellTaskOrganizer} that the client has + * removed the splash screen view. + * @see com.android.wm.shell.ShellTaskOrganizer#onAppSplashScreenViewRemoved(int) + * @see SplashScreenView#remove() + */ + public void onAppSplashScreenViewRemoved(Task task) { + final Task rootTask = task.getRootTask(); + if (rootTask == null || rootTask.mTaskOrganizer == null) { + return; + } + final TaskOrganizerState state = + mTaskOrganizerStates.get(rootTask.mTaskOrganizer.asBinder()); + state.onAppSplashScreenViewRemoved(task); + } + void onTaskAppeared(ITaskOrganizer organizer, Task task) { final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); if (state != null && state.addTask(task)) { 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 3f1248a5fff7..a1b3159825fb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -796,6 +796,9 @@ public class WindowOrganizerTests extends WindowTestsBase { @Override public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { } + @Override + public void onAppSplashScreenViewRemoved(int taskId) { + } }; private ActivityRecord makePipableActivity() { |