Merge "Add isValidVirtualDeviceId api to VDM"
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 240dbe1..befe833a 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -26,9 +26,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UiContext;
-import android.companion.virtual.VirtualDevice;
 import android.companion.virtual.VirtualDeviceManager;
-import android.companion.virtual.VirtualDeviceParams;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.AttributionSource;
 import android.content.AutofillOptions;
@@ -2742,9 +2740,12 @@
 
     @Override
     public @NonNull Context createDeviceContext(int deviceId) {
-        if (!isValidDeviceId(deviceId)) {
-            throw new IllegalArgumentException(
-                    "Not a valid ID of the default device or any virtual device: " + deviceId);
+        if (deviceId != VirtualDeviceManager.DEVICE_ID_DEFAULT) {
+            VirtualDeviceManager vdm = getSystemService(VirtualDeviceManager.class);
+            if (!vdm.isValidVirtualDeviceId(deviceId)) {
+                throw new IllegalArgumentException(
+                        "Not a valid ID of the default device or any virtual device: " + deviceId);
+            }
         }
 
         ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mParams,
@@ -2757,31 +2758,6 @@
         return context;
     }
 
-    /**
-     * Checks whether the passed {@code deviceId} is valid or not.
-     * {@link VirtualDeviceManager#DEVICE_ID_DEFAULT} is valid as it is the ID of the default
-     * device when no additional virtual devices exist. If {@code deviceId} is the id of
-     * a virtual device, it should correspond to a virtual device created by
-     * {@link VirtualDeviceManager#createVirtualDevice(int, VirtualDeviceParams)}.
-     */
-    private boolean isValidDeviceId(int deviceId) {
-        if (deviceId == VirtualDeviceManager.DEVICE_ID_DEFAULT) {
-            return true;
-        }
-        if (deviceId > VirtualDeviceManager.DEVICE_ID_DEFAULT) {
-            VirtualDeviceManager vdm = getSystemService(VirtualDeviceManager.class);
-            if (vdm != null) {
-                List<VirtualDevice> virtualDevices = vdm.getVirtualDevices();
-                for (int i = 0; i < virtualDevices.size(); i++) {
-                    if (virtualDevices.get(i).getDeviceId() == deviceId) {
-                        return true;
-                    }
-                }
-            }
-        }
-        return false;
-    }
-
     @NonNull
     @Override
     public WindowContext createWindowContext(@WindowType int type,
@@ -3044,10 +3020,13 @@
 
     @Override
     public void updateDeviceId(int updatedDeviceId) {
-        if (!isValidDeviceId(updatedDeviceId)) {
-            throw new IllegalArgumentException(
-                    "Not a valid ID of the default device or any virtual device: "
-                            + updatedDeviceId);
+        if (updatedDeviceId != VirtualDeviceManager.DEVICE_ID_DEFAULT) {
+            VirtualDeviceManager vdm = getSystemService(VirtualDeviceManager.class);
+            if (!vdm.isValidVirtualDeviceId(updatedDeviceId)) {
+                throw new IllegalArgumentException(
+                        "Not a valid ID of the default device or any virtual device: "
+                                + updatedDeviceId);
+            }
         }
         if (mIsExplicitDeviceId) {
             throw new UnsupportedOperationException(
diff --git a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
index f0d23ac..e96a2c1 100644
--- a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
+++ b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
@@ -56,6 +56,14 @@
      */
     int getDeviceIdForDisplayId(int displayId);
 
+   /**
+     * Checks whether the passed {@code deviceId} is a valid virtual device ID or not.
+     * {@link VirtualDeviceManager#DEVICE_ID_DEFAULT} is not valid as it is the ID of the default
+     * device which is not a virtual device. {@code deviceId} must correspond to a virtual device
+     * created by {@link VirtualDeviceManager#createVirtualDevice(int, VirtualDeviceParams)}.
+    */
+   boolean isValidVirtualDeviceId(int deviceId);
+
     /**
      * Returns the device policy for the given virtual device and policy type.
      */
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 6ad18d5..3bc1628 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -258,6 +258,26 @@
     }
 
     /**
+     * Checks whether the passed {@code deviceId} is a valid virtual device ID or not.
+     * {@link VirtualDeviceManager#DEVICE_ID_DEFAULT} is not valid as it is the ID of the default
+     * device which is not a virtual device. {@code deviceId} must correspond to a virtual device
+     * created by {@link VirtualDeviceManager#createVirtualDevice(int, VirtualDeviceParams)}.
+     *
+     * @hide
+     */
+    public boolean isValidVirtualDeviceId(int deviceId) {
+        if (mService == null) {
+            Log.w(TAG, "Failed to retrieve virtual devices; no virtual device manager service.");
+            return false;
+        }
+        try {
+            return mService.isValidVirtualDeviceId(deviceId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns device-specific audio session id for audio playback.
      *
      * @param deviceId - id of the virtual audio device
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 758345f..b0f2464 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -139,15 +139,6 @@
                 mActivityInterceptorCallback);
     }
 
-    @GuardedBy("mVirtualDeviceManagerLock")
-    private boolean isValidVirtualDeviceLocked(IVirtualDevice virtualDevice) {
-        try {
-            return mVirtualDevices.contains(virtualDevice.getDeviceId());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
     void onCameraAccessBlocked(int appUid) {
         synchronized (mVirtualDeviceManagerLock) {
             for (int i = 0; i < mVirtualDevices.size(); i++) {
@@ -347,6 +338,14 @@
             return VirtualDeviceManager.DEVICE_ID_DEFAULT;
         }
 
+        // Binder call
+        @Override
+        public boolean isValidVirtualDeviceId(int deviceId) {
+            synchronized (mVirtualDeviceManagerLock) {
+                return mVirtualDevices.contains(deviceId);
+            }
+        }
+
         @Override // Binder call
         public int getAudioPlaybackSessionId(int deviceId) {
             synchronized (mVirtualDeviceManagerLock) {
@@ -445,13 +444,6 @@
         private final ArraySet<Integer> mAllUidsOnVirtualDevice = new ArraySet<>();
 
         @Override
-        public boolean isValidVirtualDevice(IVirtualDevice virtualDevice) {
-            synchronized (mVirtualDeviceManagerLock) {
-                return isValidVirtualDeviceLocked(virtualDevice);
-            }
-        }
-
-        @Override
         public int getDeviceOwnerUid(int deviceId) {
             synchronized (mVirtualDeviceManagerLock) {
                 VirtualDeviceImpl virtualDevice = mVirtualDevices.get(deviceId);
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index e3ea1a6..974c04b 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -68,11 +68,6 @@
     public abstract void onAppsOnVirtualDeviceChanged();
 
     /**
-     * Validate the virtual device.
-     */
-    public abstract boolean isValidVirtualDevice(IVirtualDevice virtualDevice);
-
-    /**
      * Gets the owner uid for a deviceId.
      *
      * @param deviceId which device we're asking about
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index d44e1dc..06b99f8 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -47,6 +47,7 @@
 import android.app.AppOpsManager;
 import android.app.compat.CompatChanges;
 import android.companion.virtual.IVirtualDevice;
+import android.companion.virtual.VirtualDeviceManager;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledSince;
 import android.content.BroadcastReceiver;
@@ -1281,12 +1282,17 @@
         final Surface surface = virtualDisplayConfig.getSurface();
         int flags = virtualDisplayConfig.getFlags();
         if (virtualDevice != null) {
-            final VirtualDeviceManagerInternal vdm =
-                    getLocalService(VirtualDeviceManagerInternal.class);
-            if (!vdm.isValidVirtualDevice(virtualDevice)) {
-                throw new SecurityException("Invalid virtual device");
+            final VirtualDeviceManager vdm = mContext.getSystemService(VirtualDeviceManager.class);
+            try {
+                if (!vdm.isValidVirtualDeviceId(virtualDevice.getDeviceId())) {
+                    throw new SecurityException("Invalid virtual device");
+                }
+            } catch (RemoteException ex) {
+                throw new SecurityException("Unable to validate virtual device");
             }
-            flags |= vdm.getBaseVirtualDisplayFlags(virtualDevice);
+            final VirtualDeviceManagerInternal localVdm =
+                    getLocalService(VirtualDeviceManagerInternal.class);
+            flags |= localVdm.getBaseVirtualDisplayFlags(virtualDevice);
         }
 
         if (surface != null && surface.isSingleBuffered()) {
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 759b049..eb99e30 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -369,6 +369,21 @@
     }
 
     @Test
+    public void isDeviceIdValid_defaultDeviceId_returnsFalse() {
+        assertThat(mVdm.isValidVirtualDeviceId(DEVICE_ID_DEFAULT)).isFalse();
+    }
+
+    @Test
+    public void isDeviceIdValid_validVirtualDeviceId_returnsTrue() {
+        assertThat(mVdm.isValidVirtualDeviceId(mDeviceImpl.getDeviceId())).isTrue();
+    }
+
+    @Test
+    public void isDeviceIdValid_nonExistentDeviceId_returnsFalse() {
+        assertThat(mVdm.isValidVirtualDeviceId(mDeviceImpl.getDeviceId() + 1)).isFalse();
+    }
+
+    @Test
     public void getDevicePolicy_invalidDeviceId_returnsDefault() {
         assertThat(mVdm.getDevicePolicy(DEVICE_ID_INVALID, POLICY_TYPE_SENSORS))
                 .isEqualTo(DEVICE_POLICY_DEFAULT);
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 f676a3f..2d252cb 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -39,6 +39,8 @@
 
 import android.app.PropertyInvalidatedCache;
 import android.companion.virtual.IVirtualDevice;
+import android.companion.virtual.IVirtualDeviceManager;
+import android.companion.virtual.VirtualDeviceManager;
 import android.compat.testing.PlatformCompatChangeRule;
 import android.content.Context;
 import android.content.ContextWrapper;
@@ -173,6 +175,7 @@
 
     private final DisplayManagerService.Injector mBasicInjector = new BasicInjector();
 
+    @Mock IVirtualDeviceManager mIVirtualDeviceManager;
     @Mock InputManagerInternal mMockInputManagerInternal;
     @Mock VirtualDeviceManagerInternal mMockVirtualDeviceManagerInternal;
     @Mock IVirtualDisplayCallback.Stub mMockAppToken;
@@ -202,6 +205,8 @@
 
         mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
 
+        VirtualDeviceManager vdm = new VirtualDeviceManager(mIVirtualDeviceManager, mContext);
+        when(mContext.getSystemService(VirtualDeviceManager.class)).thenReturn(vdm);
         // Disable binder caches in this process.
         PropertyInvalidatedCache.disableForTestMode();
         setUpDisplay();
@@ -727,10 +732,8 @@
         when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
 
         IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
-        when(mMockVirtualDeviceManagerInternal.isValidVirtualDevice(virtualDevice))
-                .thenReturn(true);
         when(virtualDevice.getDeviceId()).thenReturn(1);
-
+        when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
         // Create a first virtual display. A display group should be created for this display on the
         // virtual device.
         final VirtualDisplayConfig.Builder builder1 =
@@ -780,9 +783,8 @@
         when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
 
         IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
-        when(mMockVirtualDeviceManagerInternal.isValidVirtualDevice(virtualDevice))
-                .thenReturn(true);
         when(virtualDevice.getDeviceId()).thenReturn(1);
+        when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
 
         // Create a first virtual display. A display group should be created for this display on the
         // virtual device.
@@ -806,6 +808,8 @@
                         .setFlags(VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP)
                         .setUniqueId("uniqueId --- own display group");
 
+        when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
+
         int displayId2 =
                 localService.createVirtualDisplay(
                         builder2.build(),
@@ -832,9 +836,8 @@
         when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
 
         IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
-        when(mMockVirtualDeviceManagerInternal.isValidVirtualDevice(virtualDevice))
-                .thenReturn(true);
         when(virtualDevice.getDeviceId()).thenReturn(1);
+        when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
 
         // Allow an ALWAYS_UNLOCKED display to be created.
         when(mContext.checkCallingPermission(ADD_TRUSTED_DISPLAY))
@@ -1062,7 +1065,7 @@
      * a virtual device, even if ADD_TRUSTED_DISPLAY is not granted.
      */
     @Test
-    public void testOwnDisplayGroup_allowCreationWithVirtualDevice() {
+    public void testOwnDisplayGroup_allowCreationWithVirtualDevice()  throws Exception {
         DisplayManagerService displayManager =
                 new DisplayManagerService(mContext, mBasicInjector);
         DisplayManagerInternal localService = displayManager.new LocalService();
@@ -1081,8 +1084,8 @@
         builder.setUniqueId("uniqueId --- OWN_DISPLAY_GROUP");
 
         IVirtualDevice virtualDevice = mock(IVirtualDevice.class);
-        when(mMockVirtualDeviceManagerInternal.isValidVirtualDevice(virtualDevice))
-            .thenReturn(true);
+        when(virtualDevice.getDeviceId()).thenReturn(1);
+        when(mIVirtualDeviceManager.isValidVirtualDeviceId(1)).thenReturn(true);
 
         int displayId = localService.createVirtualDisplay(builder.build(),
                 mMockAppToken /* callback */, virtualDevice /* virtualDeviceToken */,