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

Commit facac371 authored by Marvin Ramin's avatar Marvin Ramin
Browse files

Add UID check when starting MediaProjection sessions

Prevent MediaProjection tokens from being exchanged between
applications.

Also fix the unit tests that effectively relied on this behavior.

Bug: 419269649
Test: atest MediaProjectionManagerServiceTest
Flag: com.android.media.projection.flags.start_uid_check
Change-Id: I7e2c74220171484338aa339387e484b2794c9c65
parent d44d0d12
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -64,3 +64,13 @@ flag {
      purpose: PURPOSE_BUGFIX
    }
}

flag {
    namespace: "media_projection"
    name: "start_uid_check"
    description: "Improves permission model when starting MediaProjection session"
    bug: "419269649"
    metadata {
      purpose: PURPOSE_BUGFIX
    }
}
+33 −0
Original line number Diff line number Diff line
@@ -734,6 +734,35 @@ public final class MediaProjectionManagerService extends SystemService
        }
    }

    /**
     * Verifies whether the calling package name matches the calling app uid.
     *
     * @param context the context
     * @param callingPackage the calling application package name
     * @return {@code true} if the package name matches {@link Binder#getCallingUid()}, or
     *   {@code false} otherwise
     */
    private static boolean validateCallingPackageName(Context context, String callingPackage) {
        final int callingUid = Binder.getCallingUid();
        final long token = Binder.clearCallingIdentity();
        try {
            int packageUid = context.getPackageManager()
                    .getPackageUidAsUser(callingPackage, UserHandle.getUserId(callingUid));
            if (packageUid != callingUid) {
                Slog.e(TAG, "validatePackageName: App with package name " + callingPackage
                        + " is UID " + packageUid + " but caller is " + callingUid);
                return false;
            }
        } catch (PackageManager.NameNotFoundException e) {
            Slog.e(TAG, "validatePackageName: App with package name " + callingPackage
                    + " does not exist");
            return false;
        } finally {
            Binder.restoreCallingIdentity(token);
        }
        return true;
    }

    private void dump(final PrintWriter pw) {
        pw.println("MEDIA PROJECTION MANAGER (dumpsys media_projection)");
        synchronized (mLock) {
@@ -1189,6 +1218,10 @@ public final class MediaProjectionManagerService extends SystemService
                    mCountStarts++;
                    return;
                }
                if (Flags.startUidCheck() && validateCallingPackageName(mContext, packageName)) {
                    throw new SecurityException(
                            "This MediaProjection session was not granted to this application.");
                }

                if (REQUIRE_FG_SERVICE_FOR_PROJECTION
                        && requiresForegroundService() && !hasFGS) {
+23 −1
Original line number Diff line number Diff line
@@ -74,6 +74,7 @@ import android.media.projection.StopReason;
import android.os.Binder;
import android.os.IBinder;
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.test.TestLooper;
@@ -123,7 +124,7 @@ import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
@SuppressLint({"UseCheckPermission", "VisibleForTests", "MissingPermission"})
public class MediaProjectionManagerServiceTest {
    private static final int UID = 10;
    private static final int UID = Process.myUid();
    private static final String PACKAGE_NAME = "test.package";
    private final ApplicationInfo mAppInfo = new ApplicationInfo();
    private final PackageInfo mPackageInfo = new PackageInfo();
@@ -1347,6 +1348,27 @@ public class MediaProjectionManagerServiceTest {
                eq(projection.uid), anyInt());
    }

    @Test
    @EnableFlags(Flags.FLAG_START_UID_CHECK)
    public void createProjection_cannotBeStartedFromDifferentUid() throws Exception {
        doReturn(mAppInfo).when(mPackageManager).getApplicationInfoAsUser(anyString(),
                any(ApplicationInfoFlags.class), any(UserHandle.class));
        doReturn(mPackageInfo).when(mPackageManager).getPackageInfoAsUser(anyString(), anyInt(),
                anyInt());
        int uid = 10;
        MediaProjectionManagerService.MediaProjection projection =
                mService.createProjectionInternal(
                        uid,
                        PACKAGE_NAME,
                        TYPE_SCREEN_CAPTURE,
                        /* isPermanentGrant= */ false,
                        UserHandle.CURRENT,
                        DEFAULT_DISPLAY);

        // Start MediaProjection from a different UID
        assertThrows(SecurityException.class, () -> projection.start(mIMediaProjectionCallback));
    }

    private void verifySetSessionWithContent(@RecordContent int content) {
        verify(mWindowManagerInternal, atLeastOnce()).setContentRecordingSession(
                mSessionCaptor.capture());