Loading services/companion/java/com/android/server/companion/virtual/CameraAccessController.java +50 −2 Original line number Diff line number Diff line Loading @@ -30,6 +30,8 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import java.util.Set; /** * Handles blocking access to the camera for apps running on virtual devices. */ Loading @@ -50,11 +52,23 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen @GuardedBy("mLock") private ArrayMap<String, InjectionSessionData> mPackageToSessionData = new ArrayMap<>(); /** * Mapping from camera ID to open camera app associations. Key is the camera id, value is the * information of the app's uid and package name. */ @GuardedBy("mLock") private ArrayMap<String, OpenCameraInfo> mAppsToBlockOnVirtualDevice = new ArrayMap<>(); static class InjectionSessionData { public int appUid; public ArrayMap<String, CameraInjectionSession> cameraIdToSession = new ArrayMap<>(); } static class OpenCameraInfo { public String packageName; public int packageUid; } interface CameraAccessBlockedCallback { /** * Called whenever an app was blocked from accessing a camera. Loading Loading @@ -98,6 +112,33 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen } } /** * Need to block camera access for applications running on virtual displays. * <p> * Apps that open the camera on the main display will need to block camera access if moved to a * virtual display. * * @param runningUids uids of the application running on the virtual display */ public void blockCameraAccessIfNeeded(Set<Integer> runningUids) { synchronized (mLock) { for (int i = 0; i < mAppsToBlockOnVirtualDevice.size(); i++) { final String cameraId = mAppsToBlockOnVirtualDevice.keyAt(i); final OpenCameraInfo openCameraInfo = mAppsToBlockOnVirtualDevice.get(cameraId); int packageUid = openCameraInfo.packageUid; if (runningUids.contains(packageUid)) { final String packageName = openCameraInfo.packageName; InjectionSessionData data = mPackageToSessionData.get(packageName); if (data == null) { data = new InjectionSessionData(); data.appUid = packageUid; mPackageToSessionData.put(packageName, data); } startBlocking(packageName, cameraId); } } } } @Override public void close() { Loading @@ -115,10 +156,13 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen public void onCameraOpened(@NonNull String cameraId, @NonNull String packageName) { synchronized (mLock) { try { final ApplicationInfo ainfo = mPackageManager.getApplicationInfo(packageName, 0); final ApplicationInfo ainfo = mPackageManager.getApplicationInfo(packageName, 0); InjectionSessionData data = mPackageToSessionData.get(packageName); if (!mVirtualDeviceManagerInternal.isAppRunningOnAnyVirtualDevice(ainfo.uid)) { OpenCameraInfo openCameraInfo = new OpenCameraInfo(); openCameraInfo.packageName = packageName; openCameraInfo.packageUid = ainfo.uid; mAppsToBlockOnVirtualDevice.put(cameraId, openCameraInfo); CameraInjectionSession existingSession = (data != null) ? data.cameraIdToSession.get(cameraId) : null; if (existingSession != null) { Loading Loading @@ -149,6 +193,7 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen @Override public void onCameraClosed(@NonNull String cameraId) { synchronized (mLock) { mAppsToBlockOnVirtualDevice.remove(cameraId); for (int i = mPackageToSessionData.size() - 1; i >= 0; i--) { InjectionSessionData data = mPackageToSessionData.valueAt(i); CameraInjectionSession session = data.cameraIdToSession.get(cameraId); Loading @@ -168,6 +213,9 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen */ private void startBlocking(String packageName, String cameraId) { try { Slog.d( TAG, "startBlocking() cameraId: " + cameraId + " packageName: " + packageName); mCameraManager.injectCamera(packageName, cameraId, /* externalCamId */ "", mContext.getMainExecutor(), new CameraInjectionSession.InjectionStatusCallback() { Loading services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java +15 −9 Original line number Diff line number Diff line Loading @@ -93,9 +93,8 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController final ArraySet<Integer> mRunningUids = new ArraySet<>(); @Nullable private final ActivityListener mActivityListener; private final Handler mHandler = new Handler(Looper.getMainLooper()); @Nullable private RunningAppsChangedListener mRunningAppsChangedListener; private final ArraySet<RunningAppsChangedListener> mRunningAppsChangedListener = new ArraySet<>(); /** * Creates a window policy controller that is generic to the different use cases of virtual Loading Loading @@ -142,9 +141,14 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController mActivityListener = activityListener; } /** Sets listener for running applications change. */ public void setRunningAppsChangedListener(@Nullable RunningAppsChangedListener listener) { mRunningAppsChangedListener = listener; /** Register a listener for running applications changes. */ public void registerRunningAppsChangedListener(@NonNull RunningAppsChangedListener listener) { mRunningAppsChangedListener.add(listener); } /** Unregister a listener for running applications changes. */ public void unregisterRunningAppsChangedListener(@NonNull RunningAppsChangedListener listener) { mRunningAppsChangedListener.remove(listener); } @Override Loading Loading @@ -237,9 +241,11 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController mHandler.post(() -> mActivityListener.onDisplayEmpty(Display.INVALID_DISPLAY)); } } if (mRunningAppsChangedListener != null) { mRunningAppsChangedListener.onRunningAppsChanged(runningUids); mHandler.post(() -> { for (RunningAppsChangedListener listener : mRunningAppsChangedListener) { listener.onRunningAppsChanged(runningUids); } }); } /** Loading services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +24 −6 Original line number Diff line number Diff line Loading @@ -68,16 +68,18 @@ import android.window.DisplayWindowPolicyController; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.BlockedAppStreamingActivity; import com.android.server.companion.virtual.GenericWindowPolicyController.RunningAppsChangedListener; import com.android.server.companion.virtual.audio.VirtualAudioController; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Map; import java.util.Set; import java.util.function.Consumer; final class VirtualDeviceImpl extends IVirtualDevice.Stub implements IBinder.DeathRecipient { implements IBinder.DeathRecipient, RunningAppsChangedListener { private static final String TAG = "VirtualDeviceImpl"; Loading @@ -101,6 +103,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub private final VirtualDeviceParams mParams; private final Map<Integer, PowerManager.WakeLock> mPerDisplayWakelocks = new ArrayMap<>(); private final IVirtualDeviceActivityListener mActivityListener; @NonNull private Consumer<ArraySet<Integer>> mRunningAppsChangedCallback; // The default setting for showing the pointer on new displays. @GuardedBy("mVirtualDeviceLock") private boolean mDefaultShowPointerIcon = true; Loading Loading @@ -139,21 +143,25 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub IBinder token, int ownerUid, OnDeviceCloseListener listener, PendingTrampolineCallback pendingTrampolineCallback, IVirtualDeviceActivityListener activityListener, Consumer<ArraySet<Integer>> runningAppsChangedCallback, VirtualDeviceParams params) { this(context, associationInfo, token, ownerUid, /* inputController= */ null, listener, pendingTrampolineCallback, activityListener, params); pendingTrampolineCallback, activityListener, runningAppsChangedCallback, params); } @VisibleForTesting VirtualDeviceImpl(Context context, AssociationInfo associationInfo, IBinder token, int ownerUid, InputController inputController, OnDeviceCloseListener listener, PendingTrampolineCallback pendingTrampolineCallback, IVirtualDeviceActivityListener activityListener, VirtualDeviceParams params) { IVirtualDeviceActivityListener activityListener, Consumer<ArraySet<Integer>> runningAppsChangedCallback, VirtualDeviceParams params) { UserHandle ownerUserHandle = UserHandle.getUserHandleForUid(ownerUid); mContext = context.createContextAsUser(ownerUserHandle, 0); mAssociationInfo = associationInfo; mPendingTrampolineCallback = pendingTrampolineCallback; mActivityListener = activityListener; mRunningAppsChangedCallback = runningAppsChangedCallback; mOwnerUid = ownerUid; mAppToken = token; mParams = params; Loading Loading @@ -278,6 +286,11 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub close(); } @Override public void onRunningAppsChanged(ArraySet<Integer> runningUids) { mRunningAppsChangedCallback.accept(runningUids); } @VisibleForTesting VirtualAudioController getVirtualAudioControllerForTesting() { return mVirtualAudioController; Loading Loading @@ -529,7 +542,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub // reentrancy problems. mContext.getMainThreadHandler().post(() -> addWakeLockForDisplay(displayId)); final GenericWindowPolicyController dwpc = final GenericWindowPolicyController gwpc = new GenericWindowPolicyController(FLAG_SECURE, SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, getAllowedUserHandles(), Loading @@ -540,8 +553,9 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub mParams.getDefaultActivityPolicy(), createListenerAdapter(displayId), activityInfo -> onActivityBlocked(displayId, activityInfo)); mWindowPolicyControllers.put(displayId, dwpc); return dwpc; gwpc.registerRunningAppsChangedListener(/* listener= */ this); mWindowPolicyControllers.put(displayId, gwpc); return gwpc; } } Loading Loading @@ -599,6 +613,10 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub wakeLock.release(); mPerDisplayWakelocks.remove(displayId); } GenericWindowPolicyController gwpc = mWindowPolicyControllers.get(displayId); if (gwpc != null) { gwpc.unregisterRunningAppsChangedListener(/* listener= */ this); } mVirtualDisplayIds.remove(displayId); mWindowPolicyControllers.remove(displayId); } Loading services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +4 −1 Original line number Diff line number Diff line Loading @@ -251,7 +251,10 @@ public class VirtualDeviceManagerService extends SystemService { } } }, this, activityListener, params); this, activityListener, runningUids -> cameraAccessController.blockCameraAccessIfNeeded( runningUids), params); if (cameraAccessController != null) { cameraAccessController.startObservingIfNeeded(); } else { Loading services/companion/java/com/android/server/companion/virtual/audio/VirtualAudioController.java +3 −2 Original line number Diff line number Diff line Loading @@ -85,7 +85,7 @@ public final class VirtualAudioController implements AudioPlaybackCallback, @NonNull IAudioRoutingCallback routingCallback, @Nullable IAudioConfigChangedCallback configChangedCallback) { mGenericWindowPolicyController = genericWindowPolicyController; mGenericWindowPolicyController.setRunningAppsChangedListener(/* listener= */ this); mGenericWindowPolicyController.registerRunningAppsChangedListener(/* listener= */ this); synchronized (mCallbackLock) { mRoutingCallback = routingCallback; mConfigChangedCallback = configChangedCallback; Loading @@ -111,7 +111,8 @@ public final class VirtualAudioController implements AudioPlaybackCallback, mAudioPlaybackDetector.unregister(); mAudioRecordingDetector.unregister(); if (mGenericWindowPolicyController != null) { mGenericWindowPolicyController.setRunningAppsChangedListener(/* listener= */ null); mGenericWindowPolicyController.unregisterRunningAppsChangedListener( /* listener= */ this); mGenericWindowPolicyController = null; } synchronized (mCallbackLock) { Loading Loading
services/companion/java/com/android/server/companion/virtual/CameraAccessController.java +50 −2 Original line number Diff line number Diff line Loading @@ -30,6 +30,8 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import java.util.Set; /** * Handles blocking access to the camera for apps running on virtual devices. */ Loading @@ -50,11 +52,23 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen @GuardedBy("mLock") private ArrayMap<String, InjectionSessionData> mPackageToSessionData = new ArrayMap<>(); /** * Mapping from camera ID to open camera app associations. Key is the camera id, value is the * information of the app's uid and package name. */ @GuardedBy("mLock") private ArrayMap<String, OpenCameraInfo> mAppsToBlockOnVirtualDevice = new ArrayMap<>(); static class InjectionSessionData { public int appUid; public ArrayMap<String, CameraInjectionSession> cameraIdToSession = new ArrayMap<>(); } static class OpenCameraInfo { public String packageName; public int packageUid; } interface CameraAccessBlockedCallback { /** * Called whenever an app was blocked from accessing a camera. Loading Loading @@ -98,6 +112,33 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen } } /** * Need to block camera access for applications running on virtual displays. * <p> * Apps that open the camera on the main display will need to block camera access if moved to a * virtual display. * * @param runningUids uids of the application running on the virtual display */ public void blockCameraAccessIfNeeded(Set<Integer> runningUids) { synchronized (mLock) { for (int i = 0; i < mAppsToBlockOnVirtualDevice.size(); i++) { final String cameraId = mAppsToBlockOnVirtualDevice.keyAt(i); final OpenCameraInfo openCameraInfo = mAppsToBlockOnVirtualDevice.get(cameraId); int packageUid = openCameraInfo.packageUid; if (runningUids.contains(packageUid)) { final String packageName = openCameraInfo.packageName; InjectionSessionData data = mPackageToSessionData.get(packageName); if (data == null) { data = new InjectionSessionData(); data.appUid = packageUid; mPackageToSessionData.put(packageName, data); } startBlocking(packageName, cameraId); } } } } @Override public void close() { Loading @@ -115,10 +156,13 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen public void onCameraOpened(@NonNull String cameraId, @NonNull String packageName) { synchronized (mLock) { try { final ApplicationInfo ainfo = mPackageManager.getApplicationInfo(packageName, 0); final ApplicationInfo ainfo = mPackageManager.getApplicationInfo(packageName, 0); InjectionSessionData data = mPackageToSessionData.get(packageName); if (!mVirtualDeviceManagerInternal.isAppRunningOnAnyVirtualDevice(ainfo.uid)) { OpenCameraInfo openCameraInfo = new OpenCameraInfo(); openCameraInfo.packageName = packageName; openCameraInfo.packageUid = ainfo.uid; mAppsToBlockOnVirtualDevice.put(cameraId, openCameraInfo); CameraInjectionSession existingSession = (data != null) ? data.cameraIdToSession.get(cameraId) : null; if (existingSession != null) { Loading Loading @@ -149,6 +193,7 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen @Override public void onCameraClosed(@NonNull String cameraId) { synchronized (mLock) { mAppsToBlockOnVirtualDevice.remove(cameraId); for (int i = mPackageToSessionData.size() - 1; i >= 0; i--) { InjectionSessionData data = mPackageToSessionData.valueAt(i); CameraInjectionSession session = data.cameraIdToSession.get(cameraId); Loading @@ -168,6 +213,9 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen */ private void startBlocking(String packageName, String cameraId) { try { Slog.d( TAG, "startBlocking() cameraId: " + cameraId + " packageName: " + packageName); mCameraManager.injectCamera(packageName, cameraId, /* externalCamId */ "", mContext.getMainExecutor(), new CameraInjectionSession.InjectionStatusCallback() { Loading
services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java +15 −9 Original line number Diff line number Diff line Loading @@ -93,9 +93,8 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController final ArraySet<Integer> mRunningUids = new ArraySet<>(); @Nullable private final ActivityListener mActivityListener; private final Handler mHandler = new Handler(Looper.getMainLooper()); @Nullable private RunningAppsChangedListener mRunningAppsChangedListener; private final ArraySet<RunningAppsChangedListener> mRunningAppsChangedListener = new ArraySet<>(); /** * Creates a window policy controller that is generic to the different use cases of virtual Loading Loading @@ -142,9 +141,14 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController mActivityListener = activityListener; } /** Sets listener for running applications change. */ public void setRunningAppsChangedListener(@Nullable RunningAppsChangedListener listener) { mRunningAppsChangedListener = listener; /** Register a listener for running applications changes. */ public void registerRunningAppsChangedListener(@NonNull RunningAppsChangedListener listener) { mRunningAppsChangedListener.add(listener); } /** Unregister a listener for running applications changes. */ public void unregisterRunningAppsChangedListener(@NonNull RunningAppsChangedListener listener) { mRunningAppsChangedListener.remove(listener); } @Override Loading Loading @@ -237,9 +241,11 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController mHandler.post(() -> mActivityListener.onDisplayEmpty(Display.INVALID_DISPLAY)); } } if (mRunningAppsChangedListener != null) { mRunningAppsChangedListener.onRunningAppsChanged(runningUids); mHandler.post(() -> { for (RunningAppsChangedListener listener : mRunningAppsChangedListener) { listener.onRunningAppsChanged(runningUids); } }); } /** Loading
services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +24 −6 Original line number Diff line number Diff line Loading @@ -68,16 +68,18 @@ import android.window.DisplayWindowPolicyController; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.BlockedAppStreamingActivity; import com.android.server.companion.virtual.GenericWindowPolicyController.RunningAppsChangedListener; import com.android.server.companion.virtual.audio.VirtualAudioController; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Map; import java.util.Set; import java.util.function.Consumer; final class VirtualDeviceImpl extends IVirtualDevice.Stub implements IBinder.DeathRecipient { implements IBinder.DeathRecipient, RunningAppsChangedListener { private static final String TAG = "VirtualDeviceImpl"; Loading @@ -101,6 +103,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub private final VirtualDeviceParams mParams; private final Map<Integer, PowerManager.WakeLock> mPerDisplayWakelocks = new ArrayMap<>(); private final IVirtualDeviceActivityListener mActivityListener; @NonNull private Consumer<ArraySet<Integer>> mRunningAppsChangedCallback; // The default setting for showing the pointer on new displays. @GuardedBy("mVirtualDeviceLock") private boolean mDefaultShowPointerIcon = true; Loading Loading @@ -139,21 +143,25 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub IBinder token, int ownerUid, OnDeviceCloseListener listener, PendingTrampolineCallback pendingTrampolineCallback, IVirtualDeviceActivityListener activityListener, Consumer<ArraySet<Integer>> runningAppsChangedCallback, VirtualDeviceParams params) { this(context, associationInfo, token, ownerUid, /* inputController= */ null, listener, pendingTrampolineCallback, activityListener, params); pendingTrampolineCallback, activityListener, runningAppsChangedCallback, params); } @VisibleForTesting VirtualDeviceImpl(Context context, AssociationInfo associationInfo, IBinder token, int ownerUid, InputController inputController, OnDeviceCloseListener listener, PendingTrampolineCallback pendingTrampolineCallback, IVirtualDeviceActivityListener activityListener, VirtualDeviceParams params) { IVirtualDeviceActivityListener activityListener, Consumer<ArraySet<Integer>> runningAppsChangedCallback, VirtualDeviceParams params) { UserHandle ownerUserHandle = UserHandle.getUserHandleForUid(ownerUid); mContext = context.createContextAsUser(ownerUserHandle, 0); mAssociationInfo = associationInfo; mPendingTrampolineCallback = pendingTrampolineCallback; mActivityListener = activityListener; mRunningAppsChangedCallback = runningAppsChangedCallback; mOwnerUid = ownerUid; mAppToken = token; mParams = params; Loading Loading @@ -278,6 +286,11 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub close(); } @Override public void onRunningAppsChanged(ArraySet<Integer> runningUids) { mRunningAppsChangedCallback.accept(runningUids); } @VisibleForTesting VirtualAudioController getVirtualAudioControllerForTesting() { return mVirtualAudioController; Loading Loading @@ -529,7 +542,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub // reentrancy problems. mContext.getMainThreadHandler().post(() -> addWakeLockForDisplay(displayId)); final GenericWindowPolicyController dwpc = final GenericWindowPolicyController gwpc = new GenericWindowPolicyController(FLAG_SECURE, SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, getAllowedUserHandles(), Loading @@ -540,8 +553,9 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub mParams.getDefaultActivityPolicy(), createListenerAdapter(displayId), activityInfo -> onActivityBlocked(displayId, activityInfo)); mWindowPolicyControllers.put(displayId, dwpc); return dwpc; gwpc.registerRunningAppsChangedListener(/* listener= */ this); mWindowPolicyControllers.put(displayId, gwpc); return gwpc; } } Loading Loading @@ -599,6 +613,10 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub wakeLock.release(); mPerDisplayWakelocks.remove(displayId); } GenericWindowPolicyController gwpc = mWindowPolicyControllers.get(displayId); if (gwpc != null) { gwpc.unregisterRunningAppsChangedListener(/* listener= */ this); } mVirtualDisplayIds.remove(displayId); mWindowPolicyControllers.remove(displayId); } Loading
services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +4 −1 Original line number Diff line number Diff line Loading @@ -251,7 +251,10 @@ public class VirtualDeviceManagerService extends SystemService { } } }, this, activityListener, params); this, activityListener, runningUids -> cameraAccessController.blockCameraAccessIfNeeded( runningUids), params); if (cameraAccessController != null) { cameraAccessController.startObservingIfNeeded(); } else { Loading
services/companion/java/com/android/server/companion/virtual/audio/VirtualAudioController.java +3 −2 Original line number Diff line number Diff line Loading @@ -85,7 +85,7 @@ public final class VirtualAudioController implements AudioPlaybackCallback, @NonNull IAudioRoutingCallback routingCallback, @Nullable IAudioConfigChangedCallback configChangedCallback) { mGenericWindowPolicyController = genericWindowPolicyController; mGenericWindowPolicyController.setRunningAppsChangedListener(/* listener= */ this); mGenericWindowPolicyController.registerRunningAppsChangedListener(/* listener= */ this); synchronized (mCallbackLock) { mRoutingCallback = routingCallback; mConfigChangedCallback = configChangedCallback; Loading @@ -111,7 +111,8 @@ public final class VirtualAudioController implements AudioPlaybackCallback, mAudioPlaybackDetector.unregister(); mAudioRecordingDetector.unregister(); if (mGenericWindowPolicyController != null) { mGenericWindowPolicyController.setRunningAppsChangedListener(/* listener= */ null); mGenericWindowPolicyController.unregisterRunningAppsChangedListener( /* listener= */ this); mGenericWindowPolicyController = null; } synchronized (mCallbackLock) { Loading