Merge "Fixes the flicker when transfer splash screen view to client"
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index a28a34b..ef245c7 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -138,7 +138,6 @@
import android.widget.Toast;
import android.widget.Toolbar;
import android.window.SplashScreen;
-import android.window.SplashScreenView;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -955,7 +954,6 @@
private UiTranslationController mUiTranslationController;
private SplashScreen mSplashScreen;
- private SplashScreenView mSplashScreenView;
private final WindowControllerCallback mWindowControllerCallback =
new WindowControllerCallback() {
@@ -1615,16 +1613,6 @@
}
}
- /** @hide */
- public void setSplashScreenView(SplashScreenView v) {
- mSplashScreenView = v;
- }
-
- /** @hide */
- SplashScreenView getSplashScreenView() {
- return mSplashScreenView;
- }
-
/**
* Same as {@link #onCreate(android.os.Bundle)} but called for those activities created with
* the attribute {@link android.R.attr#persistableMode} set to
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 6ebcd81..59db755 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -166,6 +166,7 @@
import android.view.Display;
import android.view.DisplayAdjustments;
import android.view.DisplayAdjustments.FixedRotationAdjustments;
+import android.view.SurfaceControl;
import android.view.ThreadedRenderer;
import android.view.View;
import android.view.ViewDebug;
@@ -235,7 +236,6 @@
import java.util.Objects;
import java.util.TimeZone;
import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
/**
@@ -4074,10 +4074,11 @@
@Override
public void handleAttachSplashScreenView(@NonNull ActivityClientRecord r,
- @Nullable SplashScreenView.SplashScreenViewParcelable parcelable) {
+ @Nullable SplashScreenView.SplashScreenViewParcelable parcelable,
+ @NonNull SurfaceControl startingWindowLeash) {
final DecorView decorView = (DecorView) r.window.peekDecorView();
if (parcelable != null && decorView != null) {
- createSplashScreen(r, decorView, parcelable);
+ createSplashScreen(r, decorView, parcelable, startingWindowLeash);
} else {
// shouldn't happen!
Slog.e(TAG, "handleAttachSplashScreenView failed, unable to attach");
@@ -4085,61 +4086,50 @@
}
private void createSplashScreen(ActivityClientRecord r, DecorView decorView,
- SplashScreenView.SplashScreenViewParcelable parcelable) {
+ SplashScreenView.SplashScreenViewParcelable parcelable,
+ @NonNull SurfaceControl startingWindowLeash) {
final SplashScreenView.Builder builder = new SplashScreenView.Builder(r.activity);
final SplashScreenView view = builder.createFromParcel(parcelable).build();
decorView.addView(view);
view.attachHostActivityAndSetSystemUIColors(r.activity, r.window);
view.requestLayout();
- // Ensure splash screen view is shown before remove the splash screen window.
- final ViewRootImpl impl = decorView.getViewRootImpl();
- final boolean hardwareEnabled = impl != null && impl.isHardwareEnabled();
- final AtomicBoolean notified = new AtomicBoolean();
- if (hardwareEnabled) {
- final Runnable frameCommit = new Runnable() {
- @Override
- public void run() {
- view.post(() -> {
- if (!notified.get()) {
- view.getViewTreeObserver().unregisterFrameCommitCallback(this);
- ActivityClient.getInstance().reportSplashScreenAttached(
- r.token);
- notified.set(true);
- }
- });
- }
- };
- view.getViewTreeObserver().registerFrameCommitCallback(frameCommit);
- } else {
- final ViewTreeObserver.OnDrawListener onDrawListener =
- new ViewTreeObserver.OnDrawListener() {
- @Override
- public void onDraw() {
- view.post(() -> {
- if (!notified.get()) {
- view.getViewTreeObserver().removeOnDrawListener(this);
- ActivityClient.getInstance().reportSplashScreenAttached(
- r.token);
- notified.set(true);
- }
- });
- }
- };
- view.getViewTreeObserver().addOnDrawListener(onDrawListener);
+
+ view.getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
+ @Override
+ public void onDraw() {
+ // Transfer the splash screen view from shell to client.
+ // Call syncTransferSplashscreenViewTransaction at the first onDraw so we can ensure
+ // the client view is ready to show and we can use applyTransactionOnDraw to make
+ // all transitions happen at the same frame.
+ syncTransferSplashscreenViewTransaction(
+ view, r.token, decorView, startingWindowLeash);
+ view.postOnAnimation(() -> view.getViewTreeObserver().removeOnDrawListener(this));
+ }
+ });
+ }
+
+ private void reportSplashscreenViewShown(IBinder token, SplashScreenView view) {
+ ActivityClient.getInstance().reportSplashScreenAttached(token);
+ synchronized (this) {
+ if (mSplashScreenGlobal != null) {
+ mSplashScreenGlobal.handOverSplashScreenView(token, view);
+ }
}
}
- @Override
- public void handOverSplashScreenView(@NonNull ActivityClientRecord r) {
- final SplashScreenView v = r.activity.getSplashScreenView();
- if (v == null) {
- return;
- }
- synchronized (this) {
- if (mSplashScreenGlobal != null) {
- mSplashScreenGlobal.handOverSplashScreenView(r.token, v);
- }
- }
+ private void syncTransferSplashscreenViewTransaction(SplashScreenView view, IBinder token,
+ View decorView, @NonNull SurfaceControl startingWindowLeash) {
+ // Ensure splash screen view is shown before remove the splash screen window.
+ // Once the copied splash screen view is onDrawn on decor view, use applyTransactionOnDraw
+ // to ensure the transfer of surface view and hide starting window are happen at the same
+ // frame.
+ final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+ transaction.hide(startingWindowLeash);
+
+ decorView.getViewRootImpl().applyTransactionOnDraw(transaction);
+ view.syncTransferSurfaceOnDraw();
+ // Tell server we can remove the starting window
+ decorView.postOnAnimation(() -> reportSplashscreenViewShown(token, view));
}
/**
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 115101c..c743f65 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -28,6 +28,7 @@
import android.os.IBinder;
import android.util.MergedConfiguration;
import android.view.DisplayAdjustments.FixedRotationAdjustments;
+import android.view.SurfaceControl;
import android.window.SplashScreenView.SplashScreenViewParcelable;
import com.android.internal.annotations.VisibleForTesting;
@@ -165,10 +166,8 @@
/** Attach a splash screen window view to the top of the activity */
public abstract void handleAttachSplashScreenView(@NonNull ActivityClientRecord r,
- @NonNull SplashScreenViewParcelable parcelable);
-
- /** Hand over the splash screen window view to the activity */
- public abstract void handOverSplashScreenView(@NonNull ActivityClientRecord r);
+ @NonNull SplashScreenViewParcelable parcelable,
+ @NonNull SurfaceControl startingWindowLeash);
/** Perform activity launch. */
public abstract Activity handleLaunchActivity(@NonNull ActivityClientRecord r,
diff --git a/core/java/android/app/servertransaction/TransferSplashScreenViewStateItem.java b/core/java/android/app/servertransaction/TransferSplashScreenViewStateItem.java
index 5374984..767fd28 100644
--- a/core/java/android/app/servertransaction/TransferSplashScreenViewStateItem.java
+++ b/core/java/android/app/servertransaction/TransferSplashScreenViewStateItem.java
@@ -16,17 +16,14 @@
package android.app.servertransaction;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
import android.app.ClientTransactionHandler;
import android.os.Parcel;
+import android.view.SurfaceControl;
import android.window.SplashScreenView.SplashScreenViewParcelable;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
/**
* Transfer a splash screen view to an Activity.
* @hide
@@ -34,31 +31,13 @@
public class TransferSplashScreenViewStateItem extends ActivityTransactionItem {
private SplashScreenViewParcelable mSplashScreenViewParcelable;
- private @TransferRequest int mRequest;
-
- @IntDef(value = {
- ATTACH_TO,
- HANDOVER_TO
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface TransferRequest {}
- // request client to attach the view on it.
- public static final int ATTACH_TO = 0;
- // tell client that you can handle the splash screen view.
- public static final int HANDOVER_TO = 1;
+ private SurfaceControl mStartingWindowLeash;
@Override
public void execute(@NonNull ClientTransactionHandler client,
@NonNull ActivityThread.ActivityClientRecord r,
PendingTransactionActions pendingActions) {
- switch (mRequest) {
- case ATTACH_TO:
- client.handleAttachSplashScreenView(r, mSplashScreenViewParcelable);
- break;
- case HANDOVER_TO:
- client.handOverSplashScreenView(r);
- break;
- }
+ client.handleAttachSplashScreenView(r, mSplashScreenViewParcelable, mStartingWindowLeash);
}
@Override
@@ -68,26 +47,27 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mRequest);
dest.writeTypedObject(mSplashScreenViewParcelable, flags);
+ dest.writeTypedObject(mStartingWindowLeash, flags);
}
private TransferSplashScreenViewStateItem() {}
private TransferSplashScreenViewStateItem(Parcel in) {
- mRequest = in.readInt();
mSplashScreenViewParcelable = in.readTypedObject(SplashScreenViewParcelable.CREATOR);
+ mStartingWindowLeash = in.readTypedObject(SurfaceControl.CREATOR);
}
/** Obtain an instance initialized with provided params. */
- public static TransferSplashScreenViewStateItem obtain(@TransferRequest int state,
- @Nullable SplashScreenViewParcelable parcelable) {
+ public static TransferSplashScreenViewStateItem obtain(
+ @Nullable SplashScreenViewParcelable parcelable,
+ @Nullable SurfaceControl startingWindowLeash) {
TransferSplashScreenViewStateItem instance =
ObjectPool.obtain(TransferSplashScreenViewStateItem.class);
if (instance == null) {
instance = new TransferSplashScreenViewStateItem();
}
- instance.mRequest = state;
instance.mSplashScreenViewParcelable = parcelable;
+ instance.mStartingWindowLeash = startingWindowLeash;
return instance;
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 945758f..e8dfa9b 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1889,6 +1889,24 @@
* @param p The SurfacePackage to embed.
*/
public void setChildSurfacePackage(@NonNull SurfaceControlViewHost.SurfacePackage p) {
+ setChildSurfacePackage(p, false /* applyTransactionOnDraw */);
+ }
+
+ /**
+ * Similar to setChildSurfacePackage, but using the BLAST queue so the transaction can be
+ * synchronized with the ViewRootImpl frame.
+ * @hide
+ */
+ public void setChildSurfacePackageOnDraw(
+ @NonNull SurfaceControlViewHost.SurfacePackage p) {
+ setChildSurfacePackage(p, true /* applyTransactionOnDraw */);
+ }
+
+ /**
+ * @param applyTransactionOnDraw Whether to apply transaction at onDraw or immediately.
+ */
+ private void setChildSurfacePackage(
+ @NonNull SurfaceControlViewHost.SurfacePackage p, boolean applyTransactionOnDraw) {
final SurfaceControl lastSc = mSurfacePackage != null ?
mSurfacePackage.getSurfaceControl() : null;
if (mSurfaceControl != null) {
@@ -1896,13 +1914,20 @@
mTmpTransaction.reparent(lastSc, null);
mSurfacePackage.release();
}
-
reparentSurfacePackage(mTmpTransaction, p);
- mTmpTransaction.apply();
+ applyTransaction(applyTransactionOnDraw);
}
mSurfacePackage = p;
}
+ private void applyTransaction(boolean applyTransactionOnDraw) {
+ if (applyTransactionOnDraw) {
+ getViewRootImpl().applyTransactionOnDraw(mTmpTransaction);
+ } else {
+ mTmpTransaction.apply();
+ }
+ }
+
private void reparentSurfacePackage(SurfaceControl.Transaction t,
SurfaceControlViewHost.SurfacePackage p) {
final SurfaceControl sc = p.getSurfaceControl();
diff --git a/core/java/android/window/SplashScreen.java b/core/java/android/window/SplashScreen.java
index 3e00758..3354a6c 100644
--- a/core/java/android/window/SplashScreen.java
+++ b/core/java/android/window/SplashScreen.java
@@ -241,7 +241,6 @@
public void handOverSplashScreenView(@NonNull IBinder token,
@NonNull SplashScreenView splashScreenView) {
- transferSurface(splashScreenView);
dispatchOnExitAnimation(token, splashScreenView);
}
@@ -265,9 +264,5 @@
return impl != null && impl.mExitAnimationListener != null;
}
}
-
- private void transferSurface(@NonNull SplashScreenView splashScreenView) {
- splashScreenView.transferSurface();
- }
}
}
diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java
index f14294e..f748d4b 100644
--- a/core/java/android/window/SplashScreenView.java
+++ b/core/java/android/window/SplashScreenView.java
@@ -464,7 +464,10 @@
}
- void transferSurface() {
+ /**
+ * @hide
+ */
+ public void syncTransferSurfaceOnDraw() {
if (mSurfacePackage == null) {
return;
}
@@ -474,8 +477,8 @@
String.format("SurfacePackage'surface reparented to %s", parent)));
Log.d(TAG, "Transferring surface " + mSurfaceView.toString());
}
- mSurfaceView.setChildSurfacePackage(mSurfacePackage);
+ mSurfaceView.setChildSurfacePackageOnDraw(mSurfacePackage);
}
void initIconAnimation(Drawable iconDrawable, long duration) {
@@ -533,10 +536,6 @@
restoreSystemUIColors();
mWindow = null;
}
- if (mHostActivity != null) {
- mHostActivity.setSplashScreenView(null);
- mHostActivity = null;
- }
mHasRemoved = true;
}
@@ -582,7 +581,6 @@
* @hide
*/
public void attachHostActivityAndSetSystemUIColors(Activity activity, Window window) {
- activity.setSplashScreenView(this);
mHostActivity = activity;
mWindow = window;
final WindowManager.LayoutParams attr = window.getAttributes();
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 352168c..b807863 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -46,8 +46,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.activityTypeToString;
import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
-import static android.app.servertransaction.TransferSplashScreenViewStateItem.ATTACH_TO;
-import static android.app.servertransaction.TransferSplashScreenViewStateItem.HANDOVER_TO;
import static android.content.Intent.ACTION_MAIN;
import static android.content.Intent.CATEGORY_HOME;
import static android.content.Intent.CATEGORY_LAUNCHER;
@@ -2326,7 +2324,8 @@
// unable to copy from shell, maybe it's not a splash screen. or something went wrong.
// either way, abort and reset the sequence.
if (parcelable == null
- || mTransferringSplashScreenState != TRANSFER_SPLASH_SCREEN_COPYING) {
+ || mTransferringSplashScreenState != TRANSFER_SPLASH_SCREEN_COPYING
+ || mStartingWindow == null) {
if (parcelable != null) {
parcelable.clearIfNeeded();
}
@@ -2335,13 +2334,17 @@
return;
}
// schedule attach splashScreen to client
+ final SurfaceControl windowAnimationLeash = TaskOrganizerController
+ .applyStartingWindowAnimation(mStartingWindow);
try {
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_ATTACH_TO_CLIENT;
mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
- TransferSplashScreenViewStateItem.obtain(ATTACH_TO, parcelable));
+ TransferSplashScreenViewStateItem.obtain(parcelable,
+ windowAnimationLeash));
scheduleTransferSplashScreenTimeout();
} catch (Exception e) {
Slog.w(TAG, "onCopySplashScreenComplete fail: " + this);
+ mStartingWindow.cancelAnimation();
parcelable.clearIfNeeded();
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_FINISH;
}
@@ -2351,14 +2354,9 @@
removeTransferSplashScreenTimeout();
// Client has draw the splash screen, so we can remove the starting window.
if (mStartingWindow != null) {
+ mStartingWindow.cancelAnimation();
mStartingWindow.hide(false, false);
}
- try {
- mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
- TransferSplashScreenViewStateItem.obtain(HANDOVER_TO, null));
- } catch (Exception e) {
- Slog.w(TAG, "onSplashScreenAttachComplete fail: " + this);
- }
// no matter what, remove the starting window.
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_FINISH;
removeStartingWindowAnimation(false /* prepareAnimation */);
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index ccda126..c9f2696 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -422,8 +422,8 @@
}
// Capture the animation surface control for activity's main window
- private static class StartingWindowAnimationAdaptor implements AnimationAdapter {
- private SurfaceControl mAnimationLeash;
+ static class StartingWindowAnimationAdaptor implements AnimationAdapter {
+ SurfaceControl mAnimationLeash;
@Override
public boolean getShowWallpaper() {
return false;
@@ -464,6 +464,13 @@
}
}
+ static SurfaceControl applyStartingWindowAnimation(WindowContainer window) {
+ final StartingWindowAnimationAdaptor adaptor = new StartingWindowAnimationAdaptor();
+ window.startAnimation(window.getPendingTransaction(), adaptor, false,
+ ANIMATION_TYPE_STARTING_REVEAL);
+ return adaptor.mAnimationLeash;
+ }
+
boolean addStartingWindow(Task task, ActivityRecord activity, int launchTheme,
TaskSnapshot taskSnapshot) {
final Task rootTask = task.getRootTask();
@@ -507,12 +514,8 @@
final WindowState mainWindow =
topActivity.findMainWindow(false/* includeStartingApp */);
if (mainWindow != null) {
- final StartingWindowAnimationAdaptor adaptor =
- new StartingWindowAnimationAdaptor();
final SurfaceControl.Transaction t = mainWindow.getPendingTransaction();
- mainWindow.startAnimation(t, adaptor, false,
- ANIMATION_TYPE_STARTING_REVEAL);
- windowAnimationLeash = adaptor.mAnimationLeash;
+ windowAnimationLeash = applyStartingWindowAnimation(mainWindow);
mainFrame = mainWindow.getRelativeFrame();
t.setPosition(windowAnimationLeash, mainFrame.left, mainFrame.top);
}