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

Commit bd4943b4 authored by Philip P. Moltmann's avatar Philip P. Moltmann
Browse files

Make perm policy sve ready for additional usages

We want to use the permission policy services for other things than
restriction of permissions. Hence factor out the restriction code from
the framework.

Also add some comments

Test: atest CtsAppOpsTestCases:android.app.appops.cts.AppOpsTest
            CtsPermission2TestCases:android.permission2.cts.RestrictedPermissionsTest
Bug: 124731615
Change-Id: Ic32f2097af805c3f06dd9cf625b3160f62a576b2
parent 71d34507
Loading
Loading
Loading
Loading
+137 −64
Original line number Diff line number Diff line
@@ -158,6 +158,9 @@ public final class PermissionPolicyService extends SystemService {
                });
    }

    /**
     * Synchronize a single package.
     */
    private static void synchronizePackagePermissionsAndAppOpsForUser(@NonNull Context context,
            @NonNull String packageName, @UserIdInt int userId) {
        final PackageManagerInternal packageManagerInternal = LocalServices.getService(
@@ -167,7 +170,7 @@ public final class PermissionPolicyService extends SystemService {
            return;
        }
        final PermissionToOpSynchroniser synchroniser = new PermissionToOpSynchroniser(context);
        synchroniser.addPackage(context, pkg, userId);
        synchroniser.addPackage(pkg, userId);
        final String[] sharedPkgNames = packageManagerInternal.getPackagesForSharedUserId(
                pkg.mSharedUserId, userId);
        if (sharedPkgNames != null) {
@@ -175,20 +178,23 @@ public final class PermissionPolicyService extends SystemService {
                final PackageParser.Package sharedPkg = packageManagerInternal
                        .getPackage(sharedPkgName);
                if (sharedPkg != null) {
                    synchroniser.addPackage(context, sharedPkg, userId);
                    synchroniser.addPackage(sharedPkg, userId);
                }
            }
        }
        synchroniser.syncPackages();
    }

    /**
     * Synchronize all packages
     */
    private static void synchronizePermissionsAndAppOpsForUser(@NonNull Context context,
            @UserIdInt int userId) {
        final PackageManagerInternal packageManagerInternal = LocalServices.getService(
                PackageManagerInternal.class);
        final PermissionToOpSynchroniser synchronizer = new PermissionToOpSynchroniser(context);
        packageManagerInternal.forEachPackage((pkg) ->
                synchronizer.addPackage(context, pkg, userId));
                synchronizer.addPackage(pkg, userId));
        synchronizer.syncPackages();
    }

@@ -198,17 +204,39 @@ public final class PermissionPolicyService extends SystemService {
     */
    private static class PermissionToOpSynchroniser {
        private final @NonNull Context mContext;
        private final @NonNull PackageManager mPackageManager;
        private final @NonNull AppOpsManager mAppOpsManager;

        /** All uid that need to be synchronized */
        private final @NonNull SparseIntArray mAllUids = new SparseIntArray();

        private final @NonNull SparseIntArray mUids = new SparseIntArray();
        private final @NonNull SparseArray<String> mPackageNames = new SparseArray<>();
        private final @NonNull SparseIntArray mAllowedUidOps = new SparseIntArray();
        private final @NonNull SparseIntArray mDefaultUidOps = new SparseIntArray();
        /**
         * All ops that need to be restricted
         *
         * @see #syncRestrictedOps
         */
        private final @NonNull ArrayList<OpToRestrict> mOpsToRestrict = new ArrayList<>();
        /**
         * All ops that need to be unrestricted
         *
         * @see #syncRestrictedOps
         */
        private final @NonNull ArrayList<OpToUnrestrict> mOpsToUnrestrict = new ArrayList<>();

        PermissionToOpSynchroniser(@NonNull Context context) {
            mContext = context;
            mPackageManager = context.getPackageManager();
            mAppOpsManager = context.getSystemService(AppOpsManager.class);
        }

        void syncPackages() {
        /**
         * Set app ops that belong to restricted permissions.
         *
         * <p>This processes ops previously added by {@link #addOpIfRestricted}
         */
        private void syncRestrictedOps() {
            final SparseIntArray unprocessedUids = mAllUids.clone();

            // TRICKY: we set the app op for a restricted permission to allow if the app
            // requesting the permission is whitelisted and to deny if the app requesting
            // the permission is not whitelisted. However, there is another case where an
@@ -222,52 +250,48 @@ public final class PermissionPolicyService extends SystemService {
            final SparseArray<List<String>> unrequestedRestrictedPermissionsForUid =
                    new SparseArray<>();

            final AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class);
            final int allowedCount = mAllowedUidOps.size();
            for (int i = 0; i < allowedCount; i++) {
                final int opCode = mAllowedUidOps.keyAt(i);
                final int uid = mAllowedUidOps.valueAt(i);
                final String packageName = mPackageNames.valueAt(i);
                setUidModeAllowed(appOpsManager, opCode, uid, packageName);
            final int unrestrictCount = mOpsToUnrestrict.size();
            for (int i = 0; i < unrestrictCount; i++) {
                final OpToUnrestrict op = mOpsToUnrestrict.get(i);
                setUidModeAllowed(op.code, op.uid, op.packageName);

                // Keep track this permission was requested by the UID.
                List<String> unrequestedRestrictedPermissions =
                        unrequestedRestrictedPermissionsForUid.get(uid);
                        unrequestedRestrictedPermissionsForUid.get(op.uid);
                if (unrequestedRestrictedPermissions == null) {
                    unrequestedRestrictedPermissions = new ArrayList<>(sAllRestrictedPermissions);
                    unrequestedRestrictedPermissionsForUid.put(uid,
                    unrequestedRestrictedPermissionsForUid.put(op.uid,
                            unrequestedRestrictedPermissions);
                }
                unrequestedRestrictedPermissions.remove(AppOpsManager.opToPermission(opCode));
                unrequestedRestrictedPermissions.remove(AppOpsManager.opToPermission(op.code));

                mUids.delete(uid);
                unprocessedUids.delete(op.uid);
            }
            final int defaultCount = mDefaultUidOps.size();
            for (int i = 0; i < defaultCount; i++) {
                final int opCode = mDefaultUidOps.keyAt(i);
                final int uid = mDefaultUidOps.valueAt(i);
                setUidModeDefault(appOpsManager, opCode, uid);
            final int restrictCount = mOpsToRestrict.size();
            for (int i = 0; i < restrictCount; i++) {
                final OpToRestrict op = mOpsToRestrict.get(i);
                setUidModeDefault(op.code, op.uid);

                // Keep track this permission was requested by the UID.
                List<String> unrequestedRestrictedPermissions =
                        unrequestedRestrictedPermissionsForUid.get(uid);
                        unrequestedRestrictedPermissionsForUid.get(op.uid);
                if (unrequestedRestrictedPermissions == null) {
                    unrequestedRestrictedPermissions = new ArrayList<>(sAllRestrictedPermissions);
                    unrequestedRestrictedPermissionsForUid.put(uid,
                    unrequestedRestrictedPermissionsForUid.put(op.uid,
                            unrequestedRestrictedPermissions);
                }
                unrequestedRestrictedPermissions.remove(AppOpsManager.opToPermission(opCode));
                unrequestedRestrictedPermissions.remove(AppOpsManager.opToPermission(op.code));

                mUids.delete(uid);
                unprocessedUids.delete(op.uid);
            }

            // Give root access
            mUids.put(Process.ROOT_UID, Process.ROOT_UID);
            unprocessedUids.put(Process.ROOT_UID, Process.ROOT_UID);

            // Add records for UIDs that don't use any restricted permissions.
            final int uidCount = mUids.size();
            final int uidCount = unprocessedUids.size();
            for (int i = 0; i < uidCount; i++) {
                final int uid = mUids.keyAt(i);
                final int uid = unprocessedUids.keyAt(i);
                unrequestedRestrictedPermissionsForUid.put(uid,
                        new ArrayList<>(sAllRestrictedPermissions));
            }
@@ -289,8 +313,7 @@ public final class PermissionPolicyService extends SystemService {
                    for (int j = 0; j < permissionCount; j++) {
                        final String permission = unrequestedRestrictedPermissions.get(j);
                        for (String packageName : packageNames) {
                            setUidModeAllowed(appOpsManager,
                                    AppOpsManager.permissionToOpCode(permission), uid,
                            setUidModeAllowed(AppOpsManager.permissionToOpCode(permission), uid,
                                    packageName);
                        }
                    }
@@ -298,14 +321,61 @@ public final class PermissionPolicyService extends SystemService {
            }
        }

        private void addPackage(@NonNull Context context,
                @NonNull PackageParser.Package pkg, @UserIdInt int userId) {
            final PackageManager packageManager = context.getPackageManager();
        /**
         * Synchronize all previously {@link #addPackage added} packages.
         */
        void syncPackages() {
            syncRestrictedOps();
        }

        /**
         * Add op that belong to a restricted permission for later processing in
         * {@link #syncRestrictedOps}.
         *
         * <p>Note: Called with the package lock held. Do <u>not</u> call into app-op manager.
         *
         * @param permissionInfo The permission that is currently looked at
         * @param pkg The package looked at
         * @param userId The user the package belongs to
         */
        private void addOpIfRestricted(@NonNull PermissionInfo permissionInfo,
                @NonNull PackageParser.Package pkg, @UserIdInt int userId) {
            final String permission = permissionInfo.name;
            final int opCode = AppOpsManager.permissionToOpCode(permission);
            final int uid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.applicationInfo.uid));
            final UserHandle userHandle = UserHandle.of(userId);

            mUids.put(uid, uid);
            if (!permissionInfo.isRestricted()) {
                return;
            }

            final boolean applyRestriction = PackageManager.RESTRICTED_PERMISSIONS_ENABLED
                    && (mPackageManager.getPermissionFlags(permission, pkg.packageName,
                    userHandle) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;

            if (permissionInfo.isHardRestricted()) {
                if (applyRestriction) {
                    mOpsToRestrict.add(new OpToRestrict(uid, opCode));
                } else {
                    mOpsToUnrestrict.add(new OpToUnrestrict(uid, pkg.packageName, opCode));
                }
            } else if (permissionInfo.isSoftRestricted()) {
                //TODO: Implement soft restrictions like storage here.
            }
        }

        /**
         * Add a package for {@link #syncPackages() processing} later.
         *
         * <p>Note: Called with the package lock held. Do <u>not</u> call into app-op manager.
         *
         * @param pkg The package to add for later processing
         * @param userId The user the package belongs to
         */
        void addPackage(@NonNull PackageParser.Package pkg, @UserIdInt int userId) {
            final int uid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.applicationInfo.uid));

            mAllUids.put(uid, uid);

            final int permissionCount = pkg.requestedPermissions.size();
            for (int i = 0; i < permissionCount; i++) {
@@ -318,44 +388,47 @@ public final class PermissionPolicyService extends SystemService {

                final PermissionInfo permissionInfo;
                try {
                    permissionInfo = packageManager.getPermissionInfo(permission, 0);
                    permissionInfo = mPackageManager.getPermissionInfo(permission, 0);
                } catch (PackageManager.NameNotFoundException e) {
                    continue;
                }

                if (!permissionInfo.isRestricted()) {
                    continue;
                addOpIfRestricted(permissionInfo, pkg, userId);
            }

                final boolean applyRestriction = PackageManager.RESTRICTED_PERMISSIONS_ENABLED
                        && (packageManager.getPermissionFlags(permission, pkg.packageName,
                                userHandle) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;

                if (permissionInfo.isHardRestricted()) {
                    if (applyRestriction) {
                        mDefaultUidOps.put(opCode, uid);
                    } else {
                        mPackageNames.put(opCode, pkg.packageName);
                        mAllowedUidOps.put(opCode, uid);
        }
                } else if (permissionInfo.isSoftRestricted()) {
                    //TODO: Implement soft restrictions like storage here.

        private void setUidModeAllowed(int opCode, int uid, @NonNull String packageName) {
            final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager
                    .opToPublicName(opCode), uid, packageName);
            if (currentMode == AppOpsManager.MODE_DEFAULT) {
                mAppOpsManager.setUidMode(opCode, uid, AppOpsManager.MODE_ALLOWED);
            }
        }

        private void setUidModeDefault(int opCode, int uid) {
            mAppOpsManager.setUidMode(opCode, uid, AppOpsManager.MODE_DEFAULT);
        }

        private static void setUidModeAllowed(@NonNull AppOpsManager appOpsManager,
                int opCode, int uid, @NonNull String packageName) {
            final int currentMode = appOpsManager.unsafeCheckOpRaw(AppOpsManager
                    .opToPublicName(opCode), uid, packageName);
            if (currentMode == AppOpsManager.MODE_DEFAULT) {
                appOpsManager.setUidMode(opCode, uid, AppOpsManager.MODE_ALLOWED);
        private class OpToRestrict {
            final int uid;
            final int code;

            OpToRestrict(int uid, int code) {
                this.uid = uid;
                this.code = code;
            }
        }

        private static void setUidModeDefault(@NonNull AppOpsManager appOpsManager,
                int opCode, int uid) {
            appOpsManager.setUidMode(opCode, uid, AppOpsManager.MODE_DEFAULT);
        private class OpToUnrestrict {
            final int uid;
            final @NonNull String packageName;
            final int code;

            OpToUnrestrict(int uid, @NonNull String packageName, int code) {
                this.uid = uid;
                this.packageName = packageName;
                this.code = code;
            }
        }
    }
}