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

Commit f4f91bef authored by Piotr Wilczyński's avatar Piotr Wilczyński
Browse files

Limit the number of virtual displays

- general limit
- limit per package

Bug: 261791612
Flag: com.android.server.display.feature.flags.virtual_display_limit
Test: atest DisplayManagerServiceTest
Test: atest VirtualDisplayAdapterTest
Change-Id: I284e1a7a17934e1c8d3ba5ca1ddf6504f8adfe49
parent 0fc279fb
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -6916,6 +6916,14 @@
         entering the corresponding modes -->
    <string name="config_rearDisplayPhysicalAddress" translatable="false"></string>

    <!-- The maximum number of virtual displays that can exist at the same time.
         It must be >= 1. -->
    <integer name="config_virtualDisplayLimit">100</integer>

    <!-- The maximum number of virtual displays per package that can exist at the same time.
         It must be >= 1. -->
    <integer name="config_virtualDisplayLimitPerPackage">50</integer>

    <!-- List of certificate to be used for font fs-verity integrity verification -->
    <string-array translatable="false" name="config_fontManagerServiceCerts">
    </string-array>
+3 −0
Original line number Diff line number Diff line
@@ -5242,6 +5242,9 @@
  <java-symbol type="integer" name="config_deviceStateConcurrentRearDisplay" />
  <java-symbol type="string" name="config_rearDisplayPhysicalAddress" />

  <java-symbol type="integer" name="config_virtualDisplayLimit" />
  <java-symbol type="integer" name="config_virtualDisplayLimitPerPackage" />

  <!-- For app language picker -->
  <java-symbol type="string" name="system_locale_title" />
  <java-symbol type="layout" name="app_language_picker_system_default" />
+6 −5
Original line number Diff line number Diff line
@@ -1960,7 +1960,7 @@ public final class DisplayManagerService extends SystemService {
                        // handles stopping the projection.
                        Slog.w(TAG, "Content Recording: failed to start mirroring - "
                                + "releasing virtual display " + displayId);
                        releaseVirtualDisplayInternal(callback.asBinder());
                        releaseVirtualDisplayInternal(callback.asBinder(), callingUid);
                        return Display.INVALID_DISPLAY;
                    } else if (projection != null) {
                        // Indicate that this projection has been used to record, and can't be used
@@ -2049,7 +2049,7 @@ public final class DisplayManagerService extends SystemService {
        // Something weird happened and the logical display was not created.
        Slog.w(TAG, "Rejecting request to create virtual display "
                + "because the logical display was not created.");
        mVirtualDisplayAdapter.releaseVirtualDisplayLocked(callback.asBinder());
        mVirtualDisplayAdapter.releaseVirtualDisplayLocked(callback.asBinder(), callingUid);
        mDisplayDeviceRepo.onDisplayDeviceEvent(device,
                DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED);
        return -1;
@@ -2076,14 +2076,14 @@ public final class DisplayManagerService extends SystemService {
        }
    }

    private void releaseVirtualDisplayInternal(IBinder appToken) {
    private void releaseVirtualDisplayInternal(IBinder appToken, int callingUid) {
        synchronized (mSyncRoot) {
            if (mVirtualDisplayAdapter == null) {
                return;
            }

            DisplayDevice device =
                    mVirtualDisplayAdapter.releaseVirtualDisplayLocked(appToken);
                    mVirtualDisplayAdapter.releaseVirtualDisplayLocked(appToken, callingUid);
            Slog.d(TAG, "Virtual Display: Display Device released");
            if (device != null) {
                // TODO: multi-display - handle virtual displays the same as other display adapters.
@@ -4606,9 +4606,10 @@ public final class DisplayManagerService extends SystemService {

        @Override // Binder call
        public void releaseVirtualDisplay(IVirtualDisplayCallback callback) {
            final int callingUid = Binder.getCallingUid();
            final long token = Binder.clearCallingIdentity();
            try {
                releaseVirtualDisplayInternal(callback.asBinder());
                releaseVirtualDisplayInternal(callback.asBinder(), callingUid);
            } finally {
                Binder.restoreCallingIdentity(token);
            }
+72 −8
Original line number Diff line number Diff line
@@ -55,12 +55,14 @@ import android.os.RemoteException;
import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseIntArray;
import android.view.Display;
import android.view.DisplayCutout;
import android.view.DisplayShape;
import android.view.Surface;
import android.view.SurfaceControl;

import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.feature.DisplayManagerFlags;

@@ -85,6 +87,11 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
    private static final AtomicInteger sNextUniqueIndex = new AtomicInteger(0);

    private final ArrayMap<IBinder, VirtualDisplayDevice> mVirtualDisplayDevices = new ArrayMap<>();

    private final int mMaxDevices;
    private final int mMaxDevicesPerPackage;
    private final SparseIntArray mNoOfDevicesPerPackage = new SparseIntArray();

    private final Handler mHandler;
    private final SurfaceControlDisplayFactory mSurfaceControlDisplayFactory;

@@ -114,8 +121,31 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
        super(syncRoot, context, handler, listener, TAG, featureFlags);
        mHandler = handler;
        mSurfaceControlDisplayFactory = surfaceControlDisplayFactory;

        mMaxDevices = context.getResources().getInteger(R.integer.config_virtualDisplayLimit);
        if (mMaxDevices < 1) {
            throw new IllegalArgumentException("The limit of virtual displays must be >= 1");
        }
        mMaxDevicesPerPackage =
                context.getResources().getInteger(R.integer.config_virtualDisplayLimitPerPackage);
        if (mMaxDevicesPerPackage < 1) {
            throw new IllegalArgumentException(
                    "The limit of virtual displays per package must be >= 1");
        }
    }

    /**
     * Create a virtual display
     * @param callback The callback
     * @param projection The media projection
     * @param ownerUid The UID of the package creating a display
     * @param ownerPackageName The name of the package creating a display
     * @param uniqueId The unique ID of the display device
     * @param surface The surface
     * @param flags The flags
     * @param virtualDisplayConfig The config
     * @return The display device created
     */
    public DisplayDevice createVirtualDisplayLocked(IVirtualDisplayCallback callback,
            IMediaProjection projection, int ownerUid, String ownerPackageName, String uniqueId,
            Surface surface, int flags, VirtualDisplayConfig virtualDisplayConfig) {
@@ -126,6 +156,22 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
            return null;
        }

        if (getFeatureFlags().isVirtualDisplayLimitEnabled()
                && mVirtualDisplayDevices.size() >= mMaxDevices) {
            Slog.w(TAG, "Rejecting request to create private virtual display because "
                    + mMaxDevices + " devices already exist.");
            return null;
        }

        int noOfDevices = mNoOfDevicesPerPackage.get(ownerUid, /* valueIfKeyNotFound= */ 0);
        if (getFeatureFlags().isVirtualDisplayLimitEnabled()
                && noOfDevices >= mMaxDevicesPerPackage) {
            Slog.w(TAG, "Rejecting request to create private virtual display because "
                    + mMaxDevicesPerPackage + " devices already exist for package "
                    + ownerPackageName + ".");
            return null;
        }

        String name = virtualDisplayConfig.getName();
        boolean secure = (flags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0;

@@ -140,6 +186,9 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
                projection, mediaProjectionCallback, uniqueId, virtualDisplayConfig);

        mVirtualDisplayDevices.put(appToken, device);
        if (getFeatureFlags().isVirtualDisplayLimitEnabled()) {
            mNoOfDevicesPerPackage.put(ownerUid, noOfDevices + 1);
        }

        try {
            if (projection != null) {
@@ -150,7 +199,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
            appToken.linkToDeath(device, 0);
        } catch (RemoteException ex) {
            Slog.e(TAG, "Virtual Display: error while setting up VirtualDisplayDevice", ex);
            mVirtualDisplayDevices.remove(appToken);
            removeVirtualDisplayDeviceLocked(appToken, ownerUid);
            device.destroyLocked(false);
            return null;
        }
@@ -194,8 +243,15 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
        }
    }

    public DisplayDevice releaseVirtualDisplayLocked(IBinder appToken) {
        VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken);
    /**
     * Release a virtual display that was previously created
     * @param appToken The token to identify the display
     * @param ownerUid The UID of the package, used to keep track of and limit the number of
     *                 displays created per package
     * @return The display device that has been removed
     */
    public DisplayDevice releaseVirtualDisplayLocked(IBinder appToken, int ownerUid) {
        VirtualDisplayDevice device = removeVirtualDisplayDeviceLocked(appToken, ownerUid);
        if (device != null) {
            Slog.v(TAG, "Release VirtualDisplay " + device.mName);
            device.destroyLocked(true);
@@ -235,10 +291,6 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
                : ("," + uid + "," + config.getName() + "," + sNextUniqueIndex.getAndIncrement()));
    }

    private void handleBinderDiedLocked(IBinder appToken) {
        mVirtualDisplayDevices.remove(appToken);
    }

    private void handleMediaProjectionStoppedLocked(IBinder appToken) {
        VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
        if (device != null) {
@@ -248,6 +300,18 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
        }
    }

    private VirtualDisplayDevice removeVirtualDisplayDeviceLocked(IBinder appToken, int ownerUid) {
        int noOfDevices = mNoOfDevicesPerPackage.get(ownerUid, /* valueIfKeyNotFound= */ 0);
        if (getFeatureFlags().isVirtualDisplayLimitEnabled()) {
            if (noOfDevices <= 1) {
                mNoOfDevicesPerPackage.delete(ownerUid);
            } else {
                mNoOfDevicesPerPackage.put(ownerUid, noOfDevices - 1);
            }
        }
        return mVirtualDisplayDevices.remove(appToken);
    }

    private final class VirtualDisplayDevice extends DisplayDevice implements DeathRecipient {
        private static final int PENDING_SURFACE_CHANGE = 0x01;
        private static final int PENDING_RESIZE = 0x02;
@@ -309,7 +373,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
        @Override
        public void binderDied() {
            synchronized (getSyncRoot()) {
                handleBinderDiedLocked(mAppToken);
                removeVirtualDisplayDeviceLocked(mAppToken, mOwnerUid);
                Slog.i(TAG, "Virtual display device released because application token died: "
                    + mOwnerPackageName);
                destroyLocked(false);
+10 −0
Original line number Diff line number Diff line
@@ -199,6 +199,11 @@ public class DisplayManagerFlags {
            Flags.FLAG_IDLE_SCREEN_CONFIG_IN_SUBSCRIBING_LIGHT_SENSOR,
            Flags::idleScreenConfigInSubscribingLightSensor);

    private final FlagState mVirtualDisplayLimit =
            new FlagState(
                    Flags.FLAG_VIRTUAL_DISPLAY_LIMIT,
                    Flags::virtualDisplayLimit);

    private final FlagState mNormalBrightnessForDozeParameter = new FlagState(
            Flags.FLAG_NORMAL_BRIGHTNESS_FOR_DOZE_PARAMETER,
            Flags::normalBrightnessForDozeParameter
@@ -412,6 +417,10 @@ public class DisplayManagerFlags {
        return mNewHdrBrightnessModifier.isEnabled();
    }

    public boolean isVirtualDisplayLimitEnabled() {
        return mVirtualDisplayLimit.isEnabled();
    }

    /**
     * @return Whether the useDozeBrightness parameter should be used
     */
@@ -476,6 +485,7 @@ public class DisplayManagerFlags {
        pw.println(" " + mOffloadDozeOverrideHoldsWakelock);
        pw.println(" " + mOffloadSessionCancelBlockScreenOn);
        pw.println(" " + mNewHdrBrightnessModifier);
        pw.println(" " + mVirtualDisplayLimit);
        pw.println(" " + mNormalBrightnessForDozeParameter);
        pw.println(" " + mIdleScreenConfigInSubscribingLightSensor);
        pw.println(" " + mEnableBatteryStatsForAllDisplays);
Loading