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

Commit a46ff113 authored by Vadim Caen's avatar Vadim Caen
Browse files

Release the SurfacePackageViewHost on splash screen removed

The SurfacePackageViewHost was never release, leading to a leak of its
surfaces.

This CL ensure it is released:
  - Directly in the shell if the SplashScreenView was not copied
  - From the client, through the window manger and then to the shell if
  the splash screen was copied in the client process

Test: Manually tested with app setting by checking if the surfaces
are actually removed (winscope + logs) in the following scenarios:
    - When the application registers an OnExitAnimationListener
      and calls remove(),
    - When the application registers an OnExitAnimationListener
      and dies without calling remove()
    - When the applicaiton does not register an OnExitAnimationListener
    and the shell removes the splash screen
Bug: 189759180

Change-Id: Ib68bfffad6720911368739d7dd87d8a03034c589
parent 5829bd55
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -3185,6 +3185,7 @@ package android.window {
    method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.app.ActivityManager.RunningTaskInfo> getChildTasks(@NonNull android.window.WindowContainerToken, @NonNull int[]);
    method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public android.window.WindowContainerToken getImeTarget(int);
    method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List<android.app.ActivityManager.RunningTaskInfo> getRootTasks(int, @NonNull int[]);
    method @BinderThread public void onAppSplashScreenViewRemoved(int);
    method @BinderThread public void onBackPressedOnTaskRoot(@NonNull android.app.ActivityManager.RunningTaskInfo);
    method @BinderThread public void onTaskAppeared(@NonNull android.app.ActivityManager.RunningTaskInfo, @NonNull android.view.SurfaceControl);
    method @BinderThread public void onTaskInfoChanged(@NonNull android.app.ActivityManager.RunningTaskInfo);
+5 −0
Original line number Diff line number Diff line
@@ -51,6 +51,11 @@ oneway interface ITaskOrganizer {
     */
    void copySplashScreenView(int taskId);

    /**
     * Called when the Task removed the splash screen.
     */
    void onAppSplashScreenViewRemoved(int taskId);

    /**
     * A callback when the Task is available for the registered organizer. The client is responsible
     * for releasing the SurfaceControl in the callback. For non-root tasks, the leash may initially
+60 −4
Original line number Diff line number Diff line
@@ -34,8 +34,10 @@ import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteCallback;
import android.os.Trace;
import android.util.AttributeSet;
import android.util.Log;
@@ -76,7 +78,7 @@ import java.time.Instant;
 */
public final class SplashScreenView extends FrameLayout {
    private static final String TAG = SplashScreenView.class.getSimpleName();
    private static final boolean DEBUG = false;
    private static final boolean DEBUG = Build.IS_DEBUGGABLE;

    private static final int LIGHT_BARS_MASK =
            WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS
@@ -85,6 +87,7 @@ public final class SplashScreenView extends FrameLayout {
                    | FLAG_TRANSLUCENT_NAVIGATION | FLAG_TRANSLUCENT_STATUS;

    private boolean mNotCopyable;
    private boolean mIsCopied;
    private int mInitBackgroundColor;
    private int mInitIconBackgroundColor;
    private View mIconView;
@@ -103,6 +106,10 @@ public final class SplashScreenView extends FrameLayout {
    private SurfaceControlViewHost.SurfacePackage mSurfacePackage;
    @Nullable
    private SurfaceView mSurfaceView;
    @Nullable
    private SurfaceControlViewHost mSurfaceHost;
    @Nullable
    private RemoteCallback mClientCallback;

    // cache original window and status
    private Window mWindow;
@@ -127,6 +134,7 @@ public final class SplashScreenView extends FrameLayout {
        private Bitmap mParceledIconBitmap;
        private Drawable mIconDrawable;
        private SurfaceControlViewHost.SurfacePackage mSurfacePackage;
        private RemoteCallback mClientCallback;
        private int mBrandingImageWidth;
        private int mBrandingImageHeight;
        private Drawable mBrandingDrawable;
@@ -161,6 +169,7 @@ public final class SplashScreenView extends FrameLayout {
            }
            mIconAnimationStart = Instant.ofEpochMilli(parcelable.mIconAnimationStartMillis);
            mIconAnimationDuration = Duration.ofMillis(parcelable.mIconAnimationDurationMillis);
            mClientCallback = parcelable.mClientCallback;
            if (DEBUG) {
                Log.d(TAG, String.format("Building from parcel drawable: %s", mIconDrawable));
            }
@@ -228,6 +237,7 @@ public final class SplashScreenView extends FrameLayout {
            view.mInitBackgroundColor = mBackgroundColor;
            view.mInitIconBackgroundColor = mIconBackground;
            view.setBackgroundColor(mBackgroundColor);
            view.mClientCallback = mClientCallback;

            view.mBrandingImageView = view.findViewById(R.id.splashscreen_branding_view);

@@ -285,7 +295,8 @@ public final class SplashScreenView extends FrameLayout {
            if (mSurfacePackage == null) {
                if (DEBUG) {
                    Log.d(TAG,
                            "Creating Original SurfacePackage. SurfaceView: " + surfaceView);
                            "SurfaceControlViewHost created on thread "
                                    + Thread.currentThread().getId());
                }

                SurfaceControlViewHost viewHost = new SurfaceControlViewHost(mContext,
@@ -297,6 +308,7 @@ public final class SplashScreenView extends FrameLayout {
                SurfaceControlViewHost.SurfacePackage surfacePackage = viewHost.getSurfacePackage();
                surfaceView.setChildSurfacePackage(surfacePackage);
                view.mSurfacePackage = surfacePackage;
                view.mSurfaceHost = viewHost;
                view.mSurfacePackageCopy = new SurfaceControlViewHost.SurfacePackage(
                        surfacePackage);
            } else {
@@ -357,6 +369,7 @@ public final class SplashScreenView extends FrameLayout {
     * @hide
     */
    public void onCopied() {
        mIsCopied = true;
        if (mSurfaceView == null) {
            return;
        }
@@ -369,6 +382,12 @@ public final class SplashScreenView extends FrameLayout {
        mSurfacePackage = null;
    }

    /** @hide **/
    @Nullable
    public SurfaceControlViewHost getSurfaceHost() {
        return mSurfaceHost;
    }

    @Override
    public void setAlpha(float alpha) {
        super.setAlpha(alpha);
@@ -407,8 +426,7 @@ public final class SplashScreenView extends FrameLayout {
        if (DEBUG) {
            mSurfacePackage.getSurfaceControl().addOnReparentListener(
                    (transaction, parent) -> Log.e(TAG,
                            String.format("SurfacePackage'surface reparented.\n Parent: %s",
                                    parent), new Throwable()));
                            String.format("SurfacePackage'surface reparented to %s", parent)));
            Log.d(TAG, "Transferring surface " + mSurfaceView.toString());
        }
        mSurfaceView.setChildSurfacePackage(mSurfacePackage);
@@ -466,9 +484,36 @@ public final class SplashScreenView extends FrameLayout {
        mHasRemoved = true;
    }

    /** @hide **/
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        releaseAnimationSurfaceHost();
    }

    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();
            });
        } else if (mSurfacePackage != null && mSurfaceHost == null) {
            mSurfacePackage = null;
            mClientCallback.sendResult(null);
        }
    }

    /**
     * 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.
     *
     * @hide
     */
    public void attachHostActivityAndSetSystemUIColors(Activity activity, Window window) {
@@ -585,6 +630,7 @@ public final class SplashScreenView extends FrameLayout {
        private long mIconAnimationDurationMillis;

        private SurfaceControlViewHost.SurfacePackage mSurfacePackage;
        private RemoteCallback mClientCallback;

        public SplashScreenViewParcelable(SplashScreenView view) {
            mIconSize = view.mIconView.getWidth();
@@ -641,6 +687,7 @@ public final class SplashScreenView extends FrameLayout {
            mIconAnimationDurationMillis = source.readLong();
            mIconBackground = source.readInt();
            mSurfacePackage = source.readTypedObject(SurfaceControlViewHost.SurfacePackage.CREATOR);
            mClientCallback = source.readTypedObject(RemoteCallback.CREATOR);
        }

        @Override
@@ -660,6 +707,7 @@ public final class SplashScreenView extends FrameLayout {
            dest.writeLong(mIconAnimationDurationMillis);
            dest.writeInt(mIconBackground);
            dest.writeTypedObject(mSurfacePackage, flags);
            dest.writeTypedObject(mClientCallback, flags);
        }

        public static final @NonNull Parcelable.Creator<SplashScreenViewParcelable> CREATOR =
@@ -697,5 +745,13 @@ public final class SplashScreenView extends FrameLayout {
        int getIconBackground() {
            return mIconBackground;
        }

        /**
         * Sets the {@link RemoteCallback} that will be called by the client to notify the shell
         * of the removal of the {@link SplashScreenView}.
         */
        public void setClientCallback(@NonNull RemoteCallback clientCallback) {
            mClientCallback = clientCallback;
        }
    }
}
+16 −1
Original line number Diff line number Diff line
@@ -116,6 +116,16 @@ public class TaskOrganizer extends WindowOrganizer {
    @BinderThread
    public void copySplashScreenView(int taskId) {}

    /**
     * Notify the shell ({@link com.android.wm.shell.ShellTaskOrganizer} that the client has
     * removed the splash screen view.
     * @see com.android.wm.shell.ShellTaskOrganizer#onAppSplashScreenViewRemoved(int)
     * @see SplashScreenView#remove()
     */
    @BinderThread
    public void onAppSplashScreenViewRemoved(int taskId) {
    }

    /**
     * Called when a task with the registered windowing mode can be controlled by this task
     * organizer. For non-root tasks, the leash may initially be hidden so it is up to the organizer
@@ -240,6 +250,11 @@ public class TaskOrganizer extends WindowOrganizer {
            mExecutor.execute(() -> TaskOrganizer.this.copySplashScreenView(taskId));
        }

        @Override
        public void onAppSplashScreenViewRemoved(int taskId) {
            mExecutor.execute(() -> TaskOrganizer.this.onAppSplashScreenViewRemoved(taskId));
        }

        @Override
        public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
            mExecutor.execute(() -> TaskOrganizer.this.onTaskAppeared(taskInfo, leash));
+6 −0
Original line number Diff line number Diff line
@@ -961,6 +961,12 @@
      "group": "WM_DEBUG_APP_TRANSITIONS",
      "at": "com\/android\/server\/wm\/AppTransitionController.java"
    },
    "-1003678883": {
      "message": "Cleaning splash screen token=%s",
      "level": "VERBOSE",
      "group": "WM_DEBUG_STARTING_WINDOW",
      "at": "com\/android\/server\/wm\/ActivityRecord.java"
    },
    "-1003060523": {
      "message": "Finish needs to pause: %s",
      "level": "VERBOSE",
Loading