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


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


@@ -198,17 +204,39 @@ public final class PermissionPolicyService extends SystemService {
     */
     */
    private static class PermissionToOpSynchroniser {
    private static class PermissionToOpSynchroniser {
        private final @NonNull Context mContext;
        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<>();
         * All ops that need to be restricted
        private final @NonNull SparseIntArray mAllowedUidOps = new SparseIntArray();
         *
        private final @NonNull SparseIntArray mDefaultUidOps = new SparseIntArray();
         * @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) {
        PermissionToOpSynchroniser(@NonNull Context context) {
            mContext = 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
            // 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
            // requesting the permission is whitelisted and to deny if the app requesting
            // the permission is not whitelisted. However, there is another case where an
            // 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 =
            final SparseArray<List<String>> unrequestedRestrictedPermissionsForUid =
                    new SparseArray<>();
                    new SparseArray<>();


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


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


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


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


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


            // Give root access
            // 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.
            // 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++) {
            for (int i = 0; i < uidCount; i++) {
                final int uid = mUids.keyAt(i);
                final int uid = unprocessedUids.keyAt(i);
                unrequestedRestrictedPermissionsForUid.put(uid,
                unrequestedRestrictedPermissionsForUid.put(uid,
                        new ArrayList<>(sAllRestrictedPermissions));
                        new ArrayList<>(sAllRestrictedPermissions));
            }
            }
@@ -289,8 +313,7 @@ public final class PermissionPolicyService extends SystemService {
                    for (int j = 0; j < permissionCount; j++) {
                    for (int j = 0; j < permissionCount; j++) {
                        final String permission = unrequestedRestrictedPermissions.get(j);
                        final String permission = unrequestedRestrictedPermissions.get(j);
                        for (String packageName : packageNames) {
                        for (String packageName : packageNames) {
                            setUidModeAllowed(appOpsManager,
                            setUidModeAllowed(AppOpsManager.permissionToOpCode(permission), uid,
                                    AppOpsManager.permissionToOpCode(permission), uid,
                                    packageName);
                                    packageName);
                        }
                        }
                    }
                    }
@@ -298,14 +321,61 @@ public final class PermissionPolicyService extends SystemService {
            }
            }
        }
        }


        private void addPackage(@NonNull Context context,
        /**
                @NonNull PackageParser.Package pkg, @UserIdInt int userId) {
         * Synchronize all previously {@link #addPackage added} packages.
            final PackageManager packageManager = context.getPackageManager();
         */
        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 int uid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.applicationInfo.uid));
            final UserHandle userHandle = UserHandle.of(userId);
            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();
            final int permissionCount = pkg.requestedPermissions.size();
            for (int i = 0; i < permissionCount; i++) {
            for (int i = 0; i < permissionCount; i++) {
@@ -318,44 +388,47 @@ public final class PermissionPolicyService extends SystemService {


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


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

                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,
        private class OpToRestrict {
                int opCode, int uid, @NonNull String packageName) {
            final int uid;
            final int currentMode = appOpsManager.unsafeCheckOpRaw(AppOpsManager
            final int code;
                    .opToPublicName(opCode), uid, packageName);

            if (currentMode == AppOpsManager.MODE_DEFAULT) {
            OpToRestrict(int uid, int code) {
                appOpsManager.setUidMode(opCode, uid, AppOpsManager.MODE_ALLOWED);
                this.uid = uid;
                this.code = code;
            }
            }
        }
        }


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

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