Merge "Fixes the flicker when transfer splash screen view to client"

This commit is contained in:
Wei Sheng Shih
2021-10-06 02:59:09 +00:00
committed by Android (Google) Code Review
9 changed files with 102 additions and 126 deletions

View File

@@ -138,7 +138,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;
@@ -955,7 +954,6 @@ public class Activity extends ContextThemeWrapper
private UiTranslationController mUiTranslationController;
private SplashScreen mSplashScreen;
private SplashScreenView mSplashScreenView;
private final WindowControllerCallback mWindowControllerCallback =
new WindowControllerCallback() {
@@ -1615,16 +1613,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

View File

@@ -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,61 +4086,50 @@ 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));
}
});
}
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));
}
/**

View File

@@ -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,

View File

@@ -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;
}

View File

@@ -1889,6 +1889,24 @@ 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) {
@@ -1896,13 +1914,20 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
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();

View File

@@ -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();
}
}
}

View File

@@ -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();

View File

@@ -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;
@@ -2326,7 +2324,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();
}
@@ -2335,13 +2334,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;
}
@@ -2351,14 +2354,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 */);

View File

@@ -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();
@@ -507,12 +514,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);
windowAnimationLeash = adaptor.mAnimationLeash;
windowAnimationLeash = applyStartingWindowAnimation(mainWindow);
mainFrame = mainWindow.getRelativeFrame();
t.setPosition(windowAnimationLeash, mainFrame.left, mainFrame.top);
}