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

Commit 95834e5c authored by Svet Ganov's avatar Svet Ganov
Browse files

estricted permission mechanism - PermissionController

This change adds a mechanism for restricting permissions (only runtime
for now), so that an app cannot hold the permission if it is not white
listed. The whitelisting can happen at install or at any later point.

There are three whitelists: system: OS managed with default grants
and role holders being on it; upgrade: only OS puts on this list
apps when upgrading from a pre to post restriction permission database
version and OS and installer on record can remove; installer: only
the installer on record can add and remove (and the system of course).

Added a permission policy service that sits on top of permissions
and app ops and is responsible to sync between permissions and app
ops when there is an interdependecy in any direction.

Added versioning to the runtime permissions database to allow operations
that need to be done once on upgrade such as adding all permissions held
by apps pre upgrade to the upgrade whitelist if the new permisison version
inctroduces a new restricted permission. The upgrade logic is in the
permission controller and we will eventually put the default grants there.

NOTE: This change is reacting to a VP feedback for how we would handle
SMS/CallLog restriction as we pivoted from role based approach to roles
for things the user would understand plus whitelist for everything else.
This would also help us roll out softly the storage permisison as there
is too much churm coming from developer feedback.

Test: atest CtsAppSecurityHostTestCases:android.appsecurity.cts.PermissionsHostTest
Test: atest CtsPermissionTestCases
Test: atest CtsPermission2TestCases
Test: atest RoleManagerTestCases

bug:124769181

Change-Id: Ib0606b014fee6eec51d0456512877433caf22875
parent dd8da4b2
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
    <uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS" />
    <uses-permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS" />
    <uses-permission android:name="android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY" />
    <uses-permission android:name="android.permission.WHITELIST_RESTRICTED_PERMISSIONS" />
    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
    <uses-permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS" />
    <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
+8 −13
Original line number Diff line number Diff line
@@ -36,7 +36,6 @@ import android.content.pm.PermissionInfo;
import android.os.Build;
import android.os.UserHandle;
import android.permission.PermissionManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
@@ -54,6 +53,7 @@ import com.android.permissioncontroller.R;
import java.text.Collator;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * All permissions of a permission group that are requested by an app.
@@ -210,6 +210,10 @@ public final class AppPermissionGroup implements Comparable<AppPermissionGroup>
                getBackgroundRequestDetail(groupInfo), groupInfo.packageName, groupInfo.icon,
                userHandle, delayChanges);

        final Set<String> whitelistedRestrictedPermissions = context.getPackageManager()
                .getWhitelistedRestrictedPermissions(packageInfo.packageName,
                        Utils.FLAGS_PERMISSION_WHITELIST_ALL);

        // Parse and create permissions reqested by the app
        ArrayMap<String, Permission> allPermissions = new ArrayMap<>();
        final int permissionCount = packageInfo.requestedPermissions == null ? 0
@@ -318,22 +322,13 @@ public final class AppPermissionGroup implements Comparable<AppPermissionGroup>

                group.getBackgroundPermissions().addPermission(permission);
            } else {
                boolean smsAccessRestrictionEnabled = Settings.Global.getInt(
                        group.mContext.getContentResolver(),
                        Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED, 0) == 1;
                if (!smsAccessRestrictionEnabled) {
                    group.addPermission(permission);
                } else {
                    String appOp = permission.getAppOp();
                    boolean appOpDefault = appOp != null && group.mAppOps.unsafeCheckOpNoThrow(
                            appOp, packageInfo.applicationInfo.uid, packageName)
                            == AppOpsManager.MODE_DEFAULT;
                    if (!appOpDefault) {
                if (!PackageManager.RESTRICTED_PERMISSIONS_ENABLED
                        || (!permission.isHardRestricted()
                            || whitelistedRestrictedPermissions.contains(permission.getName()))) {
                    group.addPermission(permission);
                }
            }
        }
        }

        if (group.getPermissions().isEmpty()) {
            return null;
+5 −0
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ public final class Permission {
    private boolean mIsRuntimeOnly;
    private Permission mBackgroundPermission;
    private ArrayList<Permission> mForegroundPermissions;
    private boolean mWhitelisted;

    public Permission(String name, @NonNull PermissionInfo permissionInfo, boolean granted,
            String appOp, boolean appOpAllowed, int flags) {
@@ -94,6 +95,10 @@ public final class Permission {
        return mFlags;
    }

    boolean isHardRestricted() {
        return (mPermissionInfo.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0;
    }

    /**
     * Does this permission affect app ops.
     *
+6 −0
Original line number Diff line number Diff line
@@ -548,4 +548,10 @@ public final class PermissionControllerServiceImpl extends PermissionControllerS

        return true;
    }

    @Override
    public void onGrantOrUpgradeDefaultRuntimePermissions() {
        // TODO: Default permission grants should go here
        RuntimePermissionsUpgradeController.upgradeIfNeeded(this);
    }
}
+107 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.packageinstaller.permission.service;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.permission.PermissionManager;
import android.util.Log;

import androidx.annotation.NonNull;

import com.android.packageinstaller.permission.utils.Utils;

import java.util.List;

/**
 * This class handles upgrading the runtime permissions database
 */
class RuntimePermissionsUpgradeController {

    // The latest version of the runtime permissions database
    private static final int LATEST_VERSION = 1;

    private RuntimePermissionsUpgradeController() {
        /* do nothing - hide constructor */
    }

    static void upgradeIfNeeded(@NonNull Context context) {
        final PermissionManager permissionManager = context.getSystemService(
                PermissionManager.class);
        final int currentVersion = permissionManager.getRuntimePermissionsVersion();

        final int upgradedVersion = onUpgradeLocked(context, currentVersion);

        if (upgradedVersion != LATEST_VERSION) {
            Log.wtf("PermissionControllerService", "warning: upgrading permission database"
                            + " to version " + LATEST_VERSION + " left it at " + currentVersion
                            + " instead; this is probably a bug. Did you update LATEST_VERSION?",
                    new Throwable());
            throw new RuntimeException("db upgrade error");
        }

        if (currentVersion != upgradedVersion) {
            permissionManager.setRuntimePermissionsVersion(LATEST_VERSION);
        }
    }

    /**
     * You must perform all necessary mutations to bring the runtime permissions
     * database from the old to the new version. When you add a new upgrade step
     * you *must* update LATEST_VERSION.
     *
     * @param context Context to access APIs.
     * @param currentVersion The current db version.
     */
    private static int onUpgradeLocked(@NonNull Context context, int currentVersion) {
        // Grandfather SMS and CallLog permissions.
        if (currentVersion <= 0) {
            final List<String> smsPermissions = Utils.getPlatformPermissionNamesOfGroup(
                    android.Manifest.permission_group.SMS);
            final List<String> callLogPermissions = Utils.getPlatformPermissionNamesOfGroup(
                    Manifest.permission_group.CALL_LOG);

            final List<PackageInfo> apps = context.getPackageManager()
                    .getInstalledPackages(PackageManager.MATCH_ALL
                            | PackageManager.GET_PERMISSIONS);

            final int appCount = apps.size();
            for (int i = 0; i < appCount; i++) {
                final PackageInfo app = apps.get(i);
                if (app.requestedPermissions == null) {
                    continue;
                }

                for (String requestedPermission : app.requestedPermissions) {
                    if (smsPermissions.contains(requestedPermission)
                            || callLogPermissions.contains(requestedPermission)) {
                        context.getPackageManager().addWhitelistedRestrictedPermission(
                                app.packageName, requestedPermission,
                                PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE);
                    }
                }
            }
            currentVersion = 1;
        }

        // XXX: Add new upgrade steps above this point.

        return currentVersion;
    }
}
Loading