From aa967aa8bd26a3d1d7ea61f01ec9a3267121e2e2 Mon Sep 17 00:00:00 2001 From: Marvin Ramin Date: Thu, 30 Mar 2023 11:16:47 +0000 Subject: [PATCH] Close VirtualDevice when CDM association is removed Close the VirtualDevice and release all resources when the linked CDM association is removed to ensure VD owners cannot user devices without the association as the user might have revoked it. This leaves the virtual device closed and with DEVICE_ID_INVALID, ensuring new virtual device functionality cannot be invoked. Bug: 272687853 Test: cts Change-Id: If03e9236d05ad44abb2c7d9a7aeb26f34ab2279e --- .../companion/virtual/VirtualDeviceImpl.java | 3 +- .../virtual/VirtualDeviceManagerService.java | 65 +++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index b338d89a01696..947bd9c57e631 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -126,7 +126,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub private final VirtualDeviceManagerService mService; private final PendingTrampolineCallback mPendingTrampolineCallback; private final int mOwnerUid; - private final int mDeviceId; + private int mDeviceId; // Thou shall not hold the mVirtualDeviceLock over the mInputController calls. // Holding the lock can lead to lock inversion with GlobalWindowManagerLock. // 1. After display is created the window manager calls into VDM during construction @@ -404,6 +404,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub super.close_enforcePermission(); // Remove about-to-be-closed virtual device from the service before butchering it. mService.removeVirtualDevice(mDeviceId); + mDeviceId = Context.DEVICE_ID_INVALID; VirtualDisplayWrapper[] virtualDisplaysToBeReleased; synchronized (mVirtualDeviceLock) { 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 3b1983f55fb65..eec898f21800c 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java @@ -66,7 +66,9 @@ import com.android.server.wm.ActivityTaskManagerInternal; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; @@ -86,6 +88,14 @@ public class VirtualDeviceManagerService extends SystemService { private static AtomicInteger sNextUniqueIndex = new AtomicInteger( Context.DEVICE_ID_DEFAULT + 1); + private final CompanionDeviceManager.OnAssociationsChangedListener mCdmAssociationListener = + new CompanionDeviceManager.OnAssociationsChangedListener() { + @Override + public void onAssociationsChanged(@NonNull List associations) { + syncVirtualDevicesToCdmAssociations(associations); + } + }; + /** * Mapping from device IDs to virtual devices. */ @@ -204,11 +214,56 @@ public class VirtualDeviceManagerService extends SystemService { final long identity = Binder.clearCallingIdentity(); try { getContext().sendBroadcastAsUser(i, UserHandle.ALL); + + synchronized (mVirtualDeviceManagerLock) { + if (mVirtualDevices.size() == 0) { + unregisterCdmAssociationListener(); + } + } } finally { Binder.restoreCallingIdentity(identity); } } + private void syncVirtualDevicesToCdmAssociations(List associations) { + Set virtualDevicesToRemove = new HashSet<>(); + synchronized (mVirtualDeviceManagerLock) { + if (mVirtualDevices.size() == 0) { + return; + } + + Set activeAssociationIds = new HashSet<>(associations.size()); + for (AssociationInfo association : associations) { + activeAssociationIds.add(association.getId()); + } + + for (int i = 0; i < mVirtualDevices.size(); i++) { + VirtualDeviceImpl virtualDevice = mVirtualDevices.valueAt(i); + if (!activeAssociationIds.contains(virtualDevice.getAssociationId())) { + virtualDevicesToRemove.add(virtualDevice); + } + } + } + + for (VirtualDeviceImpl virtualDevice : virtualDevicesToRemove) { + virtualDevice.close(); + } + + } + + private void registerCdmAssociationListener() { + final CompanionDeviceManager cdm = getContext().getSystemService( + CompanionDeviceManager.class); + cdm.addOnAssociationsChangedListener(getContext().getMainExecutor(), + mCdmAssociationListener); + } + + private void unregisterCdmAssociationListener() { + final CompanionDeviceManager cdm = getContext().getSystemService( + CompanionDeviceManager.class); + cdm.removeOnAssociationsChangedListener(mCdmAssociationListener); + } + class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub { private final VirtualDeviceImpl.PendingTrampolineCallback mPendingTrampolineCallback = @@ -254,7 +309,17 @@ public class VirtualDeviceManagerService extends SystemService { if (associationInfo == null) { throw new IllegalArgumentException("No association with ID " + associationId); } + synchronized (mVirtualDeviceManagerLock) { + if (mVirtualDevices.size() == 0) { + final long callindId = Binder.clearCallingIdentity(); + try { + registerCdmAssociationListener(); + } finally { + Binder.restoreCallingIdentity(callindId); + } + } + final UserHandle userHandle = getCallingUserHandle(); final CameraAccessController cameraAccessController = getCameraAccessController(userHandle);