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

Commit f5d24431 authored by Hai Zhang's avatar Hai Zhang
Browse files

Respect role granted app op permissions when resetting all op modes.

We already respect role (or DPGP) granted runtime permissions when
resetting permission states, and role won't proactively come and grant
the permission again, so we should do the same for app ops to avoid
breaking apps.

Bug: 272371913
Test: manual
Change-Id: Id561ca9c02a7f4c2a140434abf5503420490e443
parent d319abbb
Loading
Loading
Loading
Loading
+42 −5
Original line number Diff line number Diff line
@@ -2217,8 +2217,7 @@ public class AppOpsManager {
    /** Whether noting for an appop should be collected */
    private static final @ShouldCollectNoteOp byte[] sAppOpsToNote = new byte[_NUM_OP];

    private static final int[] RUNTIME_AND_APPOP_PERMISSIONS_OPS = {
            // RUNTIME PERMISSIONS
    private static final int[] RUNTIME_PERMISSION_OPS = {
            // Contacts
            OP_READ_CONTACTS,
            OP_WRITE_CONTACTS,
@@ -2275,8 +2274,13 @@ public class AppOpsManager {
            OP_NEARBY_WIFI_DEVICES,
            // Notifications
            OP_POST_NOTIFICATION,
    };

            // APPOP PERMISSIONS
    /**
     * Ops for app op permissions that are setting the per-package mode for certain reasons. Most
     * app op permissions should set the per-UID mode instead.
     */
    private static final int[] APP_OP_PERMISSION_PACKAGE_OPS = {
            OP_ACCESS_NOTIFICATIONS,
            OP_SYSTEM_ALERT_WINDOW,
            OP_WRITE_SETTINGS,
@@ -2285,9 +2289,16 @@ public class AppOpsManager {
            OP_SMS_FINANCIAL_TRANSACTIONS,
            OP_MANAGE_IPSEC_TUNNELS,
            OP_INSTANT_APP_START_FOREGROUND,
            OP_LOADER_USAGE_STATS
    };

    /**
     * Ops for app op permissions that are setting the per-UID mode for certain reasons. This should
     * be preferred over the per-package mode for new app op permissions.
     */
    private static final int[] APP_OP_PERMISSION_UID_OPS = {
            OP_MANAGE_EXTERNAL_STORAGE,
            OP_INTERACT_ACROSS_PROFILES,
            OP_LOADER_USAGE_STATS,
            OP_MANAGE_ONGOING_CALLS,
            OP_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER,
            OP_SCHEDULE_EXACT_ALARM,
@@ -2777,7 +2788,17 @@ public class AppOpsManager {
                sOpStrToOp.put(sAppOpInfos[i].name, i);
            }
        }
        for (int op : RUNTIME_AND_APPOP_PERMISSIONS_OPS) {
        for (int op : RUNTIME_PERMISSION_OPS) {
            if (sAppOpInfos[op].permission != null) {
                sPermToOp.put(sAppOpInfos[op].permission, op);
            }
        }
        for (int op : APP_OP_PERMISSION_PACKAGE_OPS) {
            if (sAppOpInfos[op].permission != null) {
                sPermToOp.put(sAppOpInfos[op].permission, op);
            }
        }
        for (int op : APP_OP_PERMISSION_UID_OPS) {
            if (sAppOpInfos[op].permission != null) {
                sPermToOp.put(sAppOpInfos[op].permission, op);
            }
@@ -2946,6 +2967,22 @@ public class AppOpsManager {
        return !sAppOpInfos[op].disableReset;
    }

    /**
     * Retrieve whether the op is a per-package op for an app op permission.
     * @hide
     */
    public static boolean opIsPackageAppOpPermission(int op) {
        return ArrayUtils.contains(APP_OP_PERMISSION_PACKAGE_OPS, op);
    }

    /**
     * Retrieve whether the op is a per-package op for an app op permission.
     * @hide
     */
    public static boolean opIsUidAppOpPermission(int op) {
        return ArrayUtils.contains(APP_OP_PERMISSION_UID_OPS, op);
    }

    /**
     * Returns a listenerId suitable for use with {@link #noteOp(int, int, String, String, String)}.
     *
+46 −4
Original line number Diff line number Diff line
@@ -2096,7 +2096,9 @@ public class AppOpsService extends IAppOpsService.Stub {
                        final int code = opModes.keyAt(j);
                        if (AppOpsManager.opAllowsReset(code)) {
                            int previousMode = opModes.valueAt(j);
                            uidState.setUidMode(code, AppOpsManager.opToDefaultMode(code));
                            int newMode = isUidOpGrantedByRole(uidState.uid, code) ? MODE_ALLOWED :
                                    AppOpsManager.opToDefaultMode(code);
                            uidState.setUidMode(code, newMode);
                            for (String packageName : getPackagesForUid(uidState.uid)) {
                                callbacks = addCallbacks(callbacks, code, uidState.uid, packageName,
                                        previousMode,
@@ -2139,10 +2141,15 @@ public class AppOpsService extends IAppOpsService.Stub {
                            deferResetOpToDpm(curOp.op, reqPackageName, reqUserId);
                            continue;
                        }
                        if (AppOpsManager.opAllowsReset(curOp.op)
                                && curOp.getMode() != AppOpsManager.opToDefaultMode(curOp.op)) {
                        if (AppOpsManager.opAllowsReset(curOp.op)) {
                            int previousMode = curOp.getMode();
                            curOp.setMode(AppOpsManager.opToDefaultMode(curOp.op));
                            int newMode = isPackageOpGrantedByRole(packageName, uidState.uid,
                                    curOp.op) ? MODE_ALLOWED : AppOpsManager.opToDefaultMode(
                                    curOp.op);
                            if (previousMode == newMode) {
                                continue;
                            }
                            curOp.setMode(newMode);
                            changed = true;
                            uidChanged = true;
                            final int uid = curOp.uidState.uid;
@@ -2198,6 +2205,41 @@ public class AppOpsService extends IAppOpsService.Stub {
        }
    }

    private boolean isUidOpGrantedByRole(int uid, int code) {
        if (!AppOpsManager.opIsUidAppOpPermission(code)) {
            return false;
        }
        PackageManager packageManager = mContext.getPackageManager();
        long token = Binder.clearCallingIdentity();
        try {
            // Permissions are managed by UIDs, but unfortunately a package name is required in API.
            String packageName = ArrayUtils.firstOrNull(packageManager.getPackagesForUid(uid));
            if (packageName == null) {
                return false;
            }
            int permissionFlags = packageManager.getPermissionFlags(AppOpsManager.opToPermission(
                    code), packageName, UserHandle.getUserHandleForUid(uid));
            return (permissionFlags & PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE) != 0;
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    private boolean isPackageOpGrantedByRole(@NonNull String packageName, int uid, int code) {
        if (!AppOpsManager.opIsPackageAppOpPermission(code)) {
            return false;
        }
        PackageManager packageManager = mContext.getPackageManager();
        long token = Binder.clearCallingIdentity();
        try {
            int permissionFlags = packageManager.getPermissionFlags(AppOpsManager.opToPermission(
                    code), packageName, UserHandle.getUserHandleForUid(uid));
            return (permissionFlags & PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE) != 0;
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    private boolean shouldDeferResetOpToDpm(int op) {
        // TODO(b/174582385): avoid special-casing app-op resets by migrating app-op permission
        //  pre-grants to a role-based mechanism or another general-purpose mechanism.