From 64123f9899ad745e8c7af9900153b8a679a9d981 Mon Sep 17 00:00:00 2001 From: Chilun Date: Thu, 2 Dec 2021 01:42:37 +0800 Subject: [PATCH] Add createVirtualDisplay in DisplayManager for VirtualDeviceManager Bug: 201712607 Test: atest DisplayMnagerServiceTest Manual Change-Id: I161f281fc01efb03b0c2544ae2cefb1b89804629 --- .../hardware/display/DisplayManager.java | 13 +++- .../display/DisplayManagerGlobal.java | 7 +- .../display/DisplayManagerInternal.java | 26 ------- .../hardware/display/IDisplayManager.aidl | 5 +- .../virtual/VirtualDeviceManagerService.java | 53 ++++++++++++- .../virtual/VirtualDeviceManagerInternal.java | 28 +++++++ .../server/display/DisplayManagerService.java | 77 ++++++++++++------- .../display/DisplayManagerServiceTest.java | 15 ++-- 8 files changed, 156 insertions(+), 68 deletions(-) rename services/{companion => core}/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java (55%) diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 4c81f9cd38081..00374644d72c1 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -30,6 +30,7 @@ import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.app.KeyguardManager; +import android.companion.virtual.IVirtualDevice; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.Resources; @@ -902,8 +903,16 @@ public final class DisplayManager { @NonNull VirtualDisplayConfig virtualDisplayConfig, @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler, @Nullable Context windowContext) { - return mGlobal.createVirtualDisplay(mContext, projection, virtualDisplayConfig, callback, - handler, windowContext); + return mGlobal.createVirtualDisplay(mContext, projection, null /* virtualDevice */, + virtualDisplayConfig, callback, handler, windowContext); + } + + /** @hide */ + public VirtualDisplay createVirtualDisplay(@Nullable IVirtualDevice virtualDevice, + @NonNull VirtualDisplayConfig virtualDisplayConfig, + @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) { + return mGlobal.createVirtualDisplay(mContext, null /* projection */, virtualDevice, + virtualDisplayConfig, callback, handler, null); } /** diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 75155bbab0c88..01833fda5b95f 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -24,6 +24,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.PropertyInvalidatedCache; +import android.companion.virtual.IVirtualDevice; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.ParceledListSlice; @@ -582,14 +583,14 @@ public final class DisplayManagerGlobal { } public VirtualDisplay createVirtualDisplay(@NonNull Context context, MediaProjection projection, - @NonNull VirtualDisplayConfig virtualDisplayConfig, VirtualDisplay.Callback callback, - Handler handler, @Nullable Context windowContext) { + IVirtualDevice virtualDevice, @NonNull VirtualDisplayConfig virtualDisplayConfig, + VirtualDisplay.Callback callback, Handler handler, @Nullable Context windowContext) { VirtualDisplayCallback callbackWrapper = new VirtualDisplayCallback(callback, handler); IMediaProjection projectionToken = projection != null ? projection.getProjection() : null; int displayId; try { displayId = mDm.createVirtualDisplay(virtualDisplayConfig, callbackWrapper, - projectionToken, context.getPackageName()); + projectionToken, virtualDevice, context.getPackageName()); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 2985c754ac45f..83e1061d81436 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -20,7 +20,6 @@ import android.annotation.IntDef; import android.annotation.Nullable; import android.graphics.Point; import android.hardware.SensorManager; -import android.media.projection.IMediaProjection; import android.os.Handler; import android.os.IBinder; import android.os.PowerManager; @@ -380,31 +379,6 @@ public abstract class DisplayManagerInternal { */ public abstract void onEarlyInteractivityChange(boolean interactive); - /** - * A special API for creates a virtual display with a DisplayPolicyController in system_server. - *

- * If this method is called without original calling uid, the caller must enforce the - * corresponding permissions according to the flags. - * {@link android.Manifest.permission#CAPTURE_VIDEO_OUTPUT} - * {@link android.Manifest.permission#CAPTURE_SECURE_VIDEO_OUTPUT} - * {@link android.Manifest.permission#ADD_TRUSTED_DISPLAY} - * {@link android.Manifest.permission#INTERNAL_SYSTEM_WINDOW} - *

- * - * @param virtualDisplayConfig The arguments for the virtual display configuration. See - * {@link VirtualDisplayConfig} for using it. - * @param callback Callback to call when the virtual display's state changes, or null if none. - * @param projection MediaProjection token. - * @param packageName The package name of the app. - * @param controller The DisplayWindowPolicyControl that can control what contents are - * allowed to be displayed. - * @return The newly created virtual display id , or {@link Display#INVALID_DISPLAY} if the - * virtual display cannot be created. - */ - public abstract int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig, - IVirtualDisplayCallback callback, IMediaProjection projection, String packageName, - DisplayWindowPolicyController controller); - /** * Get {@link DisplayWindowPolicyController} associated to the {@link DisplayInfo#displayId} * diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 0d5f1af727ae8..82b31d48d5feb 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -16,6 +16,7 @@ package android.hardware.display; +import android.companion.virtual.IVirtualDevice; import android.content.pm.ParceledListSlice; import android.graphics.Point; import android.hardware.display.BrightnessConfiguration; @@ -86,10 +87,10 @@ interface IDisplayManager { void requestColorMode(int displayId, int colorMode); // Requires CAPTURE_VIDEO_OUTPUT, CAPTURE_SECURE_VIDEO_OUTPUT, or an appropriate - // MediaProjection token for certain combinations of flags. + // MediaProjection token or VirtualDevice for certain combinations of flags. int createVirtualDisplay(in VirtualDisplayConfig virtualDisplayConfig, in IVirtualDisplayCallback callback, in IMediaProjection projectionToken, - String packageName); + in IVirtualDevice virtualDevice, String packageName); // No permissions required, but must be same Uid as the creator. void resizeVirtualDisplay(in IVirtualDisplayCallback token, diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java index 8592c05d9ea57..020d08f77642b 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java @@ -31,6 +31,7 @@ import android.os.RemoteException; import android.util.ExceptionUtils; import android.util.Slog; import android.util.SparseArray; +import android.window.DisplayWindowPolicyController; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.DumpUtils; @@ -38,11 +39,11 @@ import com.android.server.SystemService; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; -/** @hide */ @SuppressLint("LongLogTag") public class VirtualDeviceManagerService extends SystemService { @@ -84,6 +85,15 @@ public class VirtualDeviceManagerService extends SystemService { publishLocalService(VirtualDeviceManagerInternal.class, new LocalService()); } + @GuardedBy("mVirtualDeviceManagerLock") + private boolean isValidVirtualDeviceLocked(IVirtualDevice virtualDevice) { + try { + return mVirtualDevices.contains(virtualDevice.getAssociationId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + @Override public void onUserStarting(@NonNull TargetUser user) { super.onUserStarting(user); @@ -121,6 +131,7 @@ public class VirtualDeviceManagerService extends SystemService { private final AssociationInfo mAssociationInfo; private final int mOwnerUid; + private final ArrayList mDisplayIds = new ArrayList<>(); private VirtualDeviceImpl(int ownerUid, IBinder token, AssociationInfo associationInfo) { mOwnerUid = ownerUid; @@ -149,6 +160,24 @@ public class VirtualDeviceManagerService extends SystemService { public void binderDied() { close(); } + + DisplayWindowPolicyController onVirtualDisplayCreatedLocked(int displayId) { + if (mDisplayIds.contains(displayId)) { + throw new IllegalStateException( + "Virtual device already have a virtual display with ID " + displayId); + } + mDisplayIds.add(displayId); + // TODO(b/201712607): Return the corresponding DisplayWindowPolicyController. + return null; + } + + void onVirtualDisplayRemovedLocked(int displayId) { + if (!mDisplayIds.contains(displayId)) { + throw new IllegalStateException( + "Virtual device doesn't have a virtual display with ID " + displayId); + } + mDisplayIds.remove(displayId); + } } class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub { @@ -229,6 +258,28 @@ public class VirtualDeviceManagerService extends SystemService { private final class LocalService extends VirtualDeviceManagerInternal { + @Override + public boolean isValidVirtualDevice(IVirtualDevice virtualDevice) { + synchronized (mVirtualDeviceManagerLock) { + return isValidVirtualDeviceLocked(virtualDevice); + } + } + + @Override + public DisplayWindowPolicyController onVirtualDisplayCreated(IVirtualDevice virtualDevice, + int displayId) { + synchronized (mVirtualDeviceManagerLock) { + return ((VirtualDeviceImpl) virtualDevice).onVirtualDisplayCreatedLocked(displayId); + } + } + + @Override + public void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId) { + synchronized (mVirtualDeviceManagerLock) { + ((VirtualDeviceImpl) virtualDevice).onVirtualDisplayRemovedLocked(displayId); + } + } + @Override public boolean isAppOwnerOfAnyVirtualDevice(int uid) { synchronized (mVirtualDeviceManagerLock) { diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java similarity index 55% rename from services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java rename to services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java index ee09832dc7e23..39fa3f200aab0 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java +++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java @@ -16,11 +16,39 @@ package com.android.server.companion.virtual; +import android.companion.virtual.IVirtualDevice; +import android.window.DisplayWindowPolicyController; + /** * Virtual device manager local service interface. + * Only for use within system server. */ public abstract class VirtualDeviceManagerInternal { + /** + * Validate the virtual device. + */ + public abstract boolean isValidVirtualDevice(IVirtualDevice virtualDevice); + + /** + * Notify a virtual display is created. + * + * @param virtualDevice The virtual device where the virtual display located. + * @param displayId The display id of the created virtual display. + * + * @return The {@link DisplayWindowPolicyController} of the virtual device. + */ + public abstract DisplayWindowPolicyController onVirtualDisplayCreated( + IVirtualDevice virtualDevice, int displayId); + + /** + * Notify a virtual display is removed. + * + * @param virtualDevice The virtual device where the virtual display located. + * @param displayId The display id of the removed virtual display. + */ + public abstract void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId); + /** * Returns true if the given {@code uid} is the owner of any virtual devices that are * currently active. diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 77ab81301059e..3f55848e26a85 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -42,6 +42,7 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.app.compat.CompatChanges; +import android.companion.virtual.IVirtualDevice; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.content.Context; @@ -126,6 +127,7 @@ import com.android.server.DisplayThread; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.UiThread; +import com.android.server.companion.virtual.VirtualDeviceManagerInternal; import com.android.server.display.DisplayDeviceConfig.SensorData; import com.android.server.display.utils.SensorUtils; import com.android.server.wm.SurfaceAnimationThread; @@ -244,9 +246,12 @@ public final class DisplayManagerService extends SystemService { public final SparseArray mCallbacks = new SparseArray(); - /** All {@link DisplayWindowPolicyController}s indexed by {@link DisplayInfo#displayId}. */ - final SparseArray mDisplayWindowPolicyController = - new SparseArray<>(); + /** + * All {@link IVirtualDevice} and {@link DisplayWindowPolicyController}s indexed by + * {@link DisplayInfo#displayId}. + */ + final SparseArray> + mDisplayWindowPolicyControllers = new SparseArray<>(); // List of all currently registered display adapters. private final ArrayList mDisplayAdapters = new ArrayList(); @@ -1180,8 +1185,8 @@ public final class DisplayManagerService extends SystemService { } private int createVirtualDisplayInternal(VirtualDisplayConfig virtualDisplayConfig, - IVirtualDisplayCallback callback, IMediaProjection projection, String packageName, - DisplayWindowPolicyController controller) { + IVirtualDisplayCallback callback, IMediaProjection projection, + IVirtualDevice virtualDevice, String packageName) { final int callingUid = Binder.getCallingUid(); if (!validatePackageName(callingUid, packageName)) { throw new SecurityException("packageName must match the calling uid"); @@ -1226,6 +1231,14 @@ public final class DisplayManagerService extends SystemService { } } + if (virtualDevice != null) { + final VirtualDeviceManagerInternal vdm = + getLocalService(VirtualDeviceManagerInternal.class); + if (!vdm.isValidVirtualDevice(virtualDevice)) { + throw new SecurityException("Invalid virtual device"); + } + } + if (callingUid != Process.SYSTEM_UID && (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) { if (!canProjectVideo(projection)) { @@ -1306,8 +1319,8 @@ public final class DisplayManagerService extends SystemService { final long token = Binder.clearCallingIdentity(); try { synchronized (mSyncRoot) { - return createVirtualDisplayLocked(callback, projection, callingUid, packageName, - surface, flags, virtualDisplayConfig, controller); + return createVirtualDisplayLocked(callback, projection, virtualDevice, callingUid, + packageName, surface, flags, virtualDisplayConfig); } } finally { Binder.restoreCallingIdentity(token); @@ -1315,9 +1328,9 @@ public final class DisplayManagerService extends SystemService { } private int createVirtualDisplayLocked(IVirtualDisplayCallback callback, - IMediaProjection projection, int callingUid, String packageName, Surface surface, - int flags, VirtualDisplayConfig virtualDisplayConfig, - DisplayWindowPolicyController controller) { + IMediaProjection projection, IVirtualDevice virtualDevice, + int callingUid, String packageName, Surface surface, + int flags, VirtualDisplayConfig virtualDisplayConfig) { if (mVirtualDisplayAdapter == null) { Slog.w(TAG, "Rejecting request to create private virtual display " + "because the virtual display adapter is not available."); @@ -1344,10 +1357,16 @@ public final class DisplayManagerService extends SystemService { final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device); if (display != null) { - if (controller != null) { - mDisplayWindowPolicyController.put(display.getDisplayIdLocked(), controller); + final int displayId = display.getDisplayIdLocked(); + if (virtualDevice != null) { + final VirtualDeviceManagerInternal vdm = + getLocalService(VirtualDeviceManagerInternal.class); + final DisplayWindowPolicyController controller = + vdm.onVirtualDisplayCreated(virtualDevice, displayId); + mDisplayWindowPolicyControllers.put(displayId, + Pair.create(virtualDevice, controller)); } - return display.getDisplayIdLocked(); + return displayId; } // Something weird happened and the logical display was not created. @@ -1391,7 +1410,13 @@ public final class DisplayManagerService extends SystemService { if (device != null) { final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device); if (display != null) { - mDisplayWindowPolicyController.delete(display.getDisplayIdLocked()); + final int displayId = display.getDisplayIdLocked(); + if (mDisplayWindowPolicyControllers.contains(displayId)) { + Pair pair = + mDisplayWindowPolicyControllers.removeReturnOld(displayId); + getLocalService(VirtualDeviceManagerInternal.class) + .onVirtualDisplayRemoved(pair.first, displayId); + } } // TODO: multi-display - handle virtual displays the same as other display adapters. mDisplayDeviceRepo.onDisplayDeviceEvent(device, @@ -2345,13 +2370,13 @@ public final class DisplayManagerService extends SystemService { pw.println(); mPersistentDataStore.dump(pw); - final int displayWindowPolicyControllerCount = mDisplayWindowPolicyController.size(); + final int displayWindowPolicyControllerCount = mDisplayWindowPolicyControllers.size(); pw.println(); pw.println("Display Window Policy Controllers: size=" + displayWindowPolicyControllerCount); for (int i = 0; i < displayWindowPolicyControllerCount; i++) { - pw.print("Display " + mDisplayWindowPolicyController.keyAt(i) + ":"); - mDisplayWindowPolicyController.valueAt(i).dump(" ", pw); + pw.print("Display " + mDisplayWindowPolicyControllers.keyAt(i) + ":"); + mDisplayWindowPolicyControllers.valueAt(i).second.dump(" ", pw); } } pw.println(); @@ -2922,9 +2947,10 @@ public final class DisplayManagerService extends SystemService { @Override // Binder call public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig, - IVirtualDisplayCallback callback, IMediaProjection projection, String packageName) { + IVirtualDisplayCallback callback, IMediaProjection projection, + IVirtualDevice virtualDeviceToken, String packageName) { return createVirtualDisplayInternal(virtualDisplayConfig, callback, projection, - packageName, null /* controller */); + virtualDeviceToken, packageName); } @Override // Binder call @@ -3689,18 +3715,13 @@ public final class DisplayManagerService extends SystemService { mLogicalDisplayMapper.onEarlyInteractivityChange(interactive); } - @Override - public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig, - IVirtualDisplayCallback callback, IMediaProjection projection, String packageName, - DisplayWindowPolicyController controller) { - return createVirtualDisplayInternal(virtualDisplayConfig, callback, projection, - packageName, controller); - } - @Override public DisplayWindowPolicyController getDisplayWindowPolicyController(int displayId) { synchronized (mSyncRoot) { - return mDisplayWindowPolicyController.get(displayId); + if (mDisplayWindowPolicyControllers.contains(displayId)) { + return mDisplayWindowPolicyControllers.get(displayId).second; + } + return null; } } } diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java index 68e90fbadd0c3..eaa271a22725d 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -219,7 +219,7 @@ public class DisplayManagerServiceTest { builder.setUniqueId(uniqueId); builder.setFlags(flags); int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */, - null /* projection */, PACKAGE_NAME); + null /* projection */, null /* virtualDeviceToken */, PACKAGE_NAME); displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); @@ -341,7 +341,7 @@ public class DisplayManagerServiceTest { builder.setFlags(flags); builder.setUniqueId(uniqueId); int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */, - null /* projection */, PACKAGE_NAME); + null /* projection */, null /* virtualDeviceToken */, PACKAGE_NAME); displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); @@ -580,7 +580,8 @@ public class DisplayManagerServiceTest { VIRTUAL_DISPLAY_NAME, width, height, dpi); builder.setUniqueId(uniqueId); final int firstDisplayId = binderService.createVirtualDisplay(builder.build(), - mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME); + mMockAppToken /* callback */, null /* projection */, null /* virtualDeviceToken */, + PACKAGE_NAME); // The second virtual display requests to mirror the first virtual display. final String uniqueId2 = "uniqueId --- displayIdToMirrorTest #2"; @@ -590,7 +591,8 @@ public class DisplayManagerServiceTest { builder2.setUniqueId(uniqueId2); builder2.setDisplayIdToMirror(firstDisplayId); final int secondDisplayId = binderService.createVirtualDisplay(builder2.build(), - mMockAppToken2 /* callback */, null /* projection */, PACKAGE_NAME); + mMockAppToken2 /* callback */, null /* projection */, + null /* virtualDeviceToken */, PACKAGE_NAME); displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); // flush the handler @@ -628,7 +630,8 @@ public class DisplayManagerServiceTest { builder.setSurface(surface); builder.setUniqueId(uniqueId); final int displayId = binderService.createVirtualDisplay(builder.build(), - mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME); + mMockAppToken /* callback */, null /* projection */, null /* virtualDeviceToken */, + PACKAGE_NAME); displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); @@ -1108,7 +1111,7 @@ public class DisplayManagerServiceTest { builder.setUniqueId(uniqueId); int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */, - null /* projection */, PACKAGE_NAME); + null /* projection */, null /* virtualDeviceToken */, PACKAGE_NAME); displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */); DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayId);