Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a120143a authored by Cliff Wu's avatar Cliff Wu
Browse files

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
parent 646b0c13
Loading
Loading
Loading
Loading
+62 −37
Original line number Diff line number Diff line
@@ -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<Integer> 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,9 +132,9 @@ 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;
                for (int packageUid : openCameraInfo.packageUids) {
                    if (runningUids.contains(packageUid)) {
                        InjectionSessionData data = mPackageToSessionData.get(packageName);
                        if (data == null) {
                            data = new InjectionSessionData();
@@ -135,6 +142,8 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen
                            mPackageToSessionData.put(packageName, data);
                        }
                        startBlocking(packageName, cameraId);
                        break;
                    }
                }
            }
        }
@@ -155,13 +164,32 @@ 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)) {
            List<UserInfo> aliveUsers = mUserManager.getAliveUsers();
            ArraySet<Integer> 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);
                    }
                }
            }
            OpenCameraInfo openCameraInfo = new OpenCameraInfo();
            openCameraInfo.packageName = packageName;
                    openCameraInfo.packageUid = ainfo.uid;
            openCameraInfo.packageUids = packageUids;
            mAppsToBlockOnVirtualDevice.put(cameraId, openCameraInfo);
            CameraInjectionSession existingSession =
                    (data != null) ? data.cameraIdToSession.get(cameraId) : null;
@@ -172,21 +200,6 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen
                    mPackageToSessionData.remove(packageName);
                }
            }
                    return;
                }
                if (data == null) {
                    data = new InjectionSessionData();
                    data.appUid = ainfo.uid;
                    mPackageToSessionData.put(packageName, data);
                }
                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;
        }
    }
}
+90 −4
Original line number Diff line number Diff line
@@ -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<Integer> mRunningUids = new ArraySet<>();
    private List<UserInfo> mAliveUsers = new ArrayList<>();

    @Captor
    ArgumentCaptor<CameraInjectionSession.InjectionStatusCallback> 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());
    }
}