Loading services/companion/java/com/android/server/companion/virtual/CameraAccessController.java +18 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.server.companion.virtual; import static android.hardware.camera2.CameraInjectionSession.InjectionStatusCallback.ERROR_INJECTION_UNSUPPORTED; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; Loading Loading @@ -94,6 +95,23 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen mUserManager = mContext.getSystemService(UserManager.class); } /** * Returns the userId for which the camera access should be blocked. */ @UserIdInt public int getUserId() { return mContext.getUserId(); } /** * Returns the number of observers currently relying on this controller. */ public int getObserverCount() { synchronized (mLock) { return mObserverCount; } } /** * Starts watching for camera access by uids running on a virtual device, if we were not * already doing so. Loading services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +13 −0 Original line number Diff line number Diff line Loading @@ -110,6 +110,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub private final int mDeviceId; private final InputController mInputController; private final SensorController mSensorController; private final CameraAccessController mCameraAccessController; private VirtualAudioController mVirtualAudioController; @VisibleForTesting final Set<Integer> mVirtualDisplayIds = new ArraySet<>(); Loading Loading @@ -165,6 +166,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub IBinder token, int ownerUid, int deviceId, CameraAccessController cameraAccessController, OnDeviceCloseListener onDeviceCloseListener, PendingTrampolineCallback pendingTrampolineCallback, IVirtualDeviceActivityListener activityListener, Loading @@ -178,6 +180,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub deviceId, /* inputController= */ null, /* sensorController= */ null, cameraAccessController, onDeviceCloseListener, pendingTrampolineCallback, activityListener, Loading @@ -194,6 +197,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub int deviceId, InputController inputController, SensorController sensorController, CameraAccessController cameraAccessController, OnDeviceCloseListener onDeviceCloseListener, PendingTrampolineCallback pendingTrampolineCallback, IVirtualDeviceActivityListener activityListener, Loading Loading @@ -223,6 +227,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } else { mSensorController = sensorController; } mCameraAccessController = cameraAccessController; mCameraAccessController.startObservingIfNeeded(); mOnDeviceCloseListener = onDeviceCloseListener; try { token.linkToDeath(this, 0); Loading @@ -243,6 +249,11 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub return flags; } /** Returns the camera access controller of this device. */ CameraAccessController getCameraAccessController() { return mCameraAccessController; } /** Returns the device display name. */ CharSequence getDisplayName() { return mAssociationInfo.getDisplayName(); Loading Loading @@ -359,6 +370,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } mOnDeviceCloseListener.onClose(mDeviceId); mAppToken.unlinkToDeath(this, 0); mCameraAccessController.stopObservingIfNeeded(); final long ident = Binder.clearCallingIdentity(); try { Loading @@ -376,6 +388,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub @Override public void onRunningAppsChanged(ArraySet<Integer> runningUids) { mCameraAccessController.blockCameraAccessIfNeeded(runningUids); mRunningAppsChangedCallback.accept(runningUids); } Loading services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +73 −131 Original line number Diff line number Diff line Loading @@ -27,7 +27,6 @@ import android.annotation.SuppressLint; import android.app.ActivityOptions; import android.companion.AssociationInfo; import android.companion.CompanionDeviceManager; import android.companion.CompanionDeviceManager.OnAssociationsChangedListener; import android.companion.virtual.IVirtualDevice; import android.companion.virtual.IVirtualDeviceActivityListener; import android.companion.virtual.IVirtualDeviceManager; Loading Loading @@ -70,6 +69,7 @@ import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; @SuppressLint("LongLogTag") Loading @@ -86,20 +86,6 @@ public class VirtualDeviceManagerService extends SystemService { private static AtomicInteger sNextUniqueIndex = new AtomicInteger( VirtualDeviceManager.DEVICE_ID_DEFAULT + 1); /** * Mapping from user IDs to CameraAccessControllers. */ @GuardedBy("mVirtualDeviceManagerLock") private final SparseArray<CameraAccessController> mCameraAccessControllers = new SparseArray<>(); /** * Mapping from device IDs to CameraAccessControllers. */ @GuardedBy("mVirtualDeviceManagerLock") private final SparseArray<CameraAccessController> mCameraAccessControllersByDeviceId = new SparseArray<>(); /** * Mapping from device IDs to virtual devices. */ Loading @@ -112,21 +98,6 @@ public class VirtualDeviceManagerService extends SystemService { @GuardedBy("mVirtualDeviceManagerLock") private final SparseArray<ArraySet<Integer>> mAppsOnVirtualDevices = new SparseArray<>(); /** * Mapping from user ID to CDM associations. The associations come from * {@link CompanionDeviceManager#getAllAssociations()}, which contains associations across all * packages. */ private final ConcurrentHashMap<Integer, List<AssociationInfo>> mAllAssociations = new ConcurrentHashMap<>(); /** * Mapping from user ID to its change listener. The listeners are added when the user is * started and removed when the user stops. */ private final SparseArray<OnAssociationsChangedListener> mOnAssociationsChangedListeners = new SparseArray<>(); public VirtualDeviceManagerService(Context context) { super(context); mImpl = new VirtualDeviceManagerImpl(); Loading Loading @@ -177,54 +148,9 @@ public class VirtualDeviceManagerService extends SystemService { } } @Override public void onUserStarting(@NonNull TargetUser user) { super.onUserStarting(user); Context userContext = getContext().createContextAsUser(user.getUserHandle(), 0); synchronized (mVirtualDeviceManagerLock) { final CompanionDeviceManager cdm = userContext.getSystemService(CompanionDeviceManager.class); final int userId = user.getUserIdentifier(); mAllAssociations.put(userId, cdm.getAllAssociations()); OnAssociationsChangedListener listener = associations -> mAllAssociations.put(userId, associations); mOnAssociationsChangedListeners.put(userId, listener); cdm.addOnAssociationsChangedListener(Runnable::run, listener); CameraAccessController cameraAccessController = new CameraAccessController( userContext, mLocalService, this::onCameraAccessBlocked); mCameraAccessControllers.put(user.getUserIdentifier(), cameraAccessController); } } @Override public void onUserStopping(@NonNull TargetUser user) { super.onUserStopping(user); synchronized (mVirtualDeviceManagerLock) { int userId = user.getUserIdentifier(); mAllAssociations.remove(userId); final CompanionDeviceManager cdm = getContext().createContextAsUser( user.getUserHandle(), 0) .getSystemService(CompanionDeviceManager.class); OnAssociationsChangedListener listener = mOnAssociationsChangedListeners.get(userId); if (listener != null) { cdm.removeOnAssociationsChangedListener(listener); mOnAssociationsChangedListeners.remove(userId); } CameraAccessController cameraAccessController = mCameraAccessControllers.get( user.getUserIdentifier()); if (cameraAccessController != null) { cameraAccessController.close(); mCameraAccessControllers.remove(user.getUserIdentifier()); } else { Slog.w(TAG, "Cannot unregister cameraAccessController for user " + user); } } } void onCameraAccessBlocked(int appUid) { synchronized (mVirtualDeviceManagerLock) { int size = mVirtualDevices.size(); for (int i = 0; i < size; i++) { for (int i = 0; i < mVirtualDevices.size(); i++) { CharSequence deviceName = mVirtualDevices.valueAt(i).getDisplayName(); mVirtualDevices.valueAt(i).showToastWhereUidIsRunning(appUid, getContext().getString( Loading @@ -235,6 +161,21 @@ public class VirtualDeviceManagerService extends SystemService { } } CameraAccessController getCameraAccessController(UserHandle userHandle) { int userId = userHandle.getIdentifier(); synchronized (mVirtualDeviceManagerLock) { for (int i = 0; i < mVirtualDevices.size(); i++) { final CameraAccessController cameraAccessController = mVirtualDevices.valueAt(i).getCameraAccessController(); if (cameraAccessController.getUserId() == userId) { return cameraAccessController; } } } Context userContext = getContext().createContextAsUser(userHandle, 0); return new CameraAccessController(userContext, mLocalService, this::onCameraAccessBlocked); } @VisibleForTesting VirtualDeviceManagerInternal getLocalServiceInstance() { return mLocalService; Loading @@ -255,8 +196,34 @@ public class VirtualDeviceManagerService extends SystemService { } } class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub implements VirtualDeviceImpl.PendingTrampolineCallback { @VisibleForTesting void removeVirtualDevice(int deviceId) { synchronized (mVirtualDeviceManagerLock) { mAppsOnVirtualDevices.remove(deviceId); mVirtualDevices.remove(deviceId); } } class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub { private final VirtualDeviceImpl.PendingTrampolineCallback mPendingTrampolineCallback = new VirtualDeviceImpl.PendingTrampolineCallback() { @Override public void startWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline) { PendingTrampoline existing = mPendingTrampolines.put( pendingTrampoline.mPendingIntent.getCreatorPackage(), pendingTrampoline); if (existing != null) { existing.mResultReceiver.send( VirtualDeviceManager.LAUNCH_FAILURE_NO_ACTIVITY, null); } } @Override public void stopWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline) { mPendingTrampolines.remove(pendingTrampoline.mPendingIntent.getCreatorPackage()); } }; @Override // Binder call public IVirtualDevice createVirtualDevice( Loading @@ -279,25 +246,16 @@ public class VirtualDeviceManagerService extends SystemService { throw new IllegalArgumentException("No association with ID " + associationId); } synchronized (mVirtualDeviceManagerLock) { final int userId = UserHandle.getUserId(callingUid); final UserHandle userHandle = getCallingUserHandle(); final CameraAccessController cameraAccessController = mCameraAccessControllers.get(userId); getCameraAccessController(userHandle); final int deviceId = sNextUniqueIndex.getAndIncrement(); final Consumer<ArraySet<Integer>> runningAppsChangedCallback = runningUids -> notifyRunningAppsChanged(deviceId, runningUids); VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl(getContext(), associationInfo, token, callingUid, deviceId, /* onDeviceCloseListener= */ this::onDeviceClosed, this, activityListener, runningUids -> { cameraAccessController.blockCameraAccessIfNeeded(runningUids); notifyRunningAppsChanged(deviceId, runningUids); }, params); if (cameraAccessController != null) { cameraAccessController.startObservingIfNeeded(); mCameraAccessControllersByDeviceId.put(deviceId, cameraAccessController); } else { Slog.w(TAG, "cameraAccessController not found for user " + userId); } associationInfo, token, callingUid, deviceId, cameraAccessController, this::onDeviceClosed, mPendingTrampolineCallback, activityListener, runningAppsChangedCallback, params); mVirtualDevices.put(deviceId, virtualDevice); return virtualDevice; } Loading Loading @@ -409,8 +367,18 @@ public class VirtualDeviceManagerService extends SystemService { @Nullable private AssociationInfo getAssociationInfo(String packageName, int associationId) { final int callingUserId = getCallingUserHandle().getIdentifier(); final List<AssociationInfo> associations = mAllAssociations.get(callingUserId); final UserHandle userHandle = getCallingUserHandle(); final CompanionDeviceManager cdm = getContext().createContextAsUser(userHandle, 0) .getSystemService(CompanionDeviceManager.class); List<AssociationInfo> associations; final long identity = Binder.clearCallingIdentity(); try { associations = cdm.getAllAssociations(); } finally { Binder.restoreCallingIdentity(identity); } final int callingUserId = userHandle.getIdentifier(); if (associations != null) { final int associationSize = associations.size(); for (int i = 0; i < associationSize; i++) { Loading @@ -427,8 +395,7 @@ public class VirtualDeviceManagerService extends SystemService { } private void onDeviceClosed(int deviceId) { synchronized (mVirtualDeviceManagerLock) { mVirtualDevices.remove(deviceId); removeVirtualDevice(deviceId); Intent i = new Intent(VirtualDeviceManager.ACTION_VIRTUAL_DEVICE_REMOVED); i.putExtra(VirtualDeviceManager.EXTRA_VIRTUAL_DEVICE_ID, deviceId); i.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); Loading @@ -438,15 +405,6 @@ public class VirtualDeviceManagerService extends SystemService { } finally { Binder.restoreCallingIdentity(identity); } mAppsOnVirtualDevices.remove(deviceId); final CameraAccessController cameraAccessController = mCameraAccessControllersByDeviceId.removeReturnOld(deviceId); if (cameraAccessController != null) { cameraAccessController.stopObservingIfNeeded(); } else { Slog.w(TAG, "cameraAccessController not found for device Id " + deviceId); } } } @Override Loading Loading @@ -474,22 +432,6 @@ public class VirtualDeviceManagerService extends SystemService { } } } @Override public void startWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline) { PendingTrampoline existing = mPendingTrampolines.put( pendingTrampoline.mPendingIntent.getCreatorPackage(), pendingTrampoline); if (existing != null) { existing.mResultReceiver.send( VirtualDeviceManager.LAUNCH_FAILURE_NO_ACTIVITY, null); } } @Override public void stopWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline) { mPendingTrampolines.remove(pendingTrampoline.mPendingIntent.getCreatorPackage()); } } private final class LocalService extends VirtualDeviceManagerInternal { Loading services/tests/mockingservicestests/src/com/android/server/companion/virtual/CameraAccessControllerTest.java +7 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.server.companion.virtual; import static android.hardware.camera2.CameraInjectionSession.InjectionStatusCallback.ERROR_INJECTION_UNSUPPORTED; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; Loading Loading @@ -115,6 +117,11 @@ public class CameraAccessControllerTest { mController.startObservingIfNeeded(); } @Test public void getUserId_returnsCorrectId() { assertThat(mController.getUserId()).isEqualTo(mContext.getUserId()); } @Test public void onCameraOpened_uidNotRunning_noCameraBlocking() throws CameraAccessException { when(mDeviceManagerInternal.isAppRunningOnAnyVirtualDevice( Loading services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java +29 −10 Original line number Diff line number Diff line Loading @@ -203,6 +203,7 @@ public class VirtualDeviceManagerServiceTest { private VirtualDeviceImpl mDeviceImpl; private InputController mInputController; private SensorController mSensorController; private CameraAccessController mCameraAccessController; private AssociationInfo mAssociationInfo; private VirtualDeviceManagerService mVdms; private VirtualDeviceManagerInternal mLocalService; Loading Loading @@ -237,6 +238,8 @@ public class VirtualDeviceManagerServiceTest { @Mock private IAudioConfigChangedCallback mConfigChangedCallback; @Mock private CameraAccessController.CameraAccessBlockedCallback mCameraAccessBlockedCallback; @Mock private ApplicationInfo mApplicationInfoMock; @Mock IInputManager mIInputManagerMock; Loading Loading @@ -325,6 +328,8 @@ public class VirtualDeviceManagerServiceTest { new Handler(TestableLooper.get(this).getLooper()), mContext.getSystemService(WindowManager.class), threadVerifier); mSensorController = new SensorController(new Object(), VIRTUAL_DEVICE_ID_1); mCameraAccessController = new CameraAccessController(mContext, mLocalService, mCameraAccessBlockedCallback); mAssociationInfo = new AssociationInfo(/* associationId= */ 1, 0, null, MacAddress.BROADCAST_ADDRESS, "", null, null, true, false, false, 0, 0); Loading Loading @@ -394,12 +399,7 @@ public class VirtualDeviceManagerServiceTest { .setBlockedActivities(getBlockedActivities()) .setDevicePolicy(POLICY_TYPE_SENSORS, DEVICE_POLICY_CUSTOM) .build(); mDeviceImpl = new VirtualDeviceImpl(mContext, mAssociationInfo, new Binder(), /* ownerUid */ 0, VIRTUAL_DEVICE_ID_1, mInputController, mSensorController, /* onDeviceCloseListener= */ (int deviceId) -> {}, mPendingTrampolineCallback, mActivityListener, mRunningAppsChangedCallback, params); mVdms.addVirtualDevice(mDeviceImpl); mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1, params); assertThat(mVdm.getDevicePolicy(mDeviceImpl.getDeviceId(), POLICY_TYPE_SENSORS)) .isEqualTo(DEVICE_POLICY_CUSTOM); Loading Loading @@ -557,6 +557,21 @@ public class VirtualDeviceManagerServiceTest { LocaleList.forLanguageTags(firstKeyboardConfig.getLanguageTag())); } @Test public void cameraAccessController_observerCountUpdated() { assertThat(mCameraAccessController.getObserverCount()).isEqualTo(1); VirtualDeviceImpl secondDevice = createVirtualDevice(VIRTUAL_DEVICE_ID_2, DEVICE_OWNER_UID_2); assertThat(mCameraAccessController.getObserverCount()).isEqualTo(2); mDeviceImpl.close(); assertThat(mCameraAccessController.getObserverCount()).isEqualTo(1); secondDevice.close(); assertThat(mCameraAccessController.getObserverCount()).isEqualTo(0); } @Test public void onVirtualDisplayRemovedLocked_doesNotThrowException() { mDeviceImpl.onVirtualDisplayCreatedLocked( Loading Loading @@ -1543,14 +1558,18 @@ public class VirtualDeviceManagerServiceTest { } private VirtualDeviceImpl createVirtualDevice(int virtualDeviceId, int ownerUid) { VirtualDeviceParams params = new VirtualDeviceParams .Builder() VirtualDeviceParams params = new VirtualDeviceParams.Builder() .setBlockedActivities(getBlockedActivities()) .build(); return createVirtualDevice(virtualDeviceId, ownerUid, params); } private VirtualDeviceImpl createVirtualDevice(int virtualDeviceId, int ownerUid, VirtualDeviceParams params) { VirtualDeviceImpl virtualDeviceImpl = new VirtualDeviceImpl(mContext, mAssociationInfo, new Binder(), ownerUid, virtualDeviceId, mInputController, mSensorController, /* onDeviceCloseListener= */ (int deviceId) -> {}, mInputController, mSensorController, mCameraAccessController, /* onDeviceCloseListener= */ deviceId -> mVdms.removeVirtualDevice(deviceId), mPendingTrampolineCallback, mActivityListener, mRunningAppsChangedCallback, params); mVdms.addVirtualDevice(virtualDeviceImpl); return virtualDeviceImpl; Loading Loading
services/companion/java/com/android/server/companion/virtual/CameraAccessController.java +18 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.server.companion.virtual; import static android.hardware.camera2.CameraInjectionSession.InjectionStatusCallback.ERROR_INJECTION_UNSUPPORTED; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; Loading Loading @@ -94,6 +95,23 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen mUserManager = mContext.getSystemService(UserManager.class); } /** * Returns the userId for which the camera access should be blocked. */ @UserIdInt public int getUserId() { return mContext.getUserId(); } /** * Returns the number of observers currently relying on this controller. */ public int getObserverCount() { synchronized (mLock) { return mObserverCount; } } /** * Starts watching for camera access by uids running on a virtual device, if we were not * already doing so. Loading
services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +13 −0 Original line number Diff line number Diff line Loading @@ -110,6 +110,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub private final int mDeviceId; private final InputController mInputController; private final SensorController mSensorController; private final CameraAccessController mCameraAccessController; private VirtualAudioController mVirtualAudioController; @VisibleForTesting final Set<Integer> mVirtualDisplayIds = new ArraySet<>(); Loading Loading @@ -165,6 +166,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub IBinder token, int ownerUid, int deviceId, CameraAccessController cameraAccessController, OnDeviceCloseListener onDeviceCloseListener, PendingTrampolineCallback pendingTrampolineCallback, IVirtualDeviceActivityListener activityListener, Loading @@ -178,6 +180,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub deviceId, /* inputController= */ null, /* sensorController= */ null, cameraAccessController, onDeviceCloseListener, pendingTrampolineCallback, activityListener, Loading @@ -194,6 +197,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub int deviceId, InputController inputController, SensorController sensorController, CameraAccessController cameraAccessController, OnDeviceCloseListener onDeviceCloseListener, PendingTrampolineCallback pendingTrampolineCallback, IVirtualDeviceActivityListener activityListener, Loading Loading @@ -223,6 +227,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } else { mSensorController = sensorController; } mCameraAccessController = cameraAccessController; mCameraAccessController.startObservingIfNeeded(); mOnDeviceCloseListener = onDeviceCloseListener; try { token.linkToDeath(this, 0); Loading @@ -243,6 +249,11 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub return flags; } /** Returns the camera access controller of this device. */ CameraAccessController getCameraAccessController() { return mCameraAccessController; } /** Returns the device display name. */ CharSequence getDisplayName() { return mAssociationInfo.getDisplayName(); Loading Loading @@ -359,6 +370,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } mOnDeviceCloseListener.onClose(mDeviceId); mAppToken.unlinkToDeath(this, 0); mCameraAccessController.stopObservingIfNeeded(); final long ident = Binder.clearCallingIdentity(); try { Loading @@ -376,6 +388,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub @Override public void onRunningAppsChanged(ArraySet<Integer> runningUids) { mCameraAccessController.blockCameraAccessIfNeeded(runningUids); mRunningAppsChangedCallback.accept(runningUids); } Loading
services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +73 −131 Original line number Diff line number Diff line Loading @@ -27,7 +27,6 @@ import android.annotation.SuppressLint; import android.app.ActivityOptions; import android.companion.AssociationInfo; import android.companion.CompanionDeviceManager; import android.companion.CompanionDeviceManager.OnAssociationsChangedListener; import android.companion.virtual.IVirtualDevice; import android.companion.virtual.IVirtualDeviceActivityListener; import android.companion.virtual.IVirtualDeviceManager; Loading Loading @@ -70,6 +69,7 @@ import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; @SuppressLint("LongLogTag") Loading @@ -86,20 +86,6 @@ public class VirtualDeviceManagerService extends SystemService { private static AtomicInteger sNextUniqueIndex = new AtomicInteger( VirtualDeviceManager.DEVICE_ID_DEFAULT + 1); /** * Mapping from user IDs to CameraAccessControllers. */ @GuardedBy("mVirtualDeviceManagerLock") private final SparseArray<CameraAccessController> mCameraAccessControllers = new SparseArray<>(); /** * Mapping from device IDs to CameraAccessControllers. */ @GuardedBy("mVirtualDeviceManagerLock") private final SparseArray<CameraAccessController> mCameraAccessControllersByDeviceId = new SparseArray<>(); /** * Mapping from device IDs to virtual devices. */ Loading @@ -112,21 +98,6 @@ public class VirtualDeviceManagerService extends SystemService { @GuardedBy("mVirtualDeviceManagerLock") private final SparseArray<ArraySet<Integer>> mAppsOnVirtualDevices = new SparseArray<>(); /** * Mapping from user ID to CDM associations. The associations come from * {@link CompanionDeviceManager#getAllAssociations()}, which contains associations across all * packages. */ private final ConcurrentHashMap<Integer, List<AssociationInfo>> mAllAssociations = new ConcurrentHashMap<>(); /** * Mapping from user ID to its change listener. The listeners are added when the user is * started and removed when the user stops. */ private final SparseArray<OnAssociationsChangedListener> mOnAssociationsChangedListeners = new SparseArray<>(); public VirtualDeviceManagerService(Context context) { super(context); mImpl = new VirtualDeviceManagerImpl(); Loading Loading @@ -177,54 +148,9 @@ public class VirtualDeviceManagerService extends SystemService { } } @Override public void onUserStarting(@NonNull TargetUser user) { super.onUserStarting(user); Context userContext = getContext().createContextAsUser(user.getUserHandle(), 0); synchronized (mVirtualDeviceManagerLock) { final CompanionDeviceManager cdm = userContext.getSystemService(CompanionDeviceManager.class); final int userId = user.getUserIdentifier(); mAllAssociations.put(userId, cdm.getAllAssociations()); OnAssociationsChangedListener listener = associations -> mAllAssociations.put(userId, associations); mOnAssociationsChangedListeners.put(userId, listener); cdm.addOnAssociationsChangedListener(Runnable::run, listener); CameraAccessController cameraAccessController = new CameraAccessController( userContext, mLocalService, this::onCameraAccessBlocked); mCameraAccessControllers.put(user.getUserIdentifier(), cameraAccessController); } } @Override public void onUserStopping(@NonNull TargetUser user) { super.onUserStopping(user); synchronized (mVirtualDeviceManagerLock) { int userId = user.getUserIdentifier(); mAllAssociations.remove(userId); final CompanionDeviceManager cdm = getContext().createContextAsUser( user.getUserHandle(), 0) .getSystemService(CompanionDeviceManager.class); OnAssociationsChangedListener listener = mOnAssociationsChangedListeners.get(userId); if (listener != null) { cdm.removeOnAssociationsChangedListener(listener); mOnAssociationsChangedListeners.remove(userId); } CameraAccessController cameraAccessController = mCameraAccessControllers.get( user.getUserIdentifier()); if (cameraAccessController != null) { cameraAccessController.close(); mCameraAccessControllers.remove(user.getUserIdentifier()); } else { Slog.w(TAG, "Cannot unregister cameraAccessController for user " + user); } } } void onCameraAccessBlocked(int appUid) { synchronized (mVirtualDeviceManagerLock) { int size = mVirtualDevices.size(); for (int i = 0; i < size; i++) { for (int i = 0; i < mVirtualDevices.size(); i++) { CharSequence deviceName = mVirtualDevices.valueAt(i).getDisplayName(); mVirtualDevices.valueAt(i).showToastWhereUidIsRunning(appUid, getContext().getString( Loading @@ -235,6 +161,21 @@ public class VirtualDeviceManagerService extends SystemService { } } CameraAccessController getCameraAccessController(UserHandle userHandle) { int userId = userHandle.getIdentifier(); synchronized (mVirtualDeviceManagerLock) { for (int i = 0; i < mVirtualDevices.size(); i++) { final CameraAccessController cameraAccessController = mVirtualDevices.valueAt(i).getCameraAccessController(); if (cameraAccessController.getUserId() == userId) { return cameraAccessController; } } } Context userContext = getContext().createContextAsUser(userHandle, 0); return new CameraAccessController(userContext, mLocalService, this::onCameraAccessBlocked); } @VisibleForTesting VirtualDeviceManagerInternal getLocalServiceInstance() { return mLocalService; Loading @@ -255,8 +196,34 @@ public class VirtualDeviceManagerService extends SystemService { } } class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub implements VirtualDeviceImpl.PendingTrampolineCallback { @VisibleForTesting void removeVirtualDevice(int deviceId) { synchronized (mVirtualDeviceManagerLock) { mAppsOnVirtualDevices.remove(deviceId); mVirtualDevices.remove(deviceId); } } class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub { private final VirtualDeviceImpl.PendingTrampolineCallback mPendingTrampolineCallback = new VirtualDeviceImpl.PendingTrampolineCallback() { @Override public void startWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline) { PendingTrampoline existing = mPendingTrampolines.put( pendingTrampoline.mPendingIntent.getCreatorPackage(), pendingTrampoline); if (existing != null) { existing.mResultReceiver.send( VirtualDeviceManager.LAUNCH_FAILURE_NO_ACTIVITY, null); } } @Override public void stopWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline) { mPendingTrampolines.remove(pendingTrampoline.mPendingIntent.getCreatorPackage()); } }; @Override // Binder call public IVirtualDevice createVirtualDevice( Loading @@ -279,25 +246,16 @@ public class VirtualDeviceManagerService extends SystemService { throw new IllegalArgumentException("No association with ID " + associationId); } synchronized (mVirtualDeviceManagerLock) { final int userId = UserHandle.getUserId(callingUid); final UserHandle userHandle = getCallingUserHandle(); final CameraAccessController cameraAccessController = mCameraAccessControllers.get(userId); getCameraAccessController(userHandle); final int deviceId = sNextUniqueIndex.getAndIncrement(); final Consumer<ArraySet<Integer>> runningAppsChangedCallback = runningUids -> notifyRunningAppsChanged(deviceId, runningUids); VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl(getContext(), associationInfo, token, callingUid, deviceId, /* onDeviceCloseListener= */ this::onDeviceClosed, this, activityListener, runningUids -> { cameraAccessController.blockCameraAccessIfNeeded(runningUids); notifyRunningAppsChanged(deviceId, runningUids); }, params); if (cameraAccessController != null) { cameraAccessController.startObservingIfNeeded(); mCameraAccessControllersByDeviceId.put(deviceId, cameraAccessController); } else { Slog.w(TAG, "cameraAccessController not found for user " + userId); } associationInfo, token, callingUid, deviceId, cameraAccessController, this::onDeviceClosed, mPendingTrampolineCallback, activityListener, runningAppsChangedCallback, params); mVirtualDevices.put(deviceId, virtualDevice); return virtualDevice; } Loading Loading @@ -409,8 +367,18 @@ public class VirtualDeviceManagerService extends SystemService { @Nullable private AssociationInfo getAssociationInfo(String packageName, int associationId) { final int callingUserId = getCallingUserHandle().getIdentifier(); final List<AssociationInfo> associations = mAllAssociations.get(callingUserId); final UserHandle userHandle = getCallingUserHandle(); final CompanionDeviceManager cdm = getContext().createContextAsUser(userHandle, 0) .getSystemService(CompanionDeviceManager.class); List<AssociationInfo> associations; final long identity = Binder.clearCallingIdentity(); try { associations = cdm.getAllAssociations(); } finally { Binder.restoreCallingIdentity(identity); } final int callingUserId = userHandle.getIdentifier(); if (associations != null) { final int associationSize = associations.size(); for (int i = 0; i < associationSize; i++) { Loading @@ -427,8 +395,7 @@ public class VirtualDeviceManagerService extends SystemService { } private void onDeviceClosed(int deviceId) { synchronized (mVirtualDeviceManagerLock) { mVirtualDevices.remove(deviceId); removeVirtualDevice(deviceId); Intent i = new Intent(VirtualDeviceManager.ACTION_VIRTUAL_DEVICE_REMOVED); i.putExtra(VirtualDeviceManager.EXTRA_VIRTUAL_DEVICE_ID, deviceId); i.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); Loading @@ -438,15 +405,6 @@ public class VirtualDeviceManagerService extends SystemService { } finally { Binder.restoreCallingIdentity(identity); } mAppsOnVirtualDevices.remove(deviceId); final CameraAccessController cameraAccessController = mCameraAccessControllersByDeviceId.removeReturnOld(deviceId); if (cameraAccessController != null) { cameraAccessController.stopObservingIfNeeded(); } else { Slog.w(TAG, "cameraAccessController not found for device Id " + deviceId); } } } @Override Loading Loading @@ -474,22 +432,6 @@ public class VirtualDeviceManagerService extends SystemService { } } } @Override public void startWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline) { PendingTrampoline existing = mPendingTrampolines.put( pendingTrampoline.mPendingIntent.getCreatorPackage(), pendingTrampoline); if (existing != null) { existing.mResultReceiver.send( VirtualDeviceManager.LAUNCH_FAILURE_NO_ACTIVITY, null); } } @Override public void stopWaitingForPendingTrampoline(PendingTrampoline pendingTrampoline) { mPendingTrampolines.remove(pendingTrampoline.mPendingIntent.getCreatorPackage()); } } private final class LocalService extends VirtualDeviceManagerInternal { Loading
services/tests/mockingservicestests/src/com/android/server/companion/virtual/CameraAccessControllerTest.java +7 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,8 @@ package com.android.server.companion.virtual; import static android.hardware.camera2.CameraInjectionSession.InjectionStatusCallback.ERROR_INJECTION_UNSUPPORTED; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; Loading Loading @@ -115,6 +117,11 @@ public class CameraAccessControllerTest { mController.startObservingIfNeeded(); } @Test public void getUserId_returnsCorrectId() { assertThat(mController.getUserId()).isEqualTo(mContext.getUserId()); } @Test public void onCameraOpened_uidNotRunning_noCameraBlocking() throws CameraAccessException { when(mDeviceManagerInternal.isAppRunningOnAnyVirtualDevice( Loading
services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java +29 −10 Original line number Diff line number Diff line Loading @@ -203,6 +203,7 @@ public class VirtualDeviceManagerServiceTest { private VirtualDeviceImpl mDeviceImpl; private InputController mInputController; private SensorController mSensorController; private CameraAccessController mCameraAccessController; private AssociationInfo mAssociationInfo; private VirtualDeviceManagerService mVdms; private VirtualDeviceManagerInternal mLocalService; Loading Loading @@ -237,6 +238,8 @@ public class VirtualDeviceManagerServiceTest { @Mock private IAudioConfigChangedCallback mConfigChangedCallback; @Mock private CameraAccessController.CameraAccessBlockedCallback mCameraAccessBlockedCallback; @Mock private ApplicationInfo mApplicationInfoMock; @Mock IInputManager mIInputManagerMock; Loading Loading @@ -325,6 +328,8 @@ public class VirtualDeviceManagerServiceTest { new Handler(TestableLooper.get(this).getLooper()), mContext.getSystemService(WindowManager.class), threadVerifier); mSensorController = new SensorController(new Object(), VIRTUAL_DEVICE_ID_1); mCameraAccessController = new CameraAccessController(mContext, mLocalService, mCameraAccessBlockedCallback); mAssociationInfo = new AssociationInfo(/* associationId= */ 1, 0, null, MacAddress.BROADCAST_ADDRESS, "", null, null, true, false, false, 0, 0); Loading Loading @@ -394,12 +399,7 @@ public class VirtualDeviceManagerServiceTest { .setBlockedActivities(getBlockedActivities()) .setDevicePolicy(POLICY_TYPE_SENSORS, DEVICE_POLICY_CUSTOM) .build(); mDeviceImpl = new VirtualDeviceImpl(mContext, mAssociationInfo, new Binder(), /* ownerUid */ 0, VIRTUAL_DEVICE_ID_1, mInputController, mSensorController, /* onDeviceCloseListener= */ (int deviceId) -> {}, mPendingTrampolineCallback, mActivityListener, mRunningAppsChangedCallback, params); mVdms.addVirtualDevice(mDeviceImpl); mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1, params); assertThat(mVdm.getDevicePolicy(mDeviceImpl.getDeviceId(), POLICY_TYPE_SENSORS)) .isEqualTo(DEVICE_POLICY_CUSTOM); Loading Loading @@ -557,6 +557,21 @@ public class VirtualDeviceManagerServiceTest { LocaleList.forLanguageTags(firstKeyboardConfig.getLanguageTag())); } @Test public void cameraAccessController_observerCountUpdated() { assertThat(mCameraAccessController.getObserverCount()).isEqualTo(1); VirtualDeviceImpl secondDevice = createVirtualDevice(VIRTUAL_DEVICE_ID_2, DEVICE_OWNER_UID_2); assertThat(mCameraAccessController.getObserverCount()).isEqualTo(2); mDeviceImpl.close(); assertThat(mCameraAccessController.getObserverCount()).isEqualTo(1); secondDevice.close(); assertThat(mCameraAccessController.getObserverCount()).isEqualTo(0); } @Test public void onVirtualDisplayRemovedLocked_doesNotThrowException() { mDeviceImpl.onVirtualDisplayCreatedLocked( Loading Loading @@ -1543,14 +1558,18 @@ public class VirtualDeviceManagerServiceTest { } private VirtualDeviceImpl createVirtualDevice(int virtualDeviceId, int ownerUid) { VirtualDeviceParams params = new VirtualDeviceParams .Builder() VirtualDeviceParams params = new VirtualDeviceParams.Builder() .setBlockedActivities(getBlockedActivities()) .build(); return createVirtualDevice(virtualDeviceId, ownerUid, params); } private VirtualDeviceImpl createVirtualDevice(int virtualDeviceId, int ownerUid, VirtualDeviceParams params) { VirtualDeviceImpl virtualDeviceImpl = new VirtualDeviceImpl(mContext, mAssociationInfo, new Binder(), ownerUid, virtualDeviceId, mInputController, mSensorController, /* onDeviceCloseListener= */ (int deviceId) -> {}, mInputController, mSensorController, mCameraAccessController, /* onDeviceCloseListener= */ deviceId -> mVdms.removeVirtualDevice(deviceId), mPendingTrampolineCallback, mActivityListener, mRunningAppsChangedCallback, params); mVdms.addVirtualDevice(virtualDeviceImpl); return virtualDeviceImpl; Loading