diff options
9 files changed, 101 insertions, 123 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index d229fd023e9f..f453ba16043c 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -140,7 +140,6 @@ import android.widget.AdapterView; 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; @@ -969,7 +968,6 @@ public class Activity extends ContextThemeWrapper private UiTranslationController mUiTranslationController; private SplashScreen mSplashScreen; - private SplashScreenView mSplashScreenView; private final WindowControllerCallback mWindowControllerCallback = new WindowControllerCallback() { @@ -1640,16 +1638,6 @@ public class Activity extends ContextThemeWrapper } } - /** @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 d0680f8c9268..431755e092e3 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -166,6 +166,7 @@ import android.view.Choreographer; 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.Map; 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 @@ public final class ActivityThread extends ClientTransactionHandler @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,63 +4086,52 @@ public final class ActivityThread extends ClientTransactionHandler } 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)); + } + }); } - @Override - public void handOverSplashScreenView(@NonNull ActivityClientRecord r) { - final SplashScreenView v = r.activity.getSplashScreenView(); - if (v == null) { - return; - } + private void reportSplashscreenViewShown(IBinder token, SplashScreenView view) { + ActivityClient.getInstance().reportSplashScreenAttached(token); synchronized (this) { if (mSplashScreenGlobal != null) { - mSplashScreenGlobal.handOverSplashScreenView(r.token, v); + mSplashScreenGlobal.handOverSplashScreenView(token, view); } } } + 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)); + } + /** * Cycle activity through onPause and onUserLeaveHint so that PIP is entered if supported, then * return to its previous state. This allows activities that rely on onUserLeaveHint instead of diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java index 115101c0bff6..c743f6572d5e 100644 --- a/core/java/android/app/ClientTransactionHandler.java +++ b/core/java/android/app/ClientTransactionHandler.java @@ -28,6 +28,7 @@ import android.content.res.Configuration; 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 @@ public abstract class ClientTransactionHandler { /** 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 5374984d31d0..767fd28b8a2a 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 @@ import java.lang.annotation.RetentionPolicy; 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 @@ public class TransferSplashScreenViewStateItem extends ActivityTransactionItem { @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 63757e373795..941f8a5bb846 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -1889,18 +1889,45 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall * @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 && lastSc != null) { - mTmpTransaction.reparent(lastSc, null).apply(); + mTmpTransaction.reparent(lastSc, null); mSurfacePackage.release(); + applyTransaction(applyTransactionOnDraw); } else if (mSurfaceControl != null) { 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 3e0075857402..3354a6ca7738 100644 --- a/core/java/android/window/SplashScreen.java +++ b/core/java/android/window/SplashScreen.java @@ -241,7 +241,6 @@ public interface SplashScreen { public void handOverSplashScreenView(@NonNull IBinder token, @NonNull SplashScreenView splashScreenView) { - transferSurface(splashScreenView); dispatchOnExitAnimation(token, splashScreenView); } @@ -265,9 +264,5 @@ public interface SplashScreen { 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 f14294ee512e..f748d4bc121d 100644 --- a/core/java/android/window/SplashScreenView.java +++ b/core/java/android/window/SplashScreenView.java @@ -464,7 +464,10 @@ public final class SplashScreenView extends FrameLayout { } - void transferSurface() { + /** + * @hide + */ + public void syncTransferSurfaceOnDraw() { if (mSurfacePackage == null) { return; } @@ -474,8 +477,8 @@ public final class SplashScreenView extends FrameLayout { 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 @@ public final class SplashScreenView extends FrameLayout { restoreSystemUIColors(); mWindow = null; } - if (mHostActivity != null) { - mHostActivity.setSplashScreenView(null); - mHostActivity = null; - } mHasRemoved = true; } @@ -582,7 +581,6 @@ public final class SplashScreenView extends FrameLayout { * @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 394ff755d252..8ef973dbcfae 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_MULTI_WINDOW; 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; @@ -2322,7 +2320,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // 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(); } @@ -2331,13 +2330,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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; } @@ -2347,14 +2350,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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 f1345e8ee61e..731036b3d9a4 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 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } // 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 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } } + 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(); @@ -510,12 +517,8 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { 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); - removalInfo.windowAnimationLeash = adaptor.mAnimationLeash; + removalInfo.windowAnimationLeash = applyStartingWindowAnimation(mainWindow); removalInfo.mainFrame = mainWindow.getRelativeFrame(); t.setPosition(removalInfo.windowAnimationLeash, removalInfo.mainFrame.left, removalInfo.mainFrame.top); |