Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bcb99535 authored by wilsonshih's avatar wilsonshih
Browse files

Adding the SurfaceView on splash screen thread.

The SurfaceView of the AVD icon should be create on splash screen
thread instead of worker thread, this can guarantee that the first
frame of the FrameLayout and AVD will be draw at the same frame.
And it also become faster by combining two view in same traversal
together.

Also stop the AVD animation after splash screen window removed.

Bug: 195736516
Test: verify no more Choreographer#doFrame on splash screen thread
after splash screen window removed.

Change-Id: I5942db8974f1272d9c26ac19b164312e4424be66
parent b24386ba
Loading
Loading
Loading
Loading
+42 −11
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ import com.android.internal.util.ContrastColorUtil;

import java.time.Duration;
import java.time.Instant;
import java.util.function.Consumer;

/**
 * <p>The view which allows an activity to customize its splash screen exit animation.</p>
@@ -144,6 +145,7 @@ public final class SplashScreenView extends FrameLayout {
        private Bitmap mParceledBrandingBitmap;
        private Instant mIconAnimationStart;
        private Duration mIconAnimationDuration;
        private Consumer<Runnable> mUiThreadInitTask;

        public Builder(@NonNull Context context) {
            mContext = context;
@@ -231,6 +233,15 @@ public final class SplashScreenView extends FrameLayout {
            return this;
        }

        /**
         * Set the Runnable that can receive the task which should be executed on UI thread.
         * @param uiThreadInitTask
         */
        public Builder setUiThreadInitConsumer(Consumer<Runnable> uiThreadInitTask) {
            mUiThreadInitTask = uiThreadInitTask;
            return this;
        }

        /**
         * Set the Drawable object and size for the branding view.
         */
@@ -262,7 +273,11 @@ public final class SplashScreenView extends FrameLayout {
            // center icon
            if (mIconDrawable instanceof SplashScreenView.IconAnimateListener
                    || mSurfacePackage != null) {
                if (mUiThreadInitTask != null) {
                    mUiThreadInitTask.accept(() -> view.mIconView = createSurfaceView(view));
                } else {
                    view.mIconView = createSurfaceView(view);
                }
                view.initIconAnimation(mIconDrawable,
                        mIconAnimationDuration != null ? mIconAnimationDuration.toMillis() : 0);
                view.mIconAnimationStart = mIconAnimationStart;
@@ -316,6 +331,7 @@ public final class SplashScreenView extends FrameLayout {
        }

        private SurfaceView createSurfaceView(@NonNull SplashScreenView view) {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "SplashScreenView#createSurfaceView");
            final Context viewContext = view.getContext();
            final SurfaceView surfaceView = new SurfaceView(viewContext);
            surfaceView.setPadding(0, 0, 0, 0);
@@ -361,6 +377,7 @@ public final class SplashScreenView extends FrameLayout {

            view.addView(surfaceView);
            view.mSurfaceView = surfaceView;
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            return surfaceView;
        }
    }
@@ -532,23 +549,32 @@ public final class SplashScreenView extends FrameLayout {

    private void releaseAnimationSurfaceHost() {
        if (mSurfaceHost != null && !mIsCopied) {
            final SurfaceControlViewHost finalSurfaceHost = mSurfaceHost;
            mSurfaceHost = null;
            finalSurfaceHost.getView().post(() -> {
            if (DEBUG) {
                Log.d(TAG,
                        "Shell removed splash screen."
                                + " Releasing SurfaceControlViewHost on thread #"
                                + Thread.currentThread().getId());
            }
                finalSurfaceHost.release();
            });
            releaseIconHost(mSurfaceHost);
            mSurfaceHost = null;
        } else if (mSurfacePackage != null && mSurfaceHost == null) {
            mSurfacePackage = null;
            mClientCallback.sendResult(null);
        }
    }

    /**
     * Release the host which hold the SurfaceView of the icon.
     * @hide
     */
    public static void releaseIconHost(SurfaceControlViewHost host) {
        final Drawable background = host.getView().getBackground();
        if (background instanceof SplashScreenView.IconAnimateListener) {
            ((SplashScreenView.IconAnimateListener) background).stopAnimation();
        }
        host.release();
    }

    /**
     * Called when this view is attached to an activity. This also makes SystemUI colors
     * transparent so the content of splash screen view can draw fully.
@@ -640,6 +666,11 @@ public final class SplashScreenView extends FrameLayout {
         * @return true if this drawable object can also be animated and it can be played now.
         */
        boolean prepareAnimate(long duration, Runnable startListener);

        /**
         * Stop animation.
         */
        void stopAnimation();
    }

    /**
+17 −6
Original line number Diff line number Diff line
@@ -138,12 +138,14 @@ public class SplashscreenContentDrawer {
     *                                 null if failed.
     */
    void createContentView(Context context, @StartingWindowType int suggestType, ActivityInfo info,
            int taskId, Consumer<SplashScreenView> splashScreenViewConsumer) {
            int taskId, Consumer<SplashScreenView> splashScreenViewConsumer,
            Consumer<Runnable> uiThreadInitConsumer) {
        mSplashscreenWorkerHandler.post(() -> {
            SplashScreenView contentView;
            try {
                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "makeSplashScreenContentView");
                contentView = makeSplashScreenContentView(context, info, suggestType);
                contentView = makeSplashScreenContentView(context, info, suggestType,
                        uiThreadInitConsumer);
                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            } catch (RuntimeException e) {
                Slog.w(TAG, "failed creating starting window content at taskId: "
@@ -239,7 +241,7 @@ public class SplashscreenContentDrawer {
    }

    private SplashScreenView makeSplashScreenContentView(Context context, ActivityInfo ai,
            @StartingWindowType int suggestType) {
            @StartingWindowType int suggestType, Consumer<Runnable> uiThreadInitConsumer) {
        updateDensity();

        getWindowAttrs(context, mTmpAttrs);
@@ -254,6 +256,7 @@ public class SplashscreenContentDrawer {
                .setWindowBGColor(themeBGColor)
                .overlayDrawable(legacyDrawable)
                .chooseStyle(suggestType)
                .setUiThreadInitConsumer(uiThreadInitConsumer)
                .build();
    }

@@ -324,6 +327,7 @@ public class SplashscreenContentDrawer {
        private int mThemeColor;
        private Drawable[] mFinalIconDrawables;
        private int mFinalIconSize = mIconSize;
        private Consumer<Runnable> mUiThreadInitTask;

        StartingWindowViewBuilder(@NonNull Context context, @NonNull ActivityInfo aInfo) {
            mContext = context;
@@ -345,6 +349,11 @@ public class SplashscreenContentDrawer {
            return this;
        }

        StartingWindowViewBuilder setUiThreadInitConsumer(Consumer<Runnable> uiThreadInitTask) {
            mUiThreadInitTask = uiThreadInitTask;
            return this;
        }

        SplashScreenView build() {
            Drawable iconDrawable;
            final int animationDuration;
@@ -391,7 +400,8 @@ public class SplashscreenContentDrawer {
                animationDuration = 0;
            }

            return fillViewWithIcon(mFinalIconSize, mFinalIconDrawables, animationDuration);
            return fillViewWithIcon(mFinalIconSize, mFinalIconDrawables, animationDuration,
                    mUiThreadInitTask);
        }

        private class ShapeIconFactory extends BaseIconFactory {
@@ -469,7 +479,7 @@ public class SplashscreenContentDrawer {
        }

        private SplashScreenView fillViewWithIcon(int iconSize, @Nullable Drawable[] iconDrawable,
                int animationDuration) {
                int animationDuration, Consumer<Runnable> uiThreadInitTask) {
            Drawable foreground = null;
            Drawable background = null;
            if (iconDrawable != null) {
@@ -485,7 +495,8 @@ public class SplashscreenContentDrawer {
                    .setIconSize(iconSize)
                    .setIconBackground(background)
                    .setCenterViewDrawable(foreground)
                    .setAnimationDurationMillis(animationDuration);
                    .setAnimationDurationMillis(animationDuration)
                    .setUiThreadInitConsumer(uiThreadInitTask);

            if (mSuggestType == STARTING_WINDOW_TYPE_SPLASH_SCREEN
                    && mTmpAttrs.mBrandingImage != null) {
+7 −0
Original line number Diff line number Diff line
@@ -304,6 +304,13 @@ public class SplashscreenIconDrawableFactory {
            return true;
        }

        @Override
        public void stopAnimation() {
            if (mIconAnimator != null) {
                mIconAnimator.end();
            }
        }

        private final Callback mCallback = new Callback() {
            @Override
            public void invalidateDrawable(@NonNull Drawable who) {
+13 −2
Original line number Diff line number Diff line
@@ -332,7 +332,7 @@ public class StartingSurfaceDrawer {
            mSysuiProxy.requestTopUi(true, TAG);
        }
        mSplashscreenContentDrawer.createContentView(context, suggestType, activityInfo, taskId,
                viewSupplier::setView);
                viewSupplier::setView, viewSupplier::setUiThreadInitTask);
        try {
            if (addWindow(taskId, appToken, rootLayout, display, params, suggestType)) {
                // We use the splash screen worker thread to create SplashScreenView while adding
@@ -367,6 +367,7 @@ public class StartingSurfaceDrawer {
    private static class SplashScreenViewSupplier implements Supplier<SplashScreenView> {
        private SplashScreenView mView;
        private boolean mIsViewSet;
        private Runnable mUiThreadInitTask;
        void setView(SplashScreenView view) {
            synchronized (this) {
                mView = view;
@@ -375,6 +376,12 @@ public class StartingSurfaceDrawer {
            }
        }

        void setUiThreadInitTask(Runnable initTask) {
            synchronized (this) {
                mUiThreadInitTask = initTask;
            }
        }

        @Override
        public @Nullable SplashScreenView get() {
            synchronized (this) {
@@ -384,6 +391,10 @@ public class StartingSurfaceDrawer {
                    } catch (InterruptedException ignored) {
                    }
                }
                if (mUiThreadInitTask != null) {
                    mUiThreadInitTask.run();
                    mUiThreadInitTask = null;
                }
                return mView;
            }
        }
@@ -506,7 +517,7 @@ public class StartingSurfaceDrawer {
            Slog.v(TAG, reason + "the splash screen. Releasing SurfaceControlViewHost for task:"
                    + taskId);
        }
        viewHost.getView().post(viewHost::release);
        SplashScreenView.releaseIconHost(viewHost);
    }

    protected boolean addWindow(int taskId, IBinder appToken, View view, Display display,