diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java index 64f16ac3e1b3a..6b53ddceaae7d 100644 --- a/core/java/android/companion/virtual/VirtualDeviceManager.java +++ b/core/java/android/companion/virtual/VirtualDeviceManager.java @@ -196,9 +196,7 @@ public final class VirtualDeviceManager { /** * Creates a virtual display for this virtual device. All displays created on the same - * device belongs to the same display group. Requires the ADD_TRUSTED_DISPLAY permission - * to create a virtual display which is not in the default DisplayGroup, and to create - * trusted displays. + * device belongs to the same display group. * * @param width The width of the virtual display in pixels, must be greater than 0. * @param height The height of the virtual display in pixels, must be greater than 0. diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 3feffc6ae7dfc..7ad4979724626 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -1318,7 +1318,9 @@ public final class DisplayManagerService extends SystemService { if (callingUid != Process.SYSTEM_UID && (flags & VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP) != 0) { - if (!checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) { + // The virtualDevice instance has been validated above using isValidVirtualDevice + if (virtualDevice == null + && !checkCallingPermission(ADD_TRUSTED_DISPLAY, "createVirtualDisplay()")) { throw new SecurityException("Requires ADD_TRUSTED_DISPLAY permission to " + "create a virtual display which is not in the default DisplayGroup."); } 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 eaa271a22725d..1fb58983607da 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -33,6 +33,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.PropertyInvalidatedCache; +import android.companion.virtual.IVirtualDevice; import android.compat.testing.PlatformCompatChangeRule; import android.content.Context; import android.content.ContextWrapper; @@ -73,6 +74,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.companion.virtual.VirtualDeviceManagerInternal; import com.android.server.display.DisplayManagerService.SyncRoot; import com.android.server.lights.LightsManager; import com.android.server.sensors.SensorManagerInternal; @@ -167,6 +169,7 @@ public class DisplayManagerServiceTest { }; @Mock InputManagerInternal mMockInputManagerInternal; + @Mock VirtualDeviceManagerInternal mMockVirtualDeviceManagerInternal; @Mock IVirtualDisplayCallback.Stub mMockAppToken; @Mock IVirtualDisplayCallback.Stub mMockAppToken2; @Mock WindowManagerInternal mMockWindowManagerInternal; @@ -187,6 +190,9 @@ public class DisplayManagerServiceTest { LocalServices.addService(LightsManager.class, mMockLightsManager); LocalServices.removeServiceForTest(SensorManagerInternal.class); LocalServices.addService(SensorManagerInternal.class, mMockSensorManagerInternal); + LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class); + LocalServices.addService( + VirtualDeviceManagerInternal.class, mMockVirtualDeviceManagerInternal); mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext())); mDeviceConfig = new FakeDeviceConfigInterface(); @@ -660,6 +666,101 @@ public class DisplayManagerServiceTest { /*deviceConfigAllows*/ false, /*flagExpected*/ false); } + /** + * Tests that specifying VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP is allowed when the permission + * ADD_TRUSTED_DISPLAY is granted. + */ + @Test + public void testOwnDisplayGroup_allowCreationWithAddTrustedDisplayPermission() { + DisplayManagerService displayManager = + new DisplayManagerService(mContext, mBasicInjector); + registerDefaultDisplays(displayManager); + + DisplayManagerService.BinderService bs = displayManager.new BinderService(); + when(mMockAppToken.asBinder()).thenReturn(mMockAppToken); + + when(mContext.checkCallingPermission(ADD_TRUSTED_DISPLAY)).thenReturn( + PackageManager.PERMISSION_GRANTED); + + final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder( + VIRTUAL_DISPLAY_NAME, 600, 800, 320); + builder.setFlags(DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP); + builder.setUniqueId("uniqueId --- OWN_DISPLAY_GROUP"); + + int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */, + null /* projection */, null /* virtualDeviceToken */, PACKAGE_NAME); + displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); + displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */); + DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayId); + assertNotNull(ddi); + assertNotEquals(0, ddi.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP); + } + + /** + * Tests that specifying VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP is blocked when the permission + * ADD_TRUSTED_DISPLAY is denied. + */ + @Test + public void testOwnDisplayGroup_withoutAddTrustedDisplayPermission_throwsSecurityException() { + DisplayManagerService displayManager = + new DisplayManagerService(mContext, mBasicInjector); + registerDefaultDisplays(displayManager); + + DisplayManagerService.BinderService bs = displayManager.new BinderService(); + when(mMockAppToken.asBinder()).thenReturn(mMockAppToken); + + when(mContext.checkCallingPermission(ADD_TRUSTED_DISPLAY)).thenReturn( + PackageManager.PERMISSION_DENIED); + + final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder( + VIRTUAL_DISPLAY_NAME, 600, 800, 320); + builder.setFlags(DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP); + builder.setUniqueId("uniqueId --- OWN_DISPLAY_GROUP"); + + try { + bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */, + null /* projection */, null /* virtualDeviceToken */, PACKAGE_NAME); + fail("Creating virtual display with VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP without " + + "ADD_TRUSTED_DISPLAY permission should throw SecurityException."); + } catch (SecurityException e) { + // SecurityException is expected + } + } + + /** + * Tests that specifying VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP is allowed when called with + * a virtual device, even if ADD_TRUSTED_DISPLAY is not granted. + */ + @Test + public void testOwnDisplayGroup_allowCreationWithVirtualDevice() { + DisplayManagerService displayManager = + new DisplayManagerService(mContext, mBasicInjector); + registerDefaultDisplays(displayManager); + + DisplayManagerService.BinderService bs = displayManager.new BinderService(); + when(mMockAppToken.asBinder()).thenReturn(mMockAppToken); + + when(mContext.checkCallingPermission(ADD_TRUSTED_DISPLAY)).thenReturn( + PackageManager.PERMISSION_DENIED); + + final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder( + VIRTUAL_DISPLAY_NAME, 600, 800, 320); + builder.setFlags(DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP); + builder.setUniqueId("uniqueId --- OWN_DISPLAY_GROUP"); + + IVirtualDevice virtualDevice = mock(IVirtualDevice.class); + when(mMockVirtualDeviceManagerInternal.isValidVirtualDevice(virtualDevice)) + .thenReturn(true); + + int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */, + null /* projection */, virtualDevice /* virtualDeviceToken */, PACKAGE_NAME); + displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); + displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */); + DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayId); + assertNotNull(ddi); + assertNotEquals(0, ddi.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP); + } + /** * Tests that there is a display change notification if the frame rate override * list is updated.