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

Commit 41ebd5be authored by Vadim Caen's avatar Vadim Caen
Browse files

Allow apps holding OP_PROJECT_MEDIA to record lock screen

Bug: 367301791
Test: com.android.server.media.projection.MediaProjectionManagerServiceTest#testCreateProjection_keyguardLocked_AppOpMediaProjection
Flag: android.companion.virtualdevice.flags.mediaProjectionKeyguardRestrictions
Change-Id: I8ebe13f8fe46d60c23fe523fa8f17e2f7e008ea0
parent ce12c494
Loading
Loading
Loading
Loading
+10 −1
Original line number Diff line number Diff line
@@ -179,7 +179,8 @@ public final class MediaProjectionManagerService extends SystemService
    /**
     * In order to record the keyguard, the MediaProjection package must be either:
     *   - a holder of RECORD_SENSITIVE_CONTENT permission, or
     *   - be one of the bugreport whitelisted packages
     *   - be one of the bugreport allowlisted packages, or
     *   - hold the OP_PROJECT_MEDIA AppOp.
     */
    private boolean canCaptureKeyguard() {
        if (!android.companion.virtualdevice.flags.Flags.mediaProjectionKeyguardRestrictions()) {
@@ -194,6 +195,14 @@ public final class MediaProjectionManagerService extends SystemService
                    == PackageManager.PERMISSION_GRANTED) {
                return true;
            }
            boolean operationActive = mAppOps.isOperationActive(AppOpsManager.OP_PROJECT_MEDIA,
                    mProjectionGrant.uid,
                    mProjectionGrant.packageName);
            if (operationActive) {
                // Some tools use media projection by granting the OP_PROJECT_MEDIA app
                // op via a shell command. Those tools can be granted keyguard capture
                return true;
            }
            return SystemConfig.getInstance().getBugreportWhitelistedPackages()
                    .contains(mProjectionGrant.packageName);
        }
+35 −7
Original line number Diff line number Diff line
@@ -50,11 +50,12 @@ import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;

import android.annotation.SuppressLint;
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions.LaunchCookie;
import android.app.AppOpsManager;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.ApplicationInfoFlags;
@@ -72,6 +73,7 @@ import android.os.test.TestLooper;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.TestableContext;
import android.view.ContentRecordingSession;
import android.view.ContentRecordingSession.RecordContent;

@@ -99,13 +101,14 @@ import java.util.concurrent.TimeUnit;

/**
 * Tests for the {@link MediaProjectionManagerService} class.
 *
 * <p>
 * Build/Install/Run:
 * atest FrameworksServicesTests:MediaProjectionManagerServiceTest
 */
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4.class)
@SuppressLint({"UseCheckPermission", "VisibleForTests", "MissingPermission"})
public class MediaProjectionManagerServiceTest {
    private static final int UID = 10;
    private static final String PACKAGE_NAME = "test.package";
@@ -151,7 +154,10 @@ public class MediaProjectionManagerServiceTest {
                }
            };

    private Context mContext;
    @Rule
    public final TestableContext mContext = spy(
            new TestableContext(InstrumentationRegistry.getInstrumentation().getContext()));

    private MediaProjectionManagerService mService;
    private OffsettableClock mClock;
    private ContentRecordingSession mWaitingDisplaySession =
@@ -169,6 +175,8 @@ public class MediaProjectionManagerServiceTest {
    @Mock
    private KeyguardManager mKeyguardManager;
    @Mock
    AppOpsManager mAppOpsManager;
    @Mock
    private IMediaProjectionWatcherCallback mWatcherCallback;
    @Mock
    private MediaProjectionMetricsLogger mMediaProjectionMetricsLogger;
@@ -185,10 +193,9 @@ public class MediaProjectionManagerServiceTest {
        LocalServices.removeServiceForTest(WindowManagerInternal.class);
        LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal);

        mContext = spy(new ContextWrapper(
                InstrumentationRegistry.getInstrumentation().getTargetContext()));
        doReturn(mPackageManager).when(mContext).getPackageManager();
        doReturn(mKeyguardManager).when(mContext).getSystemService(eq(Context.KEYGUARD_SERVICE));
        mContext.addMockSystemService(AppOpsManager.class, mAppOpsManager);
        mContext.addMockSystemService(KeyguardManager.class, mKeyguardManager);
        mContext.setMockPackageManager(mPackageManager);

        mClock = new OffsettableClock.Stopped();
        mWaitingDisplaySession.setWaitingForConsent(true);
@@ -291,6 +298,27 @@ public class MediaProjectionManagerServiceTest {
        assertThat(mService.getActiveProjectionInfo()).isNotNull();
    }

    @SuppressLint("MissingPermission")
    @EnableFlags(android.companion.virtualdevice.flags
            .Flags.FLAG_MEDIA_PROJECTION_KEYGUARD_RESTRICTIONS)
    @Test
    public void testCreateProjection_keyguardLocked_AppOpMediaProjection()
            throws NameNotFoundException {
        MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
        doReturn(true).when(mAppOpsManager).isOperationActive(eq(AppOpsManager.OP_PROJECT_MEDIA),
                eq(projection.uid), eq(projection.packageName));
        doReturn(true).when(mKeyguardManager).isKeyguardLocked();

        doReturn(PackageManager.PERMISSION_DENIED).when(mPackageManager).checkPermission(
                RECORD_SENSITIVE_CONTENT, projection.packageName);

        projection.start(mIMediaProjectionCallback);
        projection.notifyVirtualDisplayCreated(10);

        // The projection was started because it was allowed to capture the keyguard.
        assertThat(mService.getActiveProjectionInfo()).isNotNull();
    }

    @Test
    public void testCreateProjection_attemptReuse_noPriorProjectionGrant()
            throws NameNotFoundException {