From f08dae31587a342b64057340ea6b27e61c48a7e5 Mon Sep 17 00:00:00 2001 From: Vadim Caen Date: Wed, 9 Jun 2021 19:28:38 +0200 Subject: [PATCH] Implement the legacy splash screen behavior For apps opted out of the new splash screen, we show the full background drawable. Ensure there has send the splash screen background color to StartingWindowListener. Bug: 182880656 Test: manual set useLegacy to true then verify legacy splash screen. Test: atest WmTests:ActivityRecordTests Test: atest StartingSurfaceDrawerTests Change-Id: Icf662f3c5f368f447e718f82f78dc25b909ca9be --- .../java/android/window/SplashScreenView.java | 19 +++- .../android/window/StartingWindowInfo.java | 14 ++- .../SplashscreenContentDrawer.java | 96 +++++++++++++------ .../StartingSurfaceDrawer.java | 68 +++++++++---- .../StartingWindowController.java | 21 ++-- .../PhoneStartingWindowTypeAlgorithm.java | 30 ++++-- .../StartingSurfaceDrawerTests.java | 16 +++- .../com/android/server/wm/ActivityRecord.java | 18 +++- .../server/wm/StartingSurfaceController.java | 6 +- .../server/wm/ActivityRecordTests.java | 38 ++++++++ 10 files changed, 244 insertions(+), 82 deletions(-) diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java index 148986a558e77..1efd2e3812891 100644 --- a/core/java/android/window/SplashScreenView.java +++ b/core/java/android/window/SplashScreenView.java @@ -133,6 +133,8 @@ public final class SplashScreenView extends FrameLayout { private @ColorInt int mIconBackground; private Bitmap mParceledIconBitmap; private Drawable mIconDrawable; + // It is only set for legacy splash screen which won't be sent across processes. + private Drawable mOverlayDrawable; private SurfaceControlViewHost.SurfacePackage mSurfacePackage; private RemoteCallback mClientCallback; private int mBrandingImageWidth; @@ -192,6 +194,14 @@ public final class SplashScreenView extends FrameLayout { return this; } + /** + * Set the Drawable object to fill entire view + */ + public Builder setOverlayDrawable(@Nullable Drawable drawable) { + mOverlayDrawable = drawable; + return this; + } + /** * Set the Drawable object to fill the center view. */ @@ -236,7 +246,11 @@ public final class SplashScreenView extends FrameLayout { layoutInflater.inflate(R.layout.splash_screen_view, null, false); view.mInitBackgroundColor = mBackgroundColor; view.mInitIconBackgroundColor = mIconBackground; - view.setBackgroundColor(mBackgroundColor); + if (mOverlayDrawable != null) { + view.setBackground(mOverlayDrawable); + } else { + view.setBackgroundColor(mBackgroundColor); + } view.mClientCallback = mClientCallback; view.mBrandingImageView = view.findViewById(R.id.splashscreen_branding_view); @@ -261,6 +275,9 @@ public final class SplashScreenView extends FrameLayout { } } } + if (mOverlayDrawable != null || mIconDrawable == null) { + view.setNotCopyable(); + } if (mParceledIconBitmap != null) { view.mParceledIconBitmap = mParceledIconBitmap; diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java index 8bc2177b42e19..566f154fba3b7 100644 --- a/core/java/android/window/StartingWindowInfo.java +++ b/core/java/android/window/StartingWindowInfo.java @@ -55,6 +55,9 @@ public final class StartingWindowInfo implements Parcelable { */ public static final int STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN = 3; + /** @hide **/ + public static final int STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN = 4; + /** * @hide */ @@ -62,7 +65,8 @@ public final class StartingWindowInfo implements Parcelable { STARTING_WINDOW_TYPE_NONE, STARTING_WINDOW_TYPE_SPLASH_SCREEN, STARTING_WINDOW_TYPE_SNAPSHOT, - STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN + STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN, + STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN }) public @interface StartingWindowType {} @@ -103,7 +107,8 @@ public final class StartingWindowInfo implements Parcelable { TYPE_PARAMETER_PROCESS_RUNNING, TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT, TYPE_PARAMETER_ACTIVITY_CREATED, - TYPE_PARAMETER_USE_EMPTY_SPLASH_SCREEN + TYPE_PARAMETER_USE_EMPTY_SPLASH_SCREEN, + TYPE_PARAMETER_LEGACY_SPLASH_SCREEN }) public @interface StartingTypeParams {} @@ -122,6 +127,11 @@ public final class StartingWindowInfo implements Parcelable { public static final int TYPE_PARAMETER_ACTIVITY_CREATED = 0x00000010; /** @hide */ public static final int TYPE_PARAMETER_USE_EMPTY_SPLASH_SCREEN = 0x00000020; + /** + * Application is allowed to use the legacy splash screen + * @hide + */ + public static final int TYPE_PARAMETER_LEGACY_SPLASH_SCREEN = 0x80000000; /** * The parameters which effect the starting window type. 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 b09d0d89a6b9b..75dd561ffc61d 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 @@ -18,6 +18,9 @@ package com.android.wm.shell.startingsurface; import static android.os.Process.THREAD_PRIORITY_TOP_APP_BOOST; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; import android.annotation.ColorInt; import android.annotation.NonNull; @@ -47,6 +50,7 @@ import android.util.Slog; import android.view.SurfaceControl; import android.view.View; import android.window.SplashScreenView; +import android.window.StartingWindowInfo.StartingWindowType; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; @@ -122,17 +126,17 @@ public class SplashscreenContentDrawer { * view on background thread so the view and the drawable can be create and pre-draw in * parallel. * - * @param emptyView Create a splash screen view without icon on it. + * @param suggestType Suggest type to create the splash screen view. * @param consumer Receiving the SplashScreenView object, which will also be executed * on splash screen thread. Note that the view can be null if failed. */ - void createContentView(Context context, boolean emptyView, ActivityInfo info, int taskId, - Consumer consumer) { + void createContentView(Context context, @StartingWindowType int suggestType, ActivityInfo info, + int taskId, Consumer consumer) { mSplashscreenWorkerHandler.post(() -> { SplashScreenView contentView; try { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "makeSplashScreenContentView"); - contentView = makeSplashScreenContentView(context, info, emptyView); + contentView = makeSplashScreenContentView(context, info, suggestType); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } catch (RuntimeException e) { Slog.w(TAG, "failed creating starting window content at taskId: " @@ -199,22 +203,45 @@ public class SplashscreenContentDrawer { } } + private static Drawable peekLegacySplashscreenContent(Context context, + SplashScreenWindowAttrs attrs) { + final TypedArray a = context.obtainStyledAttributes(R.styleable.Window); + final int resId = safeReturnAttrDefault((def) -> + a.getResourceId(R.styleable.Window_windowSplashscreenContent, def), 0); + a.recycle(); + if (resId != 0) { + return context.getDrawable(resId); + } + if (attrs.mWindowBgResId != 0) { + return context.getDrawable(attrs.mWindowBgResId); + } + return null; + } + private SplashScreenView makeSplashScreenContentView(Context context, ActivityInfo ai, - boolean emptyView) { + @StartingWindowType int suggestType) { updateDensity(); getWindowAttrs(context, mTmpAttrs); mLastPackageContextConfigHash = context.getResources().getConfiguration().hashCode(); - final int themeBGColor = mColorCache.getWindowColor(ai.packageName, - mLastPackageContextConfigHash, mTmpAttrs.mWindowBgColor, mTmpAttrs.mWindowBgResId, - () -> peekWindowBGColor(context, mTmpAttrs)).mBgColor; - // TODO (b/173975965) Tracking the performance on improved splash screen. + + final Drawable legacyDrawable = suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN + ? peekLegacySplashscreenContent(context, mTmpAttrs) : null; + final int themeBGColor = legacyDrawable != null + ? getBGColorFromCache(ai, () -> estimateWindowBGColor(legacyDrawable)) + : getBGColorFromCache(ai, () -> peekWindowBGColor(context, mTmpAttrs)); return new StartingWindowViewBuilder(context, ai) .setWindowBGColor(themeBGColor) - .makeEmptyView(emptyView) + .overlayDrawable(legacyDrawable) + .chooseStyle(suggestType) .build(); } + private int getBGColorFromCache(ActivityInfo ai, IntSupplier windowBgColorSupplier) { + return mColorCache.getWindowColor(ai.packageName, mLastPackageContextConfigHash, + mTmpAttrs.mWindowBgColor, mTmpAttrs.mWindowBgResId, windowBgColorSupplier).mBgColor; + } + private static T safeReturnAttrDefault(UnaryOperator getMethod, T def) { try { return getMethod.apply(def); @@ -267,7 +294,8 @@ public class SplashscreenContentDrawer { private final Context mContext; private final ActivityInfo mActivityInfo; - private boolean mEmptyView; + private Drawable mOverlayDrawable; + private int mSuggestType; private int mThemeColor; private Drawable mFinalIconDrawable; private int mFinalIconSize = mIconSize; @@ -282,16 +310,22 @@ public class SplashscreenContentDrawer { return this; } - StartingWindowViewBuilder makeEmptyView(boolean empty) { - mEmptyView = empty; + StartingWindowViewBuilder overlayDrawable(Drawable overlay) { + mOverlayDrawable = overlay; + return this; + } + + StartingWindowViewBuilder chooseStyle(int suggestType) { + mSuggestType = suggestType; return this; } SplashScreenView build() { Drawable iconDrawable; final int animationDuration; - if (mEmptyView) { - // empty splash screen case + if (mSuggestType == STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN + || mSuggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) { + // empty or legacy splash screen case animationDuration = 0; mFinalIconSize = 0; } else if (mTmpAttrs.mSplashScreenIcon != null) { @@ -403,13 +437,15 @@ public class SplashscreenContentDrawer { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "fillViewWithIcon"); final SplashScreenView.Builder builder = new SplashScreenView.Builder(mContext); builder.setBackgroundColor(mThemeColor); + builder.setOverlayDrawable(mOverlayDrawable); if (iconDrawable != null) { builder.setIconSize(iconSize) .setIconBackground(mTmpAttrs.mIconBgColor) .setCenterViewDrawable(iconDrawable) .setAnimationDurationMillis(animationDuration); } - if (mTmpAttrs.mBrandingImage != null) { + if (mSuggestType == STARTING_WINDOW_TYPE_SPLASH_SCREEN + && mTmpAttrs.mBrandingImage != null) { builder.setBrandingDrawable(mTmpAttrs.mBrandingImage, mBrandingImageWidth, mBrandingImageHeight); } @@ -417,20 +453,22 @@ public class SplashscreenContentDrawer { if (DEBUG) { Slog.d(TAG, "fillViewWithIcon surfaceWindowView " + splashScreenView); } - if (mEmptyView) { - splashScreenView.setNotCopyable(); - } - splashScreenView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { - @Override - public void onViewAttachedToWindow(View v) { - SplashScreenView.applySystemBarsContrastColor(v.getWindowInsetsController(), - splashScreenView.getInitBackgroundColor()); - } + if (mSuggestType != STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) { + splashScreenView.addOnAttachStateChangeListener( + new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + SplashScreenView.applySystemBarsContrastColor( + v.getWindowInsetsController(), + splashScreenView.getInitBackgroundColor()); + } + + @Override + public void onViewDetachedFromWindow(View v) { + } + }); + } - @Override - public void onViewDetachedFromWindow(View v) { - } - }); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); return splashScreenView; } 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 670af963230a1..3cbce9c38f125 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 @@ -20,6 +20,8 @@ import static android.content.Context.CONTEXT_RESTRICTED; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Choreographer.CALLBACK_INSETS_ANIMATION; import static android.view.Display.DEFAULT_DISPLAY; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT; import android.annotation.Nullable; import android.app.ActivityManager.RunningTaskInfo; @@ -32,7 +34,6 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.PixelFormat; import android.graphics.Rect; -import android.graphics.drawable.ColorDrawable; import android.hardware.display.DisplayManager; import android.os.IBinder; import android.os.RemoteCallback; @@ -50,6 +51,7 @@ 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.TaskSnapshot; import com.android.internal.R; @@ -149,10 +151,11 @@ public class StartingSurfaceDrawer { /** * Called when a task need a splash screen starting window. - * @param emptyView Whether drawing an empty frame without anything on it. + * + * @param suggestType The suggestion type to draw the splash screen. */ void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, IBinder appToken, - boolean emptyView) { + @StartingWindowType int suggestType) { final RunningTaskInfo taskInfo = windowInfo.taskInfo; final ActivityInfo activityInfo = taskInfo.topActivityInfo; if (activityInfo == null) { @@ -173,7 +176,8 @@ public class StartingSurfaceDrawer { : com.android.internal.R.style.Theme_DeviceDefault_DayNight; if (DEBUG_SPLASH_SCREEN) { Slog.d(TAG, "addSplashScreen " + activityInfo.packageName - + " theme=" + Integer.toHexString(theme) + " task= " + taskInfo.taskId); + + " theme=" + Integer.toHexString(theme) + " task=" + taskInfo.taskId + + " suggestType=" + suggestType); } // Obtain proper context to launch on the right display. @@ -231,13 +235,19 @@ public class StartingSurfaceDrawer { params.setFitInsetsTypes(0); params.format = PixelFormat.TRANSLUCENT; int windowFlags = WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED - | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; final TypedArray a = context.obtainStyledAttributes(R.styleable.Window); if (a.getBoolean(R.styleable.Window_windowShowWallpaper, false)) { windowFlags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; } + if (suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) { + if (a.getBoolean(R.styleable.Window_windowDrawsSystemBarBackgrounds, false)) { + windowFlags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + } + } else { + windowFlags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + } params.layoutInDisplayCutoutMode = a.getInt( R.styleable.Window_windowLayoutInDisplayCutoutMode, params.layoutInDisplayCutoutMode); @@ -311,12 +321,12 @@ public class StartingSurfaceDrawer { } Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); }; - mSplashscreenContentDrawer.createContentView(context, emptyView, activityInfo, taskId, + mSplashscreenContentDrawer.createContentView(context, suggestType, activityInfo, taskId, viewSupplier::setView); try { final WindowManager wm = context.getSystemService(WindowManager.class); - if (addWindow(taskId, appToken, rootLayout, wm, params)) { + if (addWindow(taskId, appToken, rootLayout, wm, 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, @@ -336,8 +346,10 @@ public class StartingSurfaceDrawer { int getStartingWindowBackgroundColorForTask(int taskId) { StartingWindowRecord startingWindowRecord = mStartingWindowRecords.get(taskId); - if (startingWindowRecord == null || startingWindowRecord.mContentView == null) return 0; - return ((ColorDrawable) startingWindowRecord.mContentView.getBackground()).getColor(); + if (startingWindowRecord == null || startingWindowRecord.mContentView == null) { + return 0; + } + return startingWindowRecord.mContentView.getInitBackgroundColor(); } private static class SplashScreenViewSupplier implements Supplier { @@ -379,7 +391,7 @@ public class StartingSurfaceDrawer { return; } final StartingWindowRecord tView = new StartingWindowRecord(appToken, - null/* decorView */, surface); + null/* decorView */, surface, STARTING_WINDOW_TYPE_SNAPSHOT); mStartingWindowRecords.put(taskId, tView); } @@ -449,7 +461,7 @@ public class StartingSurfaceDrawer { } protected boolean addWindow(int taskId, IBinder appToken, View view, WindowManager wm, - WindowManager.LayoutParams params) { + WindowManager.LayoutParams params, @StartingWindowType int suggestType) { boolean shouldSaveView = true; try { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addRootView"); @@ -469,14 +481,15 @@ public class StartingSurfaceDrawer { } if (shouldSaveView) { removeWindowNoAnimate(taskId); - saveSplashScreenRecord(appToken, taskId, view); + saveSplashScreenRecord(appToken, taskId, view, suggestType); } return shouldSaveView; } - private void saveSplashScreenRecord(IBinder appToken, int taskId, View view) { + private void saveSplashScreenRecord(IBinder appToken, int taskId, View view, + @StartingWindowType int suggestType) { final StartingWindowRecord tView = new StartingWindowRecord(appToken, view, - null/* TaskSnapshotWindow */); + null/* TaskSnapshotWindow */, suggestType); mStartingWindowRecords.put(taskId, tView); } @@ -493,14 +506,18 @@ public class StartingSurfaceDrawer { Slog.v(TAG, "Removing splash screen window for task: " + taskId); } if (record.mContentView != null) { - if (playRevealAnimation) { - mSplashscreenContentDrawer.applyExitAnimation(record.mContentView, - leash, frame, - () -> removeWindowInner(record.mDecorView, true)); + if (record.mSuggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) { + removeWindowInner(record.mDecorView, false); } else { - // the SplashScreenView has been copied to client, hide the view to skip - // default exit animation - removeWindowInner(record.mDecorView, true); + if (playRevealAnimation) { + mSplashscreenContentDrawer.applyExitAnimation(record.mContentView, + leash, frame, + () -> removeWindowInner(record.mDecorView, true)); + } else { + // the SplashScreenView has been copied to client, hide the view to skip + // default exit animation + removeWindowInner(record.mDecorView, true); + } } } else { // shouldn't happen @@ -537,6 +554,7 @@ public class StartingSurfaceDrawer { private final TaskSnapshotWindow mTaskSnapshotWindow; private SplashScreenView mContentView; private boolean mSetSplashScreen; + private @StartingWindowType int mSuggestType; StartingWindowRecord(IBinder appToken, View decorView, TaskSnapshotWindow taskSnapshotWindow) { @@ -545,6 +563,14 @@ public class StartingSurfaceDrawer { mTaskSnapshotWindow = taskSnapshotWindow; } + StartingWindowRecord(IBinder appToken, View decorView, + TaskSnapshotWindow taskSnapshotWindow, @StartingWindowType int suggestType) { + mAppToken = appToken; + mDecorView = decorView; + mTaskSnapshotWindow = taskSnapshotWindow; + mSuggestType = suggestType; + } + private void setSplashScreenView(SplashScreenView splashScreenView) { if (mSetSplashScreen) { return; 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 9c1dde925762a..eaa89d8e3ab56 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 @@ -17,6 +17,7 @@ package com.android.wm.shell.startingsurface; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; @@ -31,6 +32,7 @@ import android.os.Trace; import android.util.Slog; import android.view.SurfaceControl; import android.window.StartingWindowInfo; +import android.window.StartingWindowInfo.StartingWindowType; import android.window.TaskOrganizer; import android.window.TaskSnapshot; @@ -106,10 +108,6 @@ public class StartingWindowController implements RemoteCallable { mStartingSurfaceDrawer.copySplashScreenView(taskId); 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 5a134b8067451..848eff4b56f36 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 @@ -18,11 +18,13 @@ package com.android.wm.shell.startingsurface.phone; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED; import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT; +import static android.window.StartingWindowInfo.TYPE_PARAMETER_LEGACY_SPLASH_SCREEN; import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK; import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING; import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH; @@ -54,30 +56,38 @@ public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgor final boolean activityCreated = (parameter & TYPE_PARAMETER_ACTIVITY_CREATED) != 0; final boolean useEmptySplashScreen = (parameter & TYPE_PARAMETER_USE_EMPTY_SPLASH_SCREEN) != 0; + final boolean legacySplashScreen = + ((parameter & TYPE_PARAMETER_LEGACY_SPLASH_SCREEN) != 0); final boolean topIsHome = windowInfo.taskInfo.topActivityType == ACTIVITY_TYPE_HOME; if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) { - Slog.d(TAG, "preferredStartingWindowType newTask " + newTask - + " taskSwitch " + taskSwitch - + " processRunning " + processRunning - + " allowTaskSnapshot " + allowTaskSnapshot - + " activityCreated " + activityCreated - + " useEmptySplashScreen " + useEmptySplashScreen - + " topIsHome " + topIsHome); + Slog.d(TAG, "preferredStartingWindowType newTask:" + newTask + + " taskSwitch:" + taskSwitch + + " processRunning:" + processRunning + + " allowTaskSnapshot:" + allowTaskSnapshot + + " activityCreated:" + activityCreated + + " useEmptySplashScreen:" + useEmptySplashScreen + + " legacySplashScreen:" + legacySplashScreen + + " topIsHome:" + topIsHome); } + + final int visibleSplashScreenType = legacySplashScreen + ? STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN + : STARTING_WINDOW_TYPE_SPLASH_SCREEN; + if (!topIsHome) { if (!processRunning) { return useEmptySplashScreen ? STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN - : STARTING_WINDOW_TYPE_SPLASH_SCREEN; + : visibleSplashScreenType; } if (newTask) { return useEmptySplashScreen ? STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN - : STARTING_WINDOW_TYPE_SPLASH_SCREEN; + : visibleSplashScreenType; } if (taskSwitch && !activityCreated) { - return STARTING_WINDOW_TYPE_SPLASH_SCREEN; + return visibleSplashScreenType; } } if (taskSwitch && allowTaskSnapshot) { 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 903e63ad65541..5061b2369bb21 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 @@ -15,6 +15,8 @@ */ package com.android.wm.shell.startingsurface; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; + import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; @@ -91,7 +93,7 @@ public class StartingSurfaceDrawerTests { @Override protected boolean addWindow(int taskId, IBinder appToken, - View view, WindowManager wm, WindowManager.LayoutParams params) { + View view, WindowManager wm, WindowManager.LayoutParams params, int suggestType) { // listen for addView mAddWindowForTask = taskId; mViewThemeResId = view.getContext().getThemeResId(); @@ -145,9 +147,11 @@ public class StartingSurfaceDrawerTests { final int taskId = 1; final StartingWindowInfo windowInfo = createWindowInfo(taskId, android.R.style.Theme); - mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder, false); + mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder, + STARTING_WINDOW_TYPE_SPLASH_SCREEN); waitHandlerIdle(mTestHandler); - verify(mStartingSurfaceDrawer).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); mStartingSurfaceDrawer.removeStartingWindow(windowInfo.taskInfo.taskId, null, null, false); @@ -161,9 +165,11 @@ public class StartingSurfaceDrawerTests { final int taskId = 1; final StartingWindowInfo windowInfo = createWindowInfo(taskId, 0); - mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder, false); + mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder, + STARTING_WINDOW_TYPE_SPLASH_SCREEN); waitHandlerIdle(mTestHandler); - verify(mStartingSurfaceDrawer).addWindow(eq(taskId), eq(mBinder), any(), any(), any()); + verify(mStartingSurfaceDrawer).addWindow(eq(taskId), eq(mBinder), any(), any(), any(), + eq(STARTING_WINDOW_TYPE_SPLASH_SCREEN)); assertNotEquals(mStartingSurfaceDrawer.mViewThemeResId, 0); } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index dd545290c3774..704c9d2e32b5d 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -257,6 +257,7 @@ import android.content.Intent; import android.content.LocusId; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.content.res.Resources; @@ -1929,12 +1930,23 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final TaskSnapshot snapshot = mWmService.mTaskSnapshotController.getSnapshot(task.mTaskId, task.mUserId, false /* restoreFromDisk */, false /* isLowResolution */); - final int typeParameter = mWmService.mStartingSurfaceController - .makeStartingWindowTypeParameter(newTask, taskSwitch, processRunning, - allowTaskSnapshot, activityCreated, useEmpty); final int type = getStartingWindowType(newTask, taskSwitch, processRunning, allowTaskSnapshot, activityCreated, snapshot); + //TODO(191787740) Remove for T + final boolean useLegacy = type == STARTING_WINDOW_TYPE_SPLASH_SCREEN + && mWmService.mStartingSurfaceController.isExceptionApp(packageName, mTargetSdk, + () -> { + ActivityInfo activityInfo = intent.resolveActivityInfo( + mAtmService.mContext.getPackageManager(), + PackageManager.GET_META_DATA); + return activityInfo != null ? activityInfo.applicationInfo : null; + }); + + final int typeParameter = mWmService.mStartingSurfaceController + .makeStartingWindowTypeParameter(newTask, taskSwitch, processRunning, + allowTaskSnapshot, activityCreated, useEmpty, useLegacy); + if (type == STARTING_WINDOW_TYPE_SNAPSHOT) { if (isActivityTypeHome()) { // The snapshot of home is only used once because it won't be updated while screen diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java index da257477b1d85..45b53a1a99b91 100644 --- a/services/core/java/com/android/server/wm/StartingSurfaceController.java +++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java @@ -18,6 +18,7 @@ package com.android.server.wm; import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED; import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT; +import static android.window.StartingWindowInfo.TYPE_PARAMETER_LEGACY_SPLASH_SCREEN; import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK; import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING; import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH; @@ -85,7 +86,7 @@ public class StartingSurfaceController { int makeStartingWindowTypeParameter(boolean newTask, boolean taskSwitch, boolean processRunning, boolean allowTaskSnapshot, boolean activityCreated, - boolean useEmpty) { + boolean useEmpty, boolean useLegacy) { int parameter = 0; if (newTask) { parameter |= TYPE_PARAMETER_NEW_TASK; @@ -105,6 +106,9 @@ public class StartingSurfaceController { if (useEmpty) { parameter |= TYPE_PARAMETER_USE_EMPTY_SPLASH_SCREEN; } + if (useLegacy) { + parameter |= TYPE_PARAMETER_LEGACY_SPLASH_SCREEN; + } return parameter; } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index b7713a9338de8..a1f1610d95db9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -46,6 +46,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; +import static android.window.StartingWindowInfo.TYPE_PARAMETER_LEGACY_SPLASH_SCREEN; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; @@ -111,11 +112,13 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Point; import android.graphics.Rect; +import android.os.Build; import android.os.Bundle; import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; +import android.provider.DeviceConfig; import android.util.MergedConfiguration; import android.util.MutableBoolean; import android.view.DisplayInfo; @@ -135,6 +138,7 @@ import androidx.test.filters.MediumTest; import com.android.internal.R; import com.android.server.wm.Task.ActivityState; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -2457,6 +2461,40 @@ public class ActivityRecordTests extends WindowTestsBase { assertNoStartingWindow(activity); } + private void testLegacySplashScreen(int targetSdk, int verifyType) { + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); + activity.mTargetSdk = targetSdk; + activity.addStartingWindow(mPackageName, + android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, + false, false); + waitUntilHandlersIdle(); + assertHasStartingWindow(activity); + assertEquals(activity.mStartingData.mTypeParams & TYPE_PARAMETER_LEGACY_SPLASH_SCREEN, + verifyType); + activity.removeStartingWindow(); + waitUntilHandlersIdle(); + assertNoStartingWindow(activity); + } + + @Test + public void testCreateRemoveLegacySplashScreenWindow() { + registerTestStartingWindowOrganizer(); + DeviceConfig.Properties properties = DeviceConfig.getProperties( + DeviceConfig.NAMESPACE_WINDOW_MANAGER); + try { + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER, + "splash_screen_exception_list", DEFAULT_COMPONENT_PACKAGE_NAME, false); + testLegacySplashScreen(Build.VERSION_CODES.R, TYPE_PARAMETER_LEGACY_SPLASH_SCREEN); + testLegacySplashScreen(Build.VERSION_CODES.S, 0); + } finally { + try { + DeviceConfig.setProperties(properties); + } catch (DeviceConfig.BadConfigException e) { + Assert.fail(e.getMessage()); + } + } + } + @Test public void testTransferStartingWindow() { registerTestStartingWindowOrganizer();