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

Commit e16039df authored by yutingfang's avatar yutingfang
Browse files

Always check device policy for device aware permission check

and AppOp mode checking

Before, when the deviceId in the Context is explicitly set, we don't
account for virtual device's camera/audio policy when doing permission
check. So if the virtual device doesn't have custom policy, the
permission check will not fall back to the default device.

With this fix, we always fall back to the default device if virtual
device uses the default policy, no matter if the device Id is explicitly
set in the Context or not.

Bug: 396422648
Test: atest android.permission.cts.DevicePermissionsTest
Flag: EXEMPT bug fix
Change-Id: I5bc731c275429bd6ed8d7117d2226bd41f4b7c34
parent aa6b9b69
Loading
Loading
Loading
Loading
+4 −41
Original line number Diff line number Diff line
@@ -22,14 +22,12 @@ import static android.os.StrictMode.vmIncorrectContextUseEnabled;
import static android.permission.flags.Flags.shouldRegisterAttributionSource;
import static android.view.WindowManager.LayoutParams.WindowType;

import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.UiContext;
import android.companion.virtual.VirtualDevice;
import android.companion.virtual.VirtualDeviceManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.AttributionSource;
@@ -2367,47 +2365,11 @@ class ContextImpl extends Context {
            Log.v(TAG, "Treating renounced permission " + permission + " as denied");
            return PERMISSION_DENIED;
        }
        int deviceId = resolveDeviceIdForPermissionCheck(permission);
        int deviceId = PermissionManager.resolveDeviceIdForPermissionCheck(this, getDeviceId(),
                permission);
        return PermissionManager.checkPermission(permission, pid, uid, deviceId);
    }

    private int resolveDeviceIdForPermissionCheck(String permission) {
        // When checking a device-aware permission on a remote device, if the permission is CAMERA
        // or RECORD_AUDIO we need to check remote device's corresponding capability. If the remote
        // device doesn't have capability fall back to checking permission on the default device.
        // Note: we only perform permission check redirection when the device id is not explicitly
        // set in the context.
        int deviceId = getDeviceId();
        if (deviceId != Context.DEVICE_ID_DEFAULT
                && !mIsExplicitDeviceId
                && PermissionManager.DEVICE_AWARE_PERMISSIONS.contains(permission)) {
            VirtualDeviceManager virtualDeviceManager =
                    getSystemService(VirtualDeviceManager.class);
            if (virtualDeviceManager == null) {
                Slog.e(
                        TAG,
                        "VDM is not enabled when device id is not default. deviceId = "
                                + deviceId);
            } else {
                VirtualDevice virtualDevice = virtualDeviceManager.getVirtualDevice(deviceId);
                if (virtualDevice != null) {
                    if ((Objects.equals(permission, Manifest.permission.RECORD_AUDIO)
                            && !virtualDevice.hasCustomAudioInputSupport())
                            || (Objects.equals(permission, Manifest.permission.CAMERA)
                            && !virtualDevice.hasCustomCameraSupport())) {
                        deviceId = Context.DEVICE_ID_DEFAULT;
                    }
                } else {
                    Slog.e(
                            TAG,
                            "virtualDevice is not found when device id is not default. deviceId = "
                                    + deviceId);
                }
            }
        }
        return deviceId;
    }

    /** @hide */
    @Override
    public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
@@ -2511,7 +2473,8 @@ class ContextImpl extends Context {
    @Override
    public int getPermissionRequestState(String permission) {
        Objects.requireNonNull(permission, "Permission name can't be null");
        int deviceId = resolveDeviceIdForPermissionCheck(permission);
        int deviceId = PermissionManager.resolveDeviceIdForPermissionCheck(this, getDeviceId(),
                permission);
        PermissionManager permissionManager = getSystemService(PermissionManager.class);
        return permissionManager.getPermissionRequestState(getOpPackageName(), permission,
                deviceId);
+39 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.permission;

import static android.companion.virtual.VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE;
import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
@@ -2039,12 +2040,49 @@ public final class PermissionManager {
                new PackageNamePermissionQuery(permName, pkgName, persistentDeviceId, userId));
    }

    /**
     * When checking a device-aware permission on a remote device, if the permission is CAMERA
     * or RECORD_AUDIO we need to check remote device's corresponding capability. If the remote
     * device doesn't have capability fall back to checking permission on the default device.
     *
     * @hide
     */
    public static int resolveDeviceIdForPermissionCheck(@NonNull Context context, int deviceId,
            @Nullable String permission) {
        if (deviceId == Context.DEVICE_ID_DEFAULT || !DEVICE_AWARE_PERMISSIONS.contains(
                permission)) {
            return Context.DEVICE_ID_DEFAULT;
        }

        VirtualDeviceManager virtualDeviceManager =
                context.getSystemService(VirtualDeviceManager.class);
        if (virtualDeviceManager == null) {
            Slog.e(LOG_TAG, "VDM is not enabled when device id is not default. deviceId = "
                    + deviceId);
        } else {
            VirtualDevice virtualDevice = virtualDeviceManager.getVirtualDevice(deviceId);
            if (virtualDevice != null) {
                if ((Objects.equals(permission, Manifest.permission.RECORD_AUDIO)
                        && !virtualDevice.hasCustomAudioInputSupport())
                        || (Objects.equals(permission, Manifest.permission.CAMERA)
                        && !virtualDevice.hasCustomCameraSupport())) {
                    deviceId = Context.DEVICE_ID_DEFAULT;
                }
            } else {
                Slog.e(LOG_TAG,
                        "virtualDevice is not found when device id is not default. deviceId = "
                                + deviceId);
            }
        }
        return deviceId;
    }

    @Nullable
    private String getPersistentDeviceId(int deviceId) {
        String persistentDeviceId = null;

        if (deviceId == Context.DEVICE_ID_DEFAULT) {
            persistentDeviceId = VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
            persistentDeviceId = PERSISTENT_DEVICE_ID_DEFAULT;
        } else {
            VirtualDeviceManager virtualDeviceManager = mContext.getSystemService(
                    VirtualDeviceManager.class);
+31 −18
Original line number Diff line number Diff line
@@ -2996,7 +2996,7 @@ public class AppOpsService extends IAppOpsService.Stub {
            UidState uidState = getUidStateLocked(uid, false);
            if (uidState != null) {
                int rawUidMode = mAppOpsCheckingService.getUidMode(
                        uidState.uid, getPersistentId(virtualDeviceId), code);
                        uidState.uid, getPersistentDeviceIdForOp(virtualDeviceId, code), code);

                if (rawUidMode != AppOpsManager.opToDefaultMode(code)) {
                    return raw ? rawUidMode :
@@ -3059,7 +3059,7 @@ public class AppOpsService extends IAppOpsService.Stub {

            int switchCode = AppOpsManager.opToSwitch(code);
            int rawUidMode = mAppOpsCheckingService.getUidMode(uid,
                    getPersistentId(virtualDeviceId), switchCode);
                    getPersistentDeviceIdForOp(virtualDeviceId, switchCode), switchCode);

            if (rawUidMode != AppOpsManager.opToDefaultMode(switchCode)) {
                return raw ? rawUidMode : evaluateForegroundMode(uid, switchCode, rawUidMode);
@@ -3386,7 +3386,7 @@ public class AppOpsService extends IAppOpsService.Stub {
            }
            final Op op = getOpLocked(ops, code, uid, true);
            final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag,
                    getPersistentId(virtualDeviceId));
                    getPersistentDeviceIdForOp(virtualDeviceId, code));
            if (attributedOp.isRunning()) {
                Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName + " code "
                        + code + " startTime of in progress event="
@@ -3408,15 +3408,15 @@ public class AppOpsService extends IAppOpsService.Stub {

                // If there is a non-default per UID policy (we set UID op mode only if
                // non-default) it takes over, otherwise use the per package policy.
            } else if (mAppOpsCheckingService.getUidMode(
                            uidState.uid, getPersistentId(virtualDeviceId), switchCode)
            } else if (mAppOpsCheckingService.getUidMode(uidState.uid,
                    getPersistentDeviceIdForOp(virtualDeviceId, switchCode), switchCode)
                    != AppOpsManager.opToDefaultMode(switchCode)) {
                final int uidMode =
                        uidState.evalMode(
                                code,
                                mAppOpsCheckingService.getUidMode(
                                        uidState.uid,
                                        getPersistentId(virtualDeviceId),
                                        getPersistentDeviceIdForOp(virtualDeviceId, switchCode),
                                        switchCode));
                if (uidMode != AppOpsManager.MODE_ALLOWED) {
                    if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
@@ -3468,7 +3468,8 @@ public class AppOpsService extends IAppOpsService.Stub {
                    virtualDeviceId, flags, AppOpsManager.MODE_ALLOWED);

            attributedOp.accessed(proxyUid, proxyPackageName, proxyAttributionTag,
                    getPersistentId(proxyVirtualDeviceId), uidState.getState(), flags, notedCount);
                    getPersistentDeviceIdForOp(proxyVirtualDeviceId, code), uidState.getState(),
                    flags, notedCount);

            if (shouldCollectAsyncNotedOp) {
                collectAsyncNotedOp(uid, packageName, code, attributionTag, flags, message,
@@ -4035,7 +4036,7 @@ public class AppOpsService extends IAppOpsService.Stub {
            }
            final Op op = getOpLocked(ops, code, uid, true);
            final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag,
                    getPersistentId(virtualDeviceId));
                    getPersistentDeviceIdForOp(virtualDeviceId, code));
            final UidState uidState = ops.uidState;
            isRestricted = isOpRestrictedLocked(uid, code, packageName, attributionTag,
                    virtualDeviceId, pvr.bypass, false);
@@ -4049,7 +4050,8 @@ public class AppOpsService extends IAppOpsService.Stub {
                // non-default) it takes over, otherwise use the per package policy.
            } else if ((rawUidMode =
                    mAppOpsCheckingService.getUidMode(
                                    uidState.uid, getPersistentId(virtualDeviceId), switchCode))
                            uidState.uid, getPersistentDeviceIdForOp(virtualDeviceId, switchCode),
                            switchCode))
                    != AppOpsManager.opToDefaultMode(switchCode)) {
                final int uidMode = uidState.evalMode(code, rawUidMode);
                if (!shouldStartForMode(uidMode, startIfModeDefault)) {
@@ -4097,11 +4099,13 @@ public class AppOpsService extends IAppOpsService.Stub {
            try {
                if (isRestricted) {
                    attributedOp.createPaused(clientId, virtualDeviceId, proxyUid, proxyPackageName,
                            proxyAttributionTag, getPersistentId(proxyVirtualDeviceId),
                            proxyAttributionTag,
                            getPersistentDeviceIdForOp(proxyVirtualDeviceId, code),
                            uidState.getState(), flags, attributionFlags, attributionChainId);
                } else {
                    attributedOp.started(clientId, virtualDeviceId, proxyUid, proxyPackageName,
                            proxyAttributionTag, getPersistentId(proxyVirtualDeviceId),
                            proxyAttributionTag,
                            getPersistentDeviceIdForOp(proxyVirtualDeviceId, code),
                            uidState.getState(), flags, attributionFlags, attributionChainId);
                    startType = START_TYPE_STARTED;
                }
@@ -4169,15 +4173,15 @@ public class AppOpsService extends IAppOpsService.Stub {
            final int switchCode = AppOpsManager.opToSwitch(code);
            // If there is a non-default mode per UID policy (we set UID op mode only if
            // non-default) it takes over, otherwise use the per package policy.
            if (mAppOpsCheckingService.getUidMode(
                            uidState.uid, getPersistentId(virtualDeviceId), switchCode)
            if (mAppOpsCheckingService.getUidMode(uidState.uid,
                    getPersistentDeviceIdForOp(virtualDeviceId, switchCode), switchCode)
                    != AppOpsManager.opToDefaultMode(switchCode)) {
                final int uidMode =
                        uidState.evalMode(
                                code,
                                mAppOpsCheckingService.getUidMode(
                                        uidState.uid,
                                        getPersistentId(virtualDeviceId),
                                        getPersistentDeviceIdForOp(virtualDeviceId, switchCode),
                                        switchCode));
                if (!shouldStartForMode(uidMode, startIfModeDefault)) {
                    if (DEBUG) {
@@ -4340,7 +4344,8 @@ public class AppOpsService extends IAppOpsService.Stub {
                return;
            }
            final AttributedOp attributedOp =
                    op.mDeviceAttributedOps.getOrDefault(getPersistentId(virtualDeviceId),
                    op.mDeviceAttributedOps.getOrDefault(
                            getPersistentDeviceIdForOp(virtualDeviceId, code),
                            new ArrayMap<>()).get(attributionTag);
            if (attributedOp == null) {
                Slog.e(TAG, "Attribution not found: uid=" + uid + " pkg=" + packageName + "("
@@ -4631,7 +4636,8 @@ public class AppOpsService extends IAppOpsService.Stub {
            return true;
        }
        if (mVirtualDeviceManagerInternal == null) {
            return true;
            Slog.w(TAG, "VirtualDeviceManagerInternal is null when device Id is non-default");
            return false;
        }
        if (mVirtualDeviceManagerInternal.isValidVirtualDeviceId(virtualDeviceId)) {
            mKnownDeviceIds.put(virtualDeviceId,
@@ -7299,7 +7305,13 @@ public class AppOpsService extends IAppOpsService.Stub {
        return packageNames;
    }

    @NonNull private String getPersistentId(int virtualDeviceId) {
    // For ops associated with device aware permissions, if virtual device id is non-default, we
    // will return string version of that id provided the virtual device has corresponding camera or
    // audio policy.
    @NonNull private String getPersistentDeviceIdForOp(int virtualDeviceId, int op) {
        virtualDeviceId = PermissionManager.resolveDeviceIdForPermissionCheck(mContext,
                virtualDeviceId, AppOpsManager.opToPermission(op));

        if (virtualDeviceId == Context.DEVICE_ID_DEFAULT) {
            return PERSISTENT_DEVICE_ID_DEFAULT;
        }
@@ -7308,6 +7320,7 @@ public class AppOpsService extends IAppOpsService.Stub {
        }
        String persistentId =
                mVirtualDeviceManagerInternal.getPersistentIdForDevice(virtualDeviceId);

        if (persistentId == null) {
            persistentId = mKnownDeviceIds.get(virtualDeviceId);
        }