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

Commit 4d96c1ff authored by Pierre Barbier de Reuille's avatar Pierre Barbier de Reuille
Browse files

Add APIs to control the external display state

External displays start disabled, signal connection / disconnection and
can be enabled / disabled by system apps.

The implementation of the signalling is mostly in LogicalDisplayMapper.

The flags are accessed through DisplayManagerFlags, which is injected
into DisplayManagerService. All other uses come from
DisplayManagerService and is propagated. This allows for injection in
tests.

Currently, we are using a standard flag, but if DeviceConfig is not
ready, we instead read a default static value. The value cannot change
during the runtime of the device.

When read only flags are ready, we can simplify the code to remove the
default value, as the flag will always be available.

To test on a local device, set
`DisplayManagerFlags.DEFAULT_IS_CONNECTED_DISPLAY_MANAGEMENT_ENABLED` to true.

Bug: 280739508
Test: atest LocalDisplayAdapterTest
Test: atest DisplayManagerServiceTest
Change-Id: I91411a8e726982d88474cf2ecd0f423bde9be107
parent d2cd2b64
Loading
Loading
Loading
Loading
+46 −1
Original line number Original line Diff line number Diff line
@@ -554,7 +554,8 @@ public final class DisplayManager {
            EVENT_FLAG_DISPLAY_CHANGED,
            EVENT_FLAG_DISPLAY_CHANGED,
            EVENT_FLAG_DISPLAY_REMOVED,
            EVENT_FLAG_DISPLAY_REMOVED,
            EVENT_FLAG_DISPLAY_BRIGHTNESS,
            EVENT_FLAG_DISPLAY_BRIGHTNESS,
            EVENT_FLAG_HDR_SDR_RATIO_CHANGED
            EVENT_FLAG_HDR_SDR_RATIO_CHANGED,
            EVENT_FLAG_DISPLAY_CONNECTION_CHANGED,
    })
    })
    @Retention(RetentionPolicy.SOURCE)
    @Retention(RetentionPolicy.SOURCE)
    public @interface EventsMask {}
    public @interface EventsMask {}
@@ -610,6 +611,13 @@ public final class DisplayManager {
     */
     */
    public static final long EVENT_FLAG_HDR_SDR_RATIO_CHANGED = 1L << 4;
    public static final long EVENT_FLAG_HDR_SDR_RATIO_CHANGED = 1L << 4;


    /**
     * Event flag to register for a display's connection changed.
     *
     * @hide
     */
    public static final long EVENT_FLAG_DISPLAY_CONNECTION_CHANGED = 1L << 5;

    /** @hide */
    /** @hide */
    public DisplayManager(Context context) {
    public DisplayManager(Context context) {
        mContext = context;
        mContext = context;
@@ -890,6 +898,25 @@ public final class DisplayManager {
        return mGlobal.getWifiDisplayStatus();
        return mGlobal.getWifiDisplayStatus();
    }
    }


    /**
     * Enable a connected display that is currently disabled.
     * @hide
     */
    @RequiresPermission("android.permission.MANAGE_DISPLAYS")
    public void enableConnectedDisplay(int displayId) {
        mGlobal.enableConnectedDisplay(displayId);
    }


    /**
     * Disable a connected display that is currently enabled.
     * @hide
     */
    @RequiresPermission("android.permission.MANAGE_DISPLAYS")
    public void disableConnectedDisplay(int displayId) {
        mGlobal.disableConnectedDisplay(displayId);
    }

    /**
    /**
     * Set the level of color saturation to apply to the display.
     * Set the level of color saturation to apply to the display.
     * @param level The amount of saturation to apply, between 0 and 1 inclusive.
     * @param level The amount of saturation to apply, between 0 and 1 inclusive.
@@ -1633,6 +1660,24 @@ public final class DisplayManager {
         * @param displayId The id of the logical display that changed.
         * @param displayId The id of the logical display that changed.
         */
         */
        void onDisplayChanged(int displayId);
        void onDisplayChanged(int displayId);

        /**
         * Called when a display is connected, but not necessarily used.
         *
         * A display is always connected before being added.
         * @hide
         */
        default void onDisplayConnected(int displayId) { }

        /**
         * Called when a display is disconnected.
         *
         * If a display was added, a display is only disconnected after it has been removed. Note,
         * however, that the display may have been disconnected by the time the removed event is
         * received by the listener.
         * @hide
         */
        default void onDisplayDisconnected(int displayId) { }
    }
    }


    /**
    /**
+48 −0
Original line number Original line Diff line number Diff line
@@ -90,6 +90,8 @@ public final class DisplayManagerGlobal {
            EVENT_DISPLAY_REMOVED,
            EVENT_DISPLAY_REMOVED,
            EVENT_DISPLAY_BRIGHTNESS_CHANGED,
            EVENT_DISPLAY_BRIGHTNESS_CHANGED,
            EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED,
            EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED,
            EVENT_DISPLAY_CONNECTED,
            EVENT_DISPLAY_DISCONNECTED,
    })
    })
    @Retention(RetentionPolicy.SOURCE)
    @Retention(RetentionPolicy.SOURCE)
    public @interface DisplayEvent {}
    public @interface DisplayEvent {}
@@ -99,6 +101,8 @@ public final class DisplayManagerGlobal {
    public static final int EVENT_DISPLAY_REMOVED = 3;
    public static final int EVENT_DISPLAY_REMOVED = 3;
    public static final int EVENT_DISPLAY_BRIGHTNESS_CHANGED = 4;
    public static final int EVENT_DISPLAY_BRIGHTNESS_CHANGED = 4;
    public static final int EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED = 5;
    public static final int EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED = 5;
    public static final int EVENT_DISPLAY_CONNECTED = 6;
    public static final int EVENT_DISPLAY_DISCONNECTED = 7;


    @UnsupportedAppUsage
    @UnsupportedAppUsage
    private static DisplayManagerGlobal sInstance;
    private static DisplayManagerGlobal sInstance;
@@ -414,6 +418,9 @@ public final class DisplayManagerGlobal {


    private void updateCallbackIfNeededLocked() {
    private void updateCallbackIfNeededLocked() {
        int mask = calculateEventsMaskLocked();
        int mask = calculateEventsMaskLocked();
        if (DEBUG) {
            Log.d(TAG, "Mask for listener: " + mask);
        }
        if (mask != mRegisteredEventsMask) {
        if (mask != mRegisteredEventsMask) {
            try {
            try {
                mDm.registerCallbackWithEventMask(mCallback, mask);
                mDm.registerCallbackWithEventMask(mCallback, mask);
@@ -459,6 +466,33 @@ public final class DisplayManagerGlobal {
        }
        }
    }
    }


    /**
     * Enable a connected display that is currently disabled.
     * @hide
     */
    @RequiresPermission("android.permission.MANAGE_DISPLAYS")
    public void enableConnectedDisplay(int displayId) {
        try {
            mDm.enableConnectedDisplay(displayId);
        } catch (RemoteException ex) {
            Log.e(TAG, "Error trying to enable external display", ex);
        }
    }


    /**
     * Disable a connected display that is currently enabled.
     * @hide
     */
    @RequiresPermission("android.permission.MANAGE_DISPLAYS")
    public void disableConnectedDisplay(int displayId) {
        try {
            mDm.disableConnectedDisplay(displayId);
        } catch (RemoteException ex) {
            Log.e(TAG, "Error trying to enable external display", ex);
        }
    }

    public void startWifiDisplayScan() {
    public void startWifiDisplayScan() {
        synchronized (mLock) {
        synchronized (mLock) {
            if (mWifiDisplayScanNestCount++ == 0) {
            if (mWifiDisplayScanNestCount++ == 0) {
@@ -1179,6 +1213,16 @@ public final class DisplayManagerGlobal {
                        mListener.onDisplayChanged(msg.arg1);
                        mListener.onDisplayChanged(msg.arg1);
                    }
                    }
                    break;
                    break;
                case EVENT_DISPLAY_CONNECTED:
                    if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0) {
                        mListener.onDisplayConnected(msg.arg1);
                    }
                    break;
                case EVENT_DISPLAY_DISCONNECTED:
                    if ((mEventsMask & DisplayManager.EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0) {
                        mListener.onDisplayDisconnected(msg.arg1);
                    }
                    break;
            }
            }
            if (DEBUG) {
            if (DEBUG) {
                Trace.endSection();
                Trace.endSection();
@@ -1302,6 +1346,10 @@ public final class DisplayManagerGlobal {
                return "BRIGHTNESS_CHANGED";
                return "BRIGHTNESS_CHANGED";
            case EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED:
            case EVENT_DISPLAY_HDR_SDR_RATIO_CHANGED:
                return "HDR_SDR_RATIO_CHANGED";
                return "HDR_SDR_RATIO_CHANGED";
            case EVENT_DISPLAY_CONNECTED:
                return "EVENT_DISPLAY_CONNECTED";
            case EVENT_DISPLAY_DISCONNECTED:
                return "EVENT_DISPLAY_DISCONNECTED";
        }
        }
        return "UNKNOWN";
        return "UNKNOWN";
    }
    }
+8 −0
Original line number Original line Diff line number Diff line
@@ -227,4 +227,12 @@ interface IDisplayManager {


    // Query overlay properties of the device
    // Query overlay properties of the device
    OverlayProperties getOverlaySupport();
    OverlayProperties getOverlaySupport();

    // Enable a connected display that is disabled.
    @EnforcePermission("MANAGE_DISPLAYS")
    void enableConnectedDisplay(int displayId);

    // Disable a connected display that is enabled.
    @EnforcePermission("MANAGE_DISPLAYS")
    void disableConnectedDisplay(int displayId);
}
}
+9 −0
Original line number Original line Diff line number Diff line
@@ -7706,6 +7706,15 @@
    <permission android:name="android.permission.WRITE_FLAGS"
    <permission android:name="android.permission.WRITE_FLAGS"
        android:protectionLevel="signature" />
        android:protectionLevel="signature" />


    <!-- @hide Allows internal applications to manage displays.
        <p>This means intercept internal signals about displays being (dis-)connected
        and being able to enable or disable the external displays.
        <p>Not for use by third-party applications.
        <p>Protection level: signature
    -->
    <permission android:name="android.permission.MANAGE_DISPLAYS"
        android:protectionLevel="signature" />

    <!-- Attribution for Geofencing service. -->
    <!-- Attribution for Geofencing service. -->
    <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
    <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/>
    <!-- Attribution for Country Detector. -->
    <!-- Attribution for Country Detector. -->
+2 −0
Original line number Original line Diff line number Diff line
@@ -846,6 +846,8 @@
    <uses-permission android:name="android.permission.LAUNCH_CREDENTIAL_SELECTOR" />
    <uses-permission android:name="android.permission.LAUNCH_CREDENTIAL_SELECTOR" />
    <!-- Permission required for CTS test IntentRedirectionTest -->
    <!-- Permission required for CTS test IntentRedirectionTest -->
    <uses-permission android:name="android.permission.QUERY_CLONED_APPS" />
    <uses-permission android:name="android.permission.QUERY_CLONED_APPS" />
    <!-- Permission required for adb display commands `enable-display` and `disable-display`. -->
    <uses-permission android:name="android.permission.MANAGE_DISPLAYS" />
    <!-- Permission required for accessing all content provider mime types -->
    <!-- Permission required for accessing all content provider mime types -->
    <uses-permission android:name="android.permission.GET_ANY_PROVIDER_TYPE" />
    <uses-permission android:name="android.permission.GET_ANY_PROVIDER_TYPE" />


Loading