From a120143a974668dffea4805a76b61417a8f46a21 Mon Sep 17 00:00:00 2001 From: Cliff Wu Date: Sun, 11 Sep 2022 18:20:27 +0800 Subject: [PATCH] Fixed camera streaming not being blocked when user taps camera on personal streaming apps Root cause: When accessing cross profiles using personal + corp account login, etc., the virtual device always use the user id of the work profile, whether the user launches the personal profile app or the work profile app in app streaming. It will cause a NameNotFoundException to occur and cannot block the camera access when using PackageManager.getApplicationInfo() to query apps that are only installed on the personal profile. Solution: Get all the existing user ids to find out the correct uid of the app that opened the camera, and compare it with the app detected on the virtual display to decide whether to block the camera access. Bug: 245250121 Test: Manual Change-Id: Iea062154ea472d589e5ffc33998698b4b685d7bd --- .../virtual/CameraAccessController.java | 99 ++++++++++++------- .../virtual/CameraAccessControllerTest.java | 94 +++++++++++++++++- 2 files changed, 152 insertions(+), 41 deletions(-) diff --git a/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java b/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java index ec0da490adcf8..2904f28fca011 100644 --- a/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java +++ b/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java @@ -22,14 +22,19 @@ import android.annotation.NonNull; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.pm.UserInfo; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraInjectionSession; import android.hardware.camera2.CameraManager; +import android.os.Process; +import android.os.UserManager; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import java.util.List; import java.util.Set; /** @@ -45,6 +50,7 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen private final CameraAccessBlockedCallback mBlockedCallback; private final CameraManager mCameraManager; private final PackageManager mPackageManager; + private final UserManager mUserManager; @GuardedBy("mLock") private int mObserverCount = 0; @@ -66,7 +72,7 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen static class OpenCameraInfo { public String packageName; - public int packageUid; + public Set packageUids; } interface CameraAccessBlockedCallback { @@ -85,6 +91,7 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen mBlockedCallback = blockedCallback; mCameraManager = mContext.getSystemService(CameraManager.class); mPackageManager = mContext.getPackageManager(); + mUserManager = mContext.getSystemService(UserManager.class); } /** @@ -125,16 +132,18 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen 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); + final String packageName = openCameraInfo.packageName; + for (int packageUid : openCameraInfo.packageUids) { + if (runningUids.contains(packageUid)) { + InjectionSessionData data = mPackageToSessionData.get(packageName); + if (data == null) { + data = new InjectionSessionData(); + data.appUid = packageUid; + mPackageToSessionData.put(packageName, data); + } + startBlocking(packageName, cameraId); + break; } - startBlocking(packageName, cameraId); } } } @@ -155,37 +164,41 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen @Override public void onCameraOpened(@NonNull String cameraId, @NonNull String packageName) { synchronized (mLock) { - try { - 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) { - existingSession.close(); - data.cameraIdToSession.remove(cameraId); - if (data.cameraIdToSession.isEmpty()) { - mPackageToSessionData.remove(packageName); - } + InjectionSessionData data = mPackageToSessionData.get(packageName); + List aliveUsers = mUserManager.getAliveUsers(); + ArraySet packageUids = new ArraySet<>(); + for (UserInfo user : aliveUsers) { + int userId = user.getUserHandle().getIdentifier(); + int appUid = queryUidFromPackageName(userId, packageName); + if (mVirtualDeviceManagerInternal.isAppRunningOnAnyVirtualDevice(appUid)) { + if (data == null) { + data = new InjectionSessionData(); + data.appUid = appUid; + mPackageToSessionData.put(packageName, data); } + if (data.cameraIdToSession.containsKey(cameraId)) { + return; + } + startBlocking(packageName, cameraId); return; + } else { + if (appUid != Process.INVALID_UID) { + packageUids.add(appUid); + } } - if (data == null) { - data = new InjectionSessionData(); - data.appUid = ainfo.uid; - mPackageToSessionData.put(packageName, data); + } + OpenCameraInfo openCameraInfo = new OpenCameraInfo(); + openCameraInfo.packageName = packageName; + openCameraInfo.packageUids = packageUids; + mAppsToBlockOnVirtualDevice.put(cameraId, openCameraInfo); + CameraInjectionSession existingSession = + (data != null) ? data.cameraIdToSession.get(cameraId) : null; + if (existingSession != null) { + existingSession.close(); + data.cameraIdToSession.remove(cameraId); + if (data.cameraIdToSession.isEmpty()) { + mPackageToSessionData.remove(packageName); } - if (data.cameraIdToSession.containsKey(cameraId)) { - return; - } - startBlocking(packageName, cameraId); - } catch (PackageManager.NameNotFoundException e) { - Slog.e(TAG, "onCameraOpened - unknown package " + packageName, e); - return; } } } @@ -274,4 +287,16 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen } } } + + private int queryUidFromPackageName(int userId, String packageName) { + try { + final ApplicationInfo ainfo = + mPackageManager.getApplicationInfoAsUser(packageName, + PackageManager.GET_ACTIVITIES, userId); + return ainfo.uid; + } catch (PackageManager.NameNotFoundException e) { + Slog.w(TAG, "queryUidFromPackageName - unknown package " + packageName, e); + return Process.INVALID_UID; + } + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/companion/virtual/CameraAccessControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/companion/virtual/CameraAccessControllerTest.java index c4c3abc1388ee..145e66c92f148 100644 --- a/services/tests/mockingservicestests/src/com/android/server/companion/virtual/CameraAccessControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/companion/virtual/CameraAccessControllerTest.java @@ -30,10 +30,13 @@ import static org.mockito.Mockito.when; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.UserInfo; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraInjectionSession; import android.hardware.camera2.CameraManager; import android.os.Process; +import android.os.UserManager; import android.testing.TestableContext; import android.util.ArraySet; @@ -51,12 +54,17 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; +import java.util.List; + @RunWith(AndroidJUnit4.class) public class CameraAccessControllerTest { private static final String FRONT_CAMERA = "0"; private static final String REAR_CAMERA = "1"; private static final String TEST_APP_PACKAGE = "some.package"; private static final String OTHER_APP_PACKAGE = "other.package"; + private static final int PERSONAL_PROFILE_USER_ID = 0; + private static final int WORK_PROFILE_USER_ID = 10; private CameraAccessController mController; @@ -69,6 +77,8 @@ public class CameraAccessControllerTest { @Mock private PackageManager mPackageManager; @Mock + private UserManager mUserManager; + @Mock private VirtualDeviceManagerInternal mDeviceManagerInternal; @Mock private CameraAccessController.CameraAccessBlockedCallback mBlockedCallback; @@ -76,6 +86,7 @@ public class CameraAccessControllerTest { private ApplicationInfo mTestAppInfo = new ApplicationInfo(); private ApplicationInfo mOtherAppInfo = new ApplicationInfo(); private ArraySet mRunningUids = new ArraySet<>(); + private List mAliveUsers = new ArrayList<>(); @Captor ArgumentCaptor mInjectionCallbackCaptor; @@ -84,6 +95,7 @@ public class CameraAccessControllerTest { public void setUp() throws PackageManager.NameNotFoundException { MockitoAnnotations.initMocks(this); mContext.addMockSystemService(CameraManager.class, mCameraManager); + mContext.addMockSystemService(UserManager.class, mUserManager); mContext.setMockPackageManager(mPackageManager); LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class); LocalServices.addService(VirtualDeviceManagerInternal.class, mDeviceManagerInternal); @@ -92,10 +104,14 @@ public class CameraAccessControllerTest { mTestAppInfo.uid = Process.FIRST_APPLICATION_UID; mOtherAppInfo.uid = Process.FIRST_APPLICATION_UID + 1; mRunningUids.add(Process.FIRST_APPLICATION_UID); - when(mPackageManager.getApplicationInfo(eq(TEST_APP_PACKAGE), anyInt())).thenReturn( - mTestAppInfo); - when(mPackageManager.getApplicationInfo(eq(OTHER_APP_PACKAGE), anyInt())).thenReturn( - mOtherAppInfo); + mAliveUsers.add(new UserInfo(PERSONAL_PROFILE_USER_ID, "", 0)); + when(mPackageManager.getApplicationInfoAsUser( + eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES), + anyInt())).thenReturn(mTestAppInfo); + when(mPackageManager.getApplicationInfoAsUser( + eq(OTHER_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES), + anyInt())).thenReturn(mOtherAppInfo); + when(mUserManager.getAliveUsers()).thenReturn(mAliveUsers); mController.startObservingIfNeeded(); } @@ -227,4 +243,74 @@ public class CameraAccessControllerTest { verify(mCameraManager, times(1)).injectCamera(any(), any(), any(), any(), any()); } + + @Test + public void multipleUsers_getPersonalProfileAppUid_cameraBlocked() + throws CameraAccessException, NameNotFoundException { + mAliveUsers.add(new UserInfo(WORK_PROFILE_USER_ID, "", 0)); + when(mPackageManager.getApplicationInfoAsUser( + eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES), + eq(PERSONAL_PROFILE_USER_ID))).thenReturn(mTestAppInfo); + when(mPackageManager.getApplicationInfoAsUser( + eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES), + eq(WORK_PROFILE_USER_ID))).thenThrow(NameNotFoundException.class); + when(mDeviceManagerInternal.isAppRunningOnAnyVirtualDevice( + eq(mTestAppInfo.uid))).thenReturn(true); + mController.onCameraOpened(FRONT_CAMERA, TEST_APP_PACKAGE); + + verify(mCameraManager).injectCamera(eq(TEST_APP_PACKAGE), eq(FRONT_CAMERA), anyString(), + any(), any()); + } + + @Test + public void multipleUsers_getPersonalProfileAppUid_noCameraBlocking() + throws CameraAccessException, NameNotFoundException { + mAliveUsers.add(new UserInfo(WORK_PROFILE_USER_ID, "", 0)); + when(mPackageManager.getApplicationInfoAsUser( + eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES), + eq(PERSONAL_PROFILE_USER_ID))).thenReturn(mTestAppInfo); + when(mPackageManager.getApplicationInfoAsUser( + eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES), + eq(WORK_PROFILE_USER_ID))).thenThrow(NameNotFoundException.class); + when(mDeviceManagerInternal.isAppRunningOnAnyVirtualDevice( + eq(mTestAppInfo.uid))).thenReturn(false); + mController.onCameraOpened(FRONT_CAMERA, TEST_APP_PACKAGE); + + verify(mCameraManager, never()).injectCamera(any(), any(), any(), any(), any()); + } + + @Test + public void multipleUsers_getWorkProfileAppUid_cameraBlocked() + throws CameraAccessException, NameNotFoundException { + mAliveUsers.add(new UserInfo(WORK_PROFILE_USER_ID, "", 0)); + when(mPackageManager.getApplicationInfoAsUser( + eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES), + eq(PERSONAL_PROFILE_USER_ID))).thenThrow(NameNotFoundException.class); + when(mPackageManager.getApplicationInfoAsUser( + eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES), + eq(WORK_PROFILE_USER_ID))).thenReturn(mTestAppInfo); + when(mDeviceManagerInternal.isAppRunningOnAnyVirtualDevice( + eq(mTestAppInfo.uid))).thenReturn(true); + mController.onCameraOpened(FRONT_CAMERA, TEST_APP_PACKAGE); + + verify(mCameraManager).injectCamera(eq(TEST_APP_PACKAGE), eq(FRONT_CAMERA), anyString(), + any(), any()); + } + + @Test + public void multipleUsers_getWorkProfileAppUid_noCameraBlocking() + throws CameraAccessException, NameNotFoundException { + mAliveUsers.add(new UserInfo(WORK_PROFILE_USER_ID, "", 0)); + when(mPackageManager.getApplicationInfoAsUser( + eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES), + eq(PERSONAL_PROFILE_USER_ID))).thenThrow(NameNotFoundException.class); + when(mPackageManager.getApplicationInfoAsUser( + eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES), + eq(WORK_PROFILE_USER_ID))).thenReturn(mTestAppInfo); + when(mDeviceManagerInternal.isAppRunningOnAnyVirtualDevice( + eq(mTestAppInfo.uid))).thenReturn(false); + mController.onCameraOpened(FRONT_CAMERA, TEST_APP_PACKAGE); + + verify(mCameraManager, never()).injectCamera(any(), any(), any(), any(), any()); + } }