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

Commit 17469b9a authored by dakinola's avatar dakinola Committed by Naomi Musgrave
Browse files

VirtualDisplay: Merge AIDL calls to stop race condition

Updating execution flow so that setting ContentRecordingSession is no longer
a second AIDL call to the DisplayManager Service, but instead passing in
the session details with the VirtualDisplayConfig, and having it be set
manually in the execution of an earlier AIDL call.

Bug: 267610691
Fix: 267610691
Test: manually built
Test: atest CtsWindowManagerDeviceTestCases:ActivityCaptureCallbackTests
Test: atest CtsWindowManagerDeviceTestCases:DisplayTests
Test: atest DisplayManagerServiceTest
Test: atest DisplayTest
Test: atest WmTests:ContentRecorderTests
Change-Id: Id926aa0800bd23d329bbc8f2a6f739e26fd72a3c
parent 25d0a6a2
Loading
Loading
Loading
Loading
+37 −2
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArraySet;
import android.view.ContentRecordingSession;
import android.view.Display;
import android.view.Surface;

@@ -53,6 +54,8 @@ public final class VirtualDisplayConfig implements Parcelable {
    private final int mDisplayIdToMirror;
    private final boolean mWindowManagerMirroringEnabled;
    private ArraySet<String> mDisplayCategories = null;
    @Nullable
    private ContentRecordingSession mContentRecordingSession;
    private final float mRequestedRefreshRate;

    private VirtualDisplayConfig(
@@ -65,6 +68,7 @@ public final class VirtualDisplayConfig implements Parcelable {
            @Nullable String uniqueId,
            int displayIdToMirror,
            boolean windowManagerMirroringEnabled,
            ContentRecordingSession session,
            @NonNull ArraySet<String> displayCategories,
            float requestedRefreshRate) {
        mName = name;
@@ -76,6 +80,7 @@ public final class VirtualDisplayConfig implements Parcelable {
        mUniqueId = uniqueId;
        mDisplayIdToMirror = displayIdToMirror;
        mWindowManagerMirroringEnabled = windowManagerMirroringEnabled;
        mContentRecordingSession = session;
        mDisplayCategories = displayCategories;
        mRequestedRefreshRate = requestedRefreshRate;
    }
@@ -155,6 +160,17 @@ public final class VirtualDisplayConfig implements Parcelable {
        return mWindowManagerMirroringEnabled;
    }

    /**
     * Returns the recording session associated with this VirtualDisplay. Only used for
     * recording via {@link MediaProjection}.
     *
     * @hide
     */
    @Nullable
    public ContentRecordingSession getContentRecordingSession() {
        return mContentRecordingSession;
    }

    /**
     * Returns the display categories.
     *
@@ -186,6 +202,7 @@ public final class VirtualDisplayConfig implements Parcelable {
        dest.writeString8(mUniqueId);
        dest.writeInt(mDisplayIdToMirror);
        dest.writeBoolean(mWindowManagerMirroringEnabled);
        dest.writeTypedObject(mContentRecordingSession, flags);
        dest.writeArraySet(mDisplayCategories);
        dest.writeFloat(mRequestedRefreshRate);
    }
@@ -211,6 +228,7 @@ public final class VirtualDisplayConfig implements Parcelable {
                && Objects.equals(mUniqueId, that.mUniqueId)
                && mDisplayIdToMirror == that.mDisplayIdToMirror
                && mWindowManagerMirroringEnabled == that.mWindowManagerMirroringEnabled
                && Objects.equals(mContentRecordingSession, that.mContentRecordingSession)
                && Objects.equals(mDisplayCategories, that.mDisplayCategories)
                && mRequestedRefreshRate == that.mRequestedRefreshRate;
    }
@@ -219,8 +237,8 @@ public final class VirtualDisplayConfig implements Parcelable {
    public int hashCode() {
        int hashCode = Objects.hash(
                mName, mWidth, mHeight, mDensityDpi, mFlags, mSurface, mUniqueId,
                mDisplayIdToMirror, mWindowManagerMirroringEnabled, mDisplayCategories,
                mRequestedRefreshRate);
                mDisplayIdToMirror, mWindowManagerMirroringEnabled, mContentRecordingSession,
                mDisplayCategories, mRequestedRefreshRate);
        return hashCode;
    }

@@ -237,6 +255,7 @@ public final class VirtualDisplayConfig implements Parcelable {
                + " mUniqueId=" + mUniqueId
                + " mDisplayIdToMirror=" + mDisplayIdToMirror
                + " mWindowManagerMirroringEnabled=" + mWindowManagerMirroringEnabled
                + " mContentRecordingSession=" + mContentRecordingSession
                + " mDisplayCategories=" + mDisplayCategories
                + " mRequestedRefreshRate=" + mRequestedRefreshRate
                + ")";
@@ -252,6 +271,7 @@ public final class VirtualDisplayConfig implements Parcelable {
        mUniqueId = in.readString8();
        mDisplayIdToMirror = in.readInt();
        mWindowManagerMirroringEnabled = in.readBoolean();
        mContentRecordingSession = in.readTypedObject(ContentRecordingSession.CREATOR);
        mDisplayCategories = (ArraySet<String>) in.readArraySet(null);
        mRequestedRefreshRate = in.readFloat();
    }
@@ -283,6 +303,8 @@ public final class VirtualDisplayConfig implements Parcelable {
        private String mUniqueId = null;
        private int mDisplayIdToMirror = DEFAULT_DISPLAY;
        private boolean mWindowManagerMirroringEnabled = false;
        @Nullable
        private ContentRecordingSession mContentRecordingSession;
        private ArraySet<String> mDisplayCategories = new ArraySet<>();
        private float mRequestedRefreshRate = 0.0f;

@@ -374,6 +396,18 @@ public final class VirtualDisplayConfig implements Parcelable {
            return this;
        }

        /**
         * Sets the recording session associated with this {@link VirtualDisplay}. Only used for
         * recording via {@link MediaProjection}.
         *
         * @hide
         */
        @NonNull
        public Builder setContentRecordingSession(@Nullable ContentRecordingSession session) {
            mContentRecordingSession = session;
            return this;
        }

        /**
         * Sets the display categories.
         *
@@ -435,6 +469,7 @@ public final class VirtualDisplayConfig implements Parcelable {
                    mUniqueId,
                    mDisplayIdToMirror,
                    mWindowManagerMirroringEnabled,
                    mContentRecordingSession,
                    mDisplayCategories,
                    mRequestedRefreshRate);
        }
+3 −10
Original line number Diff line number Diff line
@@ -191,20 +191,13 @@ public final class MediaProjection {
            } else {
                session = ContentRecordingSession.createTaskSession(launchCookie);
            }
            // Pass in the current session details, so they are guaranteed to only be set in WMS
            // AFTER a VirtualDisplay is constructed (assuming there are no errors during set-up).
            virtualDisplayConfig.setContentRecordingSession(session);
            virtualDisplayConfig.setWindowManagerMirroringEnabled(true);
            final DisplayManager dm = mContext.getSystemService(DisplayManager.class);
            final VirtualDisplay virtualDisplay = dm.createVirtualDisplay(this,
                    virtualDisplayConfig.build(), callback, handler, windowContext);
            if (virtualDisplay == null) {
                // Since WM handling a new display and DM creating a new VirtualDisplay is async,
                // WM may have tried to start task recording and encountered an error that required
                // stopping recording entirely. The VirtualDisplay would then be null when the
                // MediaProjection is no longer active.
                return null;
            }
            session.setDisplayId(virtualDisplay.getDisplay().getDisplayId());
            // Successfully set up, so save the current session details.
            getProjectionService().setContentRecordingSession(session, mImpl);
            return virtualDisplay;
        } catch (RemoteException e) {
            // Can not capture if WMS is not accessible, so bail out.
+42 −4
Original line number Diff line number Diff line
@@ -128,6 +128,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.Spline;
import android.view.ContentRecordingSession;
import android.view.Display;
import android.view.DisplayEventReceiver;
import android.view.DisplayInfo;
@@ -250,6 +251,7 @@ public final class DisplayManagerService extends SystemService {
    private ActivityManagerInternal mActivityManagerInternal;
    private ActivityManager mActivityManager;
    private UidImportanceListener mUidImportanceListener = new UidImportanceListener();
    @Nullable
    private IMediaProjectionManager mProjectionService;
    private DeviceStateManagerInternal mDeviceStateManager;
    @GuardedBy("mSyncRoot")
@@ -1494,8 +1496,9 @@ public final class DisplayManagerService extends SystemService {

        final long token = Binder.clearCallingIdentity();
        try {
            final int displayId;
            synchronized (mSyncRoot) {
                final int displayId =
                displayId =
                        createVirtualDisplayLocked(
                                callback,
                                projection,
@@ -1509,8 +1512,39 @@ public final class DisplayManagerService extends SystemService {
                    mDisplayWindowPolicyControllers.put(
                            displayId, Pair.create(virtualDevice, dwpc));
                }
                return displayId;
            }

            // When calling setContentRecordingSession into the WindowManagerService, the WMS
            // attempts to acquire a lock before executing its main body. Due to this, we need
            // to be sure that it isn't called while the DisplayManagerService is also holding
            // a lock, to avoid a deadlock scenario.
            final ContentRecordingSession session =
                    virtualDisplayConfig.getContentRecordingSession();

            if (displayId != Display.INVALID_DISPLAY && session != null) {
                // Only attempt to set content recording session if there are details to set and a
                // VirtualDisplay has been successfully constructed.
                session.setDisplayId(displayId);

                // We set the content recording session here on the server side instead of using
                // a second AIDL call in MediaProjection. By ensuring that a virtual display has
                // been constructed before calling setContentRecordingSession, we avoid a race
                // condition between the DMS & WMS which could lead to the MediaProjection
                // being pre-emptively torn down.
                if (!mWindowManagerInternal.setContentRecordingSession(session)) {
                    // Unable to start mirroring, so tear down projection & release VirtualDisplay.
                    try {
                        getProjectionService().stopActiveProjection();
                    } catch (RemoteException e) {
                        Slog.e(TAG, "Unable to tell MediaProjectionManagerService to stop the "
                                + "active projection", e);
                    }
                    releaseVirtualDisplayInternal(callback.asBinder());
                    return Display.INVALID_DISPLAY;
                }
            }

            return displayId;
        } finally {
            Binder.restoreCallingIdentity(token);
        }
@@ -2804,8 +2838,7 @@ public final class DisplayManagerService extends SystemService {

    private IMediaProjectionManager getProjectionService() {
        if (mProjectionService == null) {
            IBinder b = ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE);
            mProjectionService = IMediaProjectionManager.Stub.asInterface(b);
            mProjectionService = mInjector.getProjectionService();
        }
        return mProjectionService;
    }
@@ -2964,6 +2997,11 @@ public final class DisplayManagerService extends SystemService {
        boolean getHdrOutputConversionSupport() {
            return DisplayControl.getHdrOutputConversionSupport();
        }

        IMediaProjectionManager getProjectionService() {
            IBinder b = ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE);
            return  IMediaProjectionManager.Stub.asInterface(b);
        }
    }

    @VisibleForTesting
+19 −2
Original line number Diff line number Diff line
@@ -88,7 +88,17 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
    // Called with SyncRoot lock held.
    public VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
            Context context, Handler handler, Listener listener) {
        this(syncRoot, context, handler, listener, DisplayControl::createDisplay);
        this(syncRoot, context, handler, listener, new SurfaceControlDisplayFactory() {
            @Override
            public IBinder createDisplay(String name, boolean secure, float requestedRefreshRate) {
                return DisplayControl.createDisplay(name, secure, requestedRefreshRate);
            }

            @Override
            public void destroyDisplay(IBinder displayToken) {
                DisplayControl.destroyDisplay(displayToken);
            }
        });
    }

    @VisibleForTesting
@@ -311,7 +321,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
                mSurface.release();
                mSurface = null;
            }
            DisplayControl.destroyDisplay(getDisplayTokenLocked());
            mSurfaceControlDisplayFactory.destroyDisplay(getDisplayTokenLocked());
            if (mProjection != null && mMediaProjectionCallback != null) {
                try {
                    mProjection.unregisterCallback(mMediaProjectionCallback);
@@ -653,5 +663,12 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
         * @return The token reference for the display in SurfaceFlinger.
         */
        IBinder createDisplay(String name, boolean secure, float requestedRefreshRate);
        
        /**
         * Destroy a display in SurfaceFlinger.
         *
         * @param displayToken The display token for the display to be destroyed.
         */
        void destroyDisplay(IBinder displayToken);
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -108,6 +108,7 @@
    <uses-permission android:name="android.permission.UPDATE_LOCK_TASK_PACKAGES" />
    <uses-permission android:name="android.permission.ACCESS_CONTEXT_HUB" />
    <uses-permission android:name="android.permission.USE_BIOMETRIC_INTERNAL" />
    <uses-permission android:name="android.permission.MANAGE_MEDIA_PROJECTION" />

    <queries>
        <package android:name="com.android.servicestests.apps.suspendtestapp" />
Loading