summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Wei Sheng Shih <wilsonshih@google.com> 2021-04-19 01:33:24 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2021-04-19 01:33:24 +0000
commit9dea87c0d168d655e939ba524b7233c686b2cfdf (patch)
treec521ccd9f4ffb9890bdc9255c2a97e7f3da0f49d
parent4a8009373d44541ab2be49173468bfcd137bc19e (diff)
parent6298f2c0461cee74cc01b85d406ea5a9a7374257 (diff)
Merge "Starting window performance tuning for binder block." into sc-dev
-rw-r--r--core/java/android/window/StartingWindowInfo.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java60
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java54
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java144
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java37
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java35
-rw-r--r--services/core/java/com/android/server/wm/StartingSurfaceController.java4
-rw-r--r--services/core/java/com/android/server/wm/TaskOrganizerController.java64
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java15
12 files changed, 296 insertions, 144 deletions
diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java
index 08bb1a957e05..385d6cffa138 100644
--- a/core/java/android/window/StartingWindowInfo.java
+++ b/core/java/android/window/StartingWindowInfo.java
@@ -142,6 +142,12 @@ public final class StartingWindowInfo implements Parcelable {
*/
public boolean isKeyguardOccluded = false;
+ /**
+ * TaskSnapshot.
+ * @hide
+ */
+ public TaskSnapshot mTaskSnapshot;
+
public StartingWindowInfo() {
}
@@ -164,6 +170,7 @@ public final class StartingWindowInfo implements Parcelable {
dest.writeTypedObject(mainWindowLayoutParams, flags);
dest.writeInt(splashScreenThemeResId);
dest.writeBoolean(isKeyguardOccluded);
+ dest.writeTypedObject(mTaskSnapshot, flags);
}
void readFromParcel(@NonNull Parcel source) {
@@ -175,6 +182,7 @@ public final class StartingWindowInfo implements Parcelable {
mainWindowLayoutParams = source.readTypedObject(WindowManager.LayoutParams.CREATOR);
splashScreenThemeResId = source.readInt();
isKeyguardOccluded = source.readBoolean();
+ mTaskSnapshot = source.readTypedObject(TaskSnapshot.CREATOR);
}
@Override
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 516dfd0758ee..147f2e2ec846 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -16,6 +16,7 @@
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.os.UserHandle.getUserHandleForUid;
@@ -35,6 +36,8 @@ import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Trace;
import android.util.Slog;
import android.view.SurfaceControl;
@@ -48,10 +51,11 @@ import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.common.TransactionPool;
import java.util.List;
+import java.util.function.Consumer;
/**
* Util class to create the view for a splash screen content.
- *
+ * Everything execute in this class should be post to mSplashscreenWorkerHandler.
* @hide
*/
public class SplashscreenContentDrawer {
@@ -78,6 +82,7 @@ public class SplashscreenContentDrawer {
private int mIconEarlyExitDistance;
private final TransactionPool mTransactionPool;
private final SplashScreenWindowAttrs mTmpAttrs = new SplashScreenWindowAttrs();
+ private final Handler mSplashscreenWorkerHandler;
SplashscreenContentDrawer(Context context, int maxAnimatableIconDuration,
int iconExitAnimDuration, int appRevealAnimDuration, TransactionPool pool) {
@@ -87,6 +92,45 @@ public class SplashscreenContentDrawer {
mAppRevealDuration = appRevealAnimDuration;
mIconExitDuration = iconExitAnimDuration;
mTransactionPool = pool;
+
+ // Initialize Splashscreen worker thread
+ // TODO(b/185288910) move it into WMShellConcurrencyModule and provide an executor to make
+ // it easier to test stuff that happens on that thread later.
+ final HandlerThread shellSplashscreenWorkerThread =
+ new HandlerThread("wmshell.splashworker", THREAD_PRIORITY_TOP_APP_BOOST);
+ shellSplashscreenWorkerThread.start();
+ mSplashscreenWorkerHandler = shellSplashscreenWorkerThread.getThreadHandler();
+ }
+
+ /**
+ * Create a SplashScreenView object.
+ *
+ * In order to speed up the splash screen view to show on first frame, preparing the
+ * view on background thread so the view and the drawable can be create and pre-draw in
+ * parallel.
+ *
+ * @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, int splashScreenResId, ActivityInfo info,
+ int taskId, Consumer<SplashScreenView> consumer) {
+ mSplashscreenWorkerHandler.post(() -> {
+ SplashScreenView contentView;
+ try {
+ contentView = SplashscreenContentDrawer.makeSplashscreenContent(
+ context, splashScreenResId);
+ if (contentView == null) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "makeSplashScreenContentView");
+ contentView = makeSplashScreenContentView(context, info);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "failed creating starting window content at taskId: "
+ + taskId, e);
+ contentView = null;
+ }
+ consumer.accept(contentView);
+ });
}
private void updateDensity() {
@@ -146,7 +190,7 @@ public class SplashscreenContentDrawer {
}
}
- SplashScreenView makeSplashScreenContentView(Context context, ActivityInfo ai) {
+ private SplashScreenView makeSplashScreenContentView(Context context, ActivityInfo ai) {
updateDensity();
getWindowAttrs(context, mTmpAttrs);
@@ -199,7 +243,7 @@ public class SplashscreenContentDrawer {
}
}
- static class SplashScreenWindowAttrs {
+ private static class SplashScreenWindowAttrs {
private int mWindowBgResId = 0;
private int mWindowBgColor = Color.TRANSPARENT;
private Drawable mReplaceIcon = null;
@@ -271,9 +315,7 @@ public class SplashscreenContentDrawer {
if (DEBUG) {
Slog.d(TAG, "The icon is not an AdaptiveIconDrawable");
}
- mFinalIconDrawable = SplashscreenIconDrawableFactory.makeIconDrawable(
- mIconBackground != Color.TRANSPARENT
- ? mIconBackground : mThemeColor, mIconDrawable, mIconSize);
+ createIconDrawable(mIconDrawable, mIconSize);
}
final int iconSize = mFinalIconDrawable != null ? (int) (mIconSize * mScale) : 0;
mCachedResult = fillViewWithIcon(mContext, iconSize, mFinalIconDrawable);
@@ -283,8 +325,8 @@ public class SplashscreenContentDrawer {
private void createIconDrawable(Drawable iconDrawable, int iconSize) {
mFinalIconDrawable = SplashscreenIconDrawableFactory.makeIconDrawable(
- mIconBackground != Color.TRANSPARENT
- ? mIconBackground : mThemeColor, iconDrawable, iconSize);
+ mIconBackground != Color.TRANSPARENT ? mIconBackground : mThemeColor,
+ iconDrawable, iconSize, mSplashscreenWorkerHandler);
}
private boolean processAdaptiveIcon() {
@@ -399,7 +441,7 @@ public class SplashscreenContentDrawer {
return root < 0.1;
}
- static SplashScreenView makeSplashscreenContent(Context ctx,
+ private static SplashScreenView makeSplashscreenContent(Context ctx,
int splashscreenContentResId) {
// doesn't support windowSplashscreenContent after S
// TODO add an allowlist to skip some packages if needed
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
index 4196d68f5800..85845d0d9c89 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java
@@ -37,6 +37,7 @@ import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Handler;
import android.os.Trace;
import android.util.PathParser;
import android.window.SplashScreenView;
@@ -50,42 +51,61 @@ import com.android.internal.R;
public class SplashscreenIconDrawableFactory {
static Drawable makeIconDrawable(@ColorInt int backgroundColor,
- @NonNull Drawable foregroundDrawable, int iconSize) {
+ @NonNull Drawable foregroundDrawable, int iconSize,
+ Handler splashscreenWorkerHandler) {
if (foregroundDrawable instanceof Animatable) {
return new AnimatableIconDrawable(backgroundColor, foregroundDrawable);
} else if (foregroundDrawable instanceof AdaptiveIconDrawable) {
- return new ImmobileIconDrawable((AdaptiveIconDrawable) foregroundDrawable, iconSize);
+ return new ImmobileIconDrawable((AdaptiveIconDrawable) foregroundDrawable, iconSize,
+ splashscreenWorkerHandler);
} else {
return new ImmobileIconDrawable(new AdaptiveIconDrawable(
- new ColorDrawable(backgroundColor), foregroundDrawable), iconSize);
+ new ColorDrawable(backgroundColor), foregroundDrawable), iconSize,
+ splashscreenWorkerHandler);
}
}
private static class ImmobileIconDrawable extends Drawable {
- private Shader mLayersShader;
+ private boolean mCacheComplete;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG
| Paint.FILTER_BITMAP_FLAG);
- ImmobileIconDrawable(AdaptiveIconDrawable drawable, int iconSize) {
- cachePaint(drawable, iconSize, iconSize);
+ ImmobileIconDrawable(AdaptiveIconDrawable drawable, int iconSize,
+ Handler splashscreenWorkerHandler) {
+ splashscreenWorkerHandler.post(() -> cachePaint(drawable, iconSize, iconSize));
}
private void cachePaint(AdaptiveIconDrawable drawable, int width, int height) {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "cachePaint");
- final Bitmap layersBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- final Canvas canvas = new Canvas(layersBitmap);
- drawable.setBounds(0, 0, width, height);
- drawable.draw(canvas);
- mLayersShader = new BitmapShader(layersBitmap, Shader.TileMode.CLAMP,
- Shader.TileMode.CLAMP);
- mPaint.setShader(mLayersShader);
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ synchronized (mPaint) {
+ if (mCacheComplete) {
+ return;
+ }
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "cachePaint");
+ final Bitmap layersBitmap = Bitmap.createBitmap(width, height,
+ Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(layersBitmap);
+ drawable.setBounds(0, 0, width, height);
+ drawable.draw(canvas);
+ final Shader layersShader = new BitmapShader(layersBitmap, Shader.TileMode.CLAMP,
+ Shader.TileMode.CLAMP);
+ mPaint.setShader(layersShader);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ mCacheComplete = true;
+ }
}
@Override
public void draw(Canvas canvas) {
- final Rect bounds = getBounds();
- canvas.drawRect(bounds, mPaint);
+ synchronized (mPaint) {
+ if (mCacheComplete) {
+ final Rect bounds = getBounds();
+ canvas.drawRect(bounds, mPaint);
+ } else {
+ // this shouldn't happen, but if it really happen, invalidate self to wait
+ // for cachePaint finish.
+ invalidateSelf();
+ }
+ }
}
@Override
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 e95135a1a59f..7037d18decbe 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
@@ -18,8 +18,10 @@ package com.android.wm.shell.startingsurface;
import static android.content.Context.CONTEXT_RESTRICTED;
import static android.content.res.Configuration.EMPTY;
+import static android.view.Choreographer.CALLBACK_INSETS_ANIMATION;
import static android.view.Display.DEFAULT_DISPLAY;
+import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
import android.content.Context;
@@ -29,16 +31,15 @@ import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
import android.hardware.display.DisplayManager;
import android.os.IBinder;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Slog;
import android.util.SparseArray;
+import android.view.Choreographer;
import android.view.Display;
import android.view.SurfaceControl;
import android.view.View;
@@ -54,9 +55,44 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
import java.util.function.Consumer;
+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).
+ *
* @hide
*/
public class StartingSurfaceDrawer {
@@ -68,7 +104,11 @@ public class StartingSurfaceDrawer {
private final DisplayManager mDisplayManager;
private final ShellExecutor mSplashScreenExecutor;
private final SplashscreenContentDrawer mSplashscreenContentDrawer;
+ private Choreographer mChoreographer;
+ /**
+ * @param splashScreenExecutor The thread used to control add and remove starting window.
+ */
public StartingSurfaceDrawer(Context context, ShellExecutor splashScreenExecutor,
TransactionPool pool) {
mContext = context;
@@ -82,6 +122,7 @@ public class StartingSurfaceDrawer {
com.android.wm.shell.R.integer.starting_window_app_reveal_anim_duration);
mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext,
maxAnimatableIconDuration, iconExitAnimDuration, appRevealAnimDuration, pool);
+ mSplashScreenExecutor.execute(() -> mChoreographer = Choreographer.getInstance());
}
private final SparseArray<StartingWindowRecord> mStartingWindowRecords = new SparseArray<>();
@@ -267,36 +308,91 @@ public class StartingSurfaceDrawer {
params.setTitle("Splash Screen " + activityInfo.packageName);
// TODO(b/173975965) tracking performance
- SplashScreenView sView = null;
+ // 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.
+ final Runnable setViewSynchronized;
+ if (!emptyView) {
+ // Record whether create splash screen view success, notify to current thread after
+ // create splash screen view finished.
+ final SplashScreenViewSupplier viewSupplier = new SplashScreenViewSupplier();
+ setViewSynchronized = () -> {
+ // 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.
+ if (record != null) {
+ // if view == null then creation of content view was failed.
+ if (contentView != null) {
+ try {
+ win.setContentView(contentView);
+ contentView.cacheRootWindow(win);
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "failed set content view to starting window "
+ + "at taskId: " + taskId, e);
+ contentView = null;
+ }
+ }
+ record.setSplashScreenView(contentView);
+ }
+ };
+ mSplashscreenContentDrawer.createContentView(context,
+ splashscreenContentResId[0], activityInfo, taskId, viewSupplier::setView);
+ } else {
+ setViewSynchronized = null;
+ }
+
try {
final View view = win.getDecorView();
final WindowManager wm = mContext.getSystemService(WindowManager.class);
- if (!emptyView) {
- // splash screen content will be deprecated after S.
- sView = SplashscreenContentDrawer.makeSplashscreenContent(
- context, splashscreenContentResId[0]);
- final boolean splashscreenContentCompatible = sView != null;
- if (splashscreenContentCompatible) {
- win.setContentView(sView);
- } else {
- sView = mSplashscreenContentDrawer
- .makeSplashScreenContentView(context, activityInfo);
- win.setContentView(sView);
- win.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
- sView.cacheRootWindow(win);
- }
- }
postAddWindow(taskId, appToken, view, wm, params);
+
+ // all done
+ if (emptyView) {
+ return;
+ }
+ // 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);
} 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);
- sView = null;
- } finally {
- final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
- if (record != null) {
- record.setSplashScreenView(sView);
+ Slog.w(TAG, "failed creating starting window at taskId: " + taskId, e);
+ }
+ }
+
+ private static class SplashScreenViewSupplier implements Supplier<SplashScreenView> {
+ private SplashScreenView mView;
+ private boolean mIsViewSet;
+ void setView(SplashScreenView view) {
+ synchronized (this) {
+ mView = view;
+ mIsViewSet = true;
+ notify();
+ }
+ }
+
+ @Override
+ public @Nullable SplashScreenView get() {
+ synchronized (this) {
+ while (!mIsViewSet) {
+ try {
+ wait();
+ } catch (InterruptedException ignored) {
+ }
+ }
+ return mView;
}
}
}
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 8a629bcf4f3b..e3362870cdf0 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
@@ -16,6 +16,7 @@
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.window.StartingWindowInfo.STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT;
@@ -30,11 +31,11 @@ import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH;
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import android.app.ActivityManager.RunningTaskInfo;
-import android.app.ActivityTaskManager;
import android.content.Context;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.Trace;
import android.util.Slog;
import android.view.SurfaceControl;
import android.window.StartingWindowInfo;
@@ -109,17 +110,9 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo
}
private static class StartingTypeChecker {
- TaskSnapshot mSnapshot;
-
- StartingTypeChecker() { }
-
- private void reset() {
- mSnapshot = null;
- }
private @StartingWindowInfo.StartingWindowType int
estimateStartingWindowType(StartingWindowInfo windowInfo) {
- reset();
final int parameter = windowInfo.startingWindowTypeParameter;
final boolean newTask = (parameter & TYPE_PARAMETER_NEW_TASK) != 0;
final boolean taskSwitch = (parameter & TYPE_PARAMETER_TASK_SWITCH) != 0;
@@ -159,7 +152,7 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo
}
}
if (taskSwitch && allowTaskSnapshot) {
- final TaskSnapshot snapshot = getTaskSnapshot(windowInfo.taskInfo.taskId);
+ final TaskSnapshot snapshot = windowInfo.mTaskSnapshot;
if (isSnapshotCompatible(windowInfo, snapshot)) {
return STARTING_WINDOW_TYPE_SNAPSHOT;
}
@@ -198,20 +191,6 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo
}
return taskRotation == snapshotRotation;
}
-
- private TaskSnapshot getTaskSnapshot(int taskId) {
- if (mSnapshot != null) {
- return mSnapshot;
- }
- try {
- mSnapshot = ActivityTaskManager.getService().getTaskSnapshot(taskId,
- false/* isLowResolution */);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to get snapshot for task: " + taskId + ", from: " + e);
- return null;
- }
- return mSnapshot;
- }
}
/*
@@ -232,7 +211,9 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo
*/
public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) {
mSplashScreenExecutor.execute(() -> {
- final int suggestionType = mStartingTypeChecker.estimateStartingWindowType(windowInfo);
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addStartingWindow");
+ final int suggestionType = mStartingTypeChecker.estimateStartingWindowType(
+ windowInfo);
final RunningTaskInfo runningTaskInfo = windowInfo.taskInfo;
if (mTaskLaunchingCallback != null && shouldSendToListener(suggestionType)) {
mTaskLaunchingCallback.accept(runningTaskInfo.taskId, suggestionType);
@@ -244,10 +225,12 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo
mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken,
true /* emptyView */);
} else if (suggestionType == STARTING_WINDOW_TYPE_SNAPSHOT) {
- final TaskSnapshot snapshot = mStartingTypeChecker.mSnapshot;
- mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken, snapshot);
+ final TaskSnapshot snapshot = windowInfo.mTaskSnapshot;
+ mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken,
+ snapshot);
}
// If prefer don't show, then don't show!
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
});
}
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 6e437411dcf8..e9ce2addd464 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
@@ -19,6 +19,7 @@ package com.android.wm.shell.startingsurface;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.graphics.Color.WHITE;
import static android.graphics.Color.alpha;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -60,6 +61,7 @@ import android.hardware.HardwareBuffer;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.Trace;
import android.util.MergedConfiguration;
import android.util.Slog;
import android.view.IWindowSession;
@@ -222,8 +224,10 @@ public class TaskSnapshotWindow {
final InputChannel tmpInputChannel = new InputChannel();
mainExecutor.execute(() -> {
try {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "TaskSnapshot#addToDisplay");
final int res = session.addToDisplay(window, layoutParams, View.GONE, displayId,
mTmpInsetsState, tmpInputChannel, mTmpInsetsState, mTempControls);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (res < 0) {
Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
return;
@@ -233,9 +237,11 @@ public class TaskSnapshotWindow {
}
window.setOuter(snapshotSurface);
try {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "TaskSnapshot#relayout");
session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, -1,
tmpFrames, tmpMergedConfiguration, surfaceControl, mTmpInsetsState,
mTempControls, TMP_SURFACE_SIZE);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
} catch (RemoteException e) {
snapshotSurface.clearWindowSynced();
}
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 d1d13131dd44..262353502bb7 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
@@ -77,9 +77,9 @@ public class StartingSurfaceDrawerTests {
int mAddWindowForTask = 0;
int mViewThemeResId;
- TestStartingSurfaceDrawer(Context context, ShellExecutor animExecutor,
+ TestStartingSurfaceDrawer(Context context, ShellExecutor splashScreenExecutor,
TransactionPool pool) {
- super(context, animExecutor, pool);
+ super(context, splashScreenExecutor, pool);
}
@Override
@@ -120,8 +120,9 @@ public class StartingSurfaceDrawerTests {
doReturn(metrics).when(mMockWindowManager).getMaximumWindowMetrics();
doNothing().when(mMockWindowManager).addView(any(), any());
- mStartingSurfaceDrawer = spy(new TestStartingSurfaceDrawer(context,
- new HandlerExecutor(new Handler(Looper.getMainLooper())),
+ final HandlerExecutor testExecutor =
+ new HandlerExecutor(new Handler(Looper.getMainLooper()));
+ mStartingSurfaceDrawer = spy(new TestStartingSurfaceDrawer(context, testExecutor,
mTransactionPool));
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index 3fc3d89461ae..f96d34495fda 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -381,8 +381,8 @@ public abstract class WMShellBaseModule {
@WMSingleton
@Provides
static StartingWindowController provideStartingWindowController(Context context,
- @ShellSplashscreenThread ShellExecutor executor, TransactionPool pool) {
- return new StartingWindowController(context, executor, pool);
+ @ShellSplashscreenThread ShellExecutor splashScreenExecutor, TransactionPool pool) {
+ return new StartingWindowController(context, splashScreenExecutor, pool);
}
//
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index aa9727a28985..c39358e7ad4a 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1926,12 +1926,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
void scheduleAddStartingWindow() {
- // Note: we really want to do sendMessageAtFrontOfQueue() because we
- // want to process the message ASAP, before any other queued
- // messages.
- if (!mWmService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) {
- ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Enqueueing ADD_STARTING");
- mWmService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
+ if (StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER) {
+ mAddStartingWindow.run();
+ } else {
+ // Note: we really want to do sendMessageAtFrontOfQueue() because we
+ // want to process the message ASAP, before any other queued
+ // messages.
+ if (!mWmService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) {
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Enqueueing ADD_STARTING");
+ mWmService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
+ }
}
}
@@ -1943,7 +1947,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final StartingData startingData;
synchronized (mWmService.mGlobalLock) {
// There can only be one adding request, silly caller!
- mWmService.mAnimationHandler.removeCallbacks(this);
+ if (!StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER) {
+ mWmService.mAnimationHandler.removeCallbacks(this);
+ }
if (mStartingData == null) {
// Animation has been canceled... do nothing.
@@ -2192,17 +2198,22 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
+ " startingView=%s Callers=%s", this, mStartingWindow, mStartingSurface,
Debug.getCallers(5));
-
- // Use the same thread to remove the window as we used to add it, as otherwise we end up
- // with things in the view hierarchy being called from different threads.
- mWmService.mAnimationHandler.post(() -> {
+ final Runnable removeSurface = () -> {
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Removing startingView=%s", surface);
try {
surface.remove(prepareAnimation);
} catch (Exception e) {
Slog.w(TAG_WM, "Exception when removing starting window", e);
}
- });
+ };
+
+ if (StartingSurfaceController.DEBUG_ENABLE_SHELL_DRAWER) {
+ removeSurface.run();
+ } else {
+ // Use the same thread to remove the window as we used to add it, as otherwise we end up
+ // with things in the view hierarchy being called from different threads.
+ mWmService.mAnimationHandler.post(removeSurface);
+ }
}
private void removeAppTokenFromDisplay() {
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index 603bfd153481..a9b06ca5042b 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -61,7 +61,7 @@ public class StartingSurfaceController {
synchronized (mService.mGlobalLock) {
final Task task = activity.getTask();
if (task != null && mService.mAtmService.mTaskOrganizerController.addStartingWindow(
- task, activity.token, theme)) {
+ task, activity.token, theme, null /* taskSnapshot */)) {
return new ShellStartingSurface(task);
}
}
@@ -128,7 +128,7 @@ public class StartingSurfaceController {
}
if (DEBUG_ENABLE_SHELL_DRAWER) {
mService.mAtmService.mTaskOrganizerController.addStartingWindow(task,
- activity.token, 0 /* launchTheme */);
+ activity.token, 0 /* launchTheme */, taskSnapshot);
return new ShellStartingSurface(task);
}
}
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 565804fc9c8e..ccc0916cf25b 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.window.ITaskOrganizer;
import android.window.ITaskOrganizerController;
import android.window.StartingWindowInfo;
import android.window.TaskAppearedInfo;
+import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
import com.android.internal.annotations.VisibleForTesting;
@@ -118,25 +119,25 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
return mTaskOrganizer.asBinder();
}
- void addStartingWindow(Task task, IBinder appToken, int launchTheme) {
+ void addStartingWindow(Task task, IBinder appToken, int launchTheme,
+ TaskSnapshot taskSnapshot) {
final StartingWindowInfo info = task.getStartingWindowInfo();
if (launchTheme != 0) {
info.splashScreenThemeResId = launchTheme;
}
- mDeferTaskOrgCallbacksConsumer.accept(() -> {
- try {
- mTaskOrganizer.addStartingWindow(info, appToken);
- } catch (RemoteException e) {
- Slog.e(TAG, "Exception sending onTaskStart callback", e);
- }
- });
+ info.mTaskSnapshot = taskSnapshot;
+ // make this happen prior than prepare surface
+ try {
+ mTaskOrganizer.addStartingWindow(info, appToken);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending onTaskStart callback", e);
+ }
}
void removeStartingWindow(Task task, boolean prepareAnimation) {
- mDeferTaskOrgCallbacksConsumer.accept(() -> {
- SurfaceControl firstWindowLeash = null;
- Rect mainFrame = null;
- // TODO enable shift up animation once we fix flicker test
+ SurfaceControl firstWindowLeash = null;
+ Rect mainFrame = null;
+ // TODO enable shift up animation once we fix flicker test
// final boolean playShiftUpAnimation = !task.inMultiWindowMode();
// if (prepareAnimation && playShiftUpAnimation) {
// final ActivityRecord topActivity = task.topActivityWithStartingWindow();
@@ -144,32 +145,29 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
// final WindowState mainWindow =
// topActivity.findMainWindow(false/* includeStartingApp */);
// if (mainWindow != null) {
- // TODO create proper leash instead of the copied SC
+ // TODO create proper leash instead of the copied SC
// firstWindowLeash = new SurfaceControl(mainWindow.getSurfaceControl(),
// "TaskOrganizerController.removeStartingWindow");
// mainFrame = mainWindow.getRelativeFrame();
// }
// }
// }
- try {
- mTaskOrganizer.removeStartingWindow(task.mTaskId, firstWindowLeash, mainFrame,
- /* TODO(183004107) Revert this when jankiness is solved
- prepareAnimation); */ false);
+ try {
+ mTaskOrganizer.removeStartingWindow(task.mTaskId, firstWindowLeash, mainFrame,
+ /* TODO(183004107) Revert this when jankiness is solved
+ prepareAnimation); */ false);
- } catch (RemoteException e) {
- Slog.e(TAG, "Exception sending onStartTaskFinished callback", e);
- }
- });
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending onStartTaskFinished callback", e);
+ }
}
void copySplashScreenView(Task task) {
- mDeferTaskOrgCallbacksConsumer.accept(() -> {
- try {
- mTaskOrganizer.copySplashScreenView(task.mTaskId);
- } catch (RemoteException e) {
- Slog.e(TAG, "Exception sending copyStartingWindowView callback", e);
- }
- });
+ try {
+ mTaskOrganizer.copySplashScreenView(task.mTaskId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception sending copyStartingWindowView callback", e);
+ }
}
SurfaceControl prepareLeash(Task task, boolean visible, String reason) {
@@ -266,8 +264,9 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
mUid = uid;
}
- void addStartingWindow(Task t, IBinder appToken, int launchTheme) {
- mOrganizer.addStartingWindow(t, appToken, launchTheme);
+ void addStartingWindow(Task t, IBinder appToken, int launchTheme,
+ TaskSnapshot taskSnapshot) {
+ mOrganizer.addStartingWindow(t, appToken, launchTheme, taskSnapshot);
}
void removeStartingWindow(Task t, boolean prepareAnimation) {
@@ -505,14 +504,15 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub {
return !ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES, winMode);
}
- boolean addStartingWindow(Task task, IBinder appToken, int launchTheme) {
+ boolean addStartingWindow(Task task, IBinder appToken, int launchTheme,
+ TaskSnapshot taskSnapshot) {
final Task rootTask = task.getRootTask();
if (rootTask == null || rootTask.mTaskOrganizer == null) {
return false;
}
final TaskOrganizerState state =
mTaskOrganizerStates.get(rootTask.mTaskOrganizer.asBinder());
- state.addStartingWindow(task, appToken, launchTheme);
+ state.addStartingWindow(task, appToken, launchTheme, taskSnapshot);
return true;
}
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 124f6ddc8391..8cede6df491d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2358,21 +2358,6 @@ public class ActivityRecordTests extends WindowTestsBase {
}
@Test
- public void testAddRemoveRace() {
- registerTestStartingWindowOrganizer();
- // There was once a race condition between adding and removing starting windows
- final ActivityRecord appToken = new ActivityBuilder(mAtm).setCreateTask(true).build();
- for (int i = 0; i < 1000; i++) {
- appToken.addStartingWindow(mPackageName,
- android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true,
- false, false);
- appToken.removeStartingWindow();
- waitUntilHandlersIdle();
- assertNoStartingWindow(appToken);
- }
- }
-
- @Test
public void testTransferStartingWindow() {
registerTestStartingWindowOrganizer();
final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build();