android 权限申请

简介: android 权限申请

android 权限申请


官方文档:请求运行时权限  |  Android 开发者  |  Android Developers

官方提供的模板使用了三个条件分支来请求应用权限:

  1.checkSelfPermission用来检查应用是否获得 需要请求的权限,如果有权限,直接执行需要的动作;

  2.shouldShowRequestPermissionRationale在用户曾经点击过拒绝这一权限的选项后为true(非“拒绝不再询问”选项),这时系统发现应用没有响应的权限,开发者可以在这一条件分支加上相关说明的界面,向用户指出申请这个权限的必要性,但是还是有必要在界面向用户提供“拒绝”的选项;

  3.当走到最后一个分支,也就意味着应用还没有响应的权限,且用户不曾点击过拒绝这一权限的选项,于是使用requestPermissions唤起对话框申请权限。

public void requestPermission(String permission){
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
            //已经授权
            Log.e("TAG","已经授权");
        }else if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
            //拒绝过授权
            Log.e("TAG","拒绝过授权");
            ActivityCompat.requestPermissions(this, new String[]{permission}, 1);
        }
        else {
            //未授权
            Log.e("TAG","未授权");
            ActivityCompat.requestPermissions(this, new String[]{permission}, 1);
        }
    }

源码追踪


检查权限 checkSelfPermission

checkSelfPermission最终会调用到ActivityManager#checkComponentPermission,并且使得传入的pid和uid参数分别为应用的pid和uid。

public static int checkSelfPermission(@NonNull Context context, @NonNull String permission) {
        ObjectsCompat.requireNonNull(permission, "permission must be non-null");
        return context.checkPermission(permission, Process.myPid(), Process.myUid());
    }
./frameworks/base/core/java/android/content/Context.java
@CheckResult(suggest="#enforcePermission(String,int,int,String)")
    @PackageManager.PermissionResult
    public abstract int checkPermission(@NonNull String permission, int pid, int uid);
frameworks\base\core\java\android\app\ContextImpl.java
 @Override
    public int checkPermission(String permission, int pid, int uid) {
        if (permission == null) {
            throw new IllegalArgumentException("permission is null");
        }
        if (mParams.isRenouncedPermission(permission)
                && pid == android.os.Process.myPid() && uid == android.os.Process.myUid()) {
            Log.v(TAG, "Treating renounced permission " + permission + " as denied");
            return PERMISSION_DENIED;
        }
        return PermissionManager.checkPermission(permission, pid, uid);
    }
./frameworks/base/core/java/android/permission/PermissionManager.java
/** @hide */
    public static int checkPermission(@Nullable String permission, int pid, int uid) {
        return sPermissionCache.query(new PermissionQuery(permission, pid, uid));
    }
private static final PropertyInvalidatedCache<PermissionQuery, Integer> sPermissionCache =
            new PropertyInvalidatedCache<PermissionQuery, Integer>(
                    2048, CACHE_KEY_PACKAGE_INFO, "checkPermission") {
                @Override
                protected Integer recompute(PermissionQuery query) {
                    return checkPermissionUncached(query.permission, query.pid, query.uid);
                }
            };
/* @hide */
    private static int checkPermissionUncached(@Nullable String permission, int pid, int uid) {
        final IActivityManager am = ActivityManager.getService();
        if (am == null) {
            // Well this is super awkward; we somehow don't have an active ActivityManager
            // instance. If we're testing a root or system UID, then they totally have whatever
            // permission this is.
            final int appId = UserHandle.getAppId(uid);
            if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
                Slog.w(LOG_TAG, "Missing ActivityManager; assuming " + uid + " holds "
                        + permission);
                return PackageManager.PERMISSION_GRANTED;
            }
            Slog.w(LOG_TAG, "Missing ActivityManager; assuming " + uid + " does not hold "
                    + permission);
            return PackageManager.PERMISSION_DENIED;
        }
        try {
            return am.checkPermission(permission, pid, uid);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
frameworks\base\core\java\android\app\IActivityManager.aidl
@UnsupportedAppUsage
    int checkPermission(in String permission, int pid, int uid);
frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java
@Override
    public int checkPermission(String permission, int pid, int uid) {
        if (permission == null) {
            return PackageManager.PERMISSION_DENIED;
        }
        return checkComponentPermission(permission, pid, uid, -1, true);
    }
public static int checkComponentPermission(String permission, int pid, int uid,
            int owningUid, boolean exported) {
        if (pid == MY_PID) {
            return PackageManager.PERMISSION_GRANTED;
        }
        // If there is an explicit permission being checked, and this is coming from a process
        // that has been denied access to that permission, then just deny.  Ultimately this may
        // not be quite right -- it means that even if the caller would have access for another
        // reason (such as being the owner of the component it is trying to access), it would still
        // fail.  This also means the system and root uids would be able to deny themselves
        // access to permissions, which...  well okay. ¯\_(ツ)_/¯
        if (permission != null) {
            synchronized (sActiveProcessInfoSelfLocked) {
                ProcessInfo procInfo = sActiveProcessInfoSelfLocked.get(pid);
                if (procInfo != null && procInfo.deniedPermissions != null
                        && procInfo.deniedPermissions.contains(permission)) {
                    return PackageManager.PERMISSION_DENIED;
                }
            }
        }
        return ActivityManager.checkComponentPermission(permission, uid,
                owningUid, exported);
    }

frameworks\base\core\java\android\app\ActivityManager.java

/** @hide */
    @UnsupportedAppUsage
    public static int checkComponentPermission(String permission, int uid,
            int owningUid, boolean exported) {
        // Root, system server get to do everything.
        final int appId = UserHandle.getAppId(uid);
        if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
            return PackageManager.PERMISSION_GRANTED;
        }
        // Isolated processes don't get any permissions.
        if (UserHandle.isIsolated(uid)) {
            return PackageManager.PERMISSION_DENIED;
        }
        // If there is a uid that owns whatever is being accessed, it has
        // blanket access to it regardless of the permissions it requires.
        if (owningUid >= 0 && UserHandle.isSameApp(uid, owningUid)) {
            return PackageManager.PERMISSION_GRANTED;
        }
        // If the target is not exported, then nobody else can get to it.
        if (!exported) {
            /*
            RuntimeException here = new RuntimeException("here");
            here.fillInStackTrace();
            Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid,
                    here);
            */
            return PackageManager.PERMISSION_DENIED;
        }
        if (permission == null) {
            return PackageManager.PERMISSION_GRANTED;
        }
        try {
            return AppGlobals.getPackageManager()
                    .checkUidPermission(permission, uid);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

AM端的判断原则是:

  1.对root和system的uid通过检查;

  2.对isolated的uid不通过检查;

  3.对检查的权限是发起检查者定义的情况直接通过;

  4.对访问不开放(android:exported为false)组件情况不通过;

  5.对检查的权限为null的情况直接通过;

  如果上面5条还不能确认结果的话,交给PMS的checkUidPermission函数再进行判断:

frameworks\base\core\java\android\app\AppGlobals.java
    @UnsupportedAppUsage
    public static IPackageManager getPackageManager() {
        return ActivityThread.getPackageManager();
    }
frameworks\base\core\java\android\content\pm\IPackageManager.aidl
int checkUidPermission(String permName, int uid);
frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java
@Override
    public int checkUidPermission(String permName, int uid) {
        return mComputer.checkUidPermission(permName, uid);
    }
private final ComputerTracker mComputer = new ComputerTracker(this);
ComputerTracker 是PackageManagerService的内部类
/**
     * This subclass delegates to methods in a Computer after reference-counting the computer.
     */
    private static class ComputerTracker implements Computer {
/**
* This class records the Computer being used by a thread and the Computer's reference
* count. There is a thread-local copy of this class.
*/
private static class ThreadComputer {
public final int checkUidPermission(String permName, int uid) {
            ThreadComputer current = snapshot();
            try {
                return current.mComputer.checkUidPermission(permName, uid);
            } finally {
                current.release();
            }
        }
// NOTE: Can't remove without a major refactor. Keep around for now.
        public final int checkUidPermission(String permName, int uid) {
            return mPermissionManager.checkUidPermission(uid, permName);
        }
PackageManagerService使用 ThreadLocal 和 锁同步了内部的状态
// Internal interface for permission manager
    private final PermissionManagerServiceInternal mPermissionManager;
framework/base/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
    int checkUidPermission(int uid, @NonNull String permissionName);
frameworks\base\services\core\java\com\android\server\pm\permission\PermissionManagerService.java
private int checkUidPermission(int uid, String permName) {
        // Not using Objects.requireNonNull() here for compatibility reasons.
        if (permName == null) {
            return PackageManager.PERMISSION_DENIED;
        }
        final int userId = UserHandle.getUserId(uid);
        if (!mUserManagerInt.exists(userId)) {
            return PackageManager.PERMISSION_DENIED;
        }
        final CheckPermissionDelegate checkPermissionDelegate;
        synchronized (mLock) {
            checkPermissionDelegate = mCheckPermissionDelegate;
        }
        if (checkPermissionDelegate == null)  {
            return checkUidPermissionImpl(uid, permName);
        }
        return checkPermissionDelegate.checkUidPermission(uid, permName,
                this::checkUidPermissionImpl);
    }

mCheckPermissionDelegate是一个可嵌入的权限判断实现,由setCheckPermissionDelegateLocked嵌入,如果不为null,则调用嵌入代码的checkUidPermission进行判断。app进行检查的情况下这段嵌入实现为null,所以会调PMS的checkUidPermissionImpl进行判断。

private int checkUidPermissionImpl(int uid, String permName) {
        final AndroidPackage pkg = mPackageManagerInt.getPackage(uid);
        return checkUidPermissionInternal(pkg, uid, permName);
    }
private int checkUidPermissionInternal(@Nullable AndroidPackage pkg, int uid,
            @NonNull String permissionName) {
        if (pkg != null) {
            final int userId = UserHandle.getUserId(uid);
            return checkPermissionInternal(pkg, false, permissionName, userId);
        }
        synchronized (mLock) {
            if (checkSingleUidPermissionInternalLocked(uid, permissionName)) {
                return PackageManager.PERMISSION_GRANTED;
            }
            final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName);
            if (fullerPermissionName != null
                    && checkSingleUidPermissionInternalLocked(uid, fullerPermissionName)) {
                return PackageManager.PERMISSION_GRANTED;
            }
        }
        return PackageManager.PERMISSION_DENIED;
    }
private int checkPermissionInternal(@NonNull AndroidPackage pkg, boolean isPackageExplicit,
            @NonNull String permissionName, @UserIdInt int userId) {
        final int callingUid = getCallingUid();
        if (isPackageExplicit || pkg.getSharedUserId() == null) {
            if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
                return PackageManager.PERMISSION_DENIED;
            }
        } else {
            if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
                return PackageManager.PERMISSION_DENIED;
            }
        }
        final int uid = UserHandle.getUid(userId, pkg.getUid());
        final boolean isInstantApp = mPackageManagerInt.getInstantAppPackageName(uid) != null;
        synchronized (mLock) {
            final UidPermissionState uidState = getUidStateLocked(pkg, userId);
            if (uidState == null) {
                Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
                        + userId);
                return PackageManager.PERMISSION_DENIED;
            }
            if (checkSinglePermissionInternalLocked(uidState, permissionName, isInstantApp)) {
                return PackageManager.PERMISSION_GRANTED;
            }
            final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName);
            if (fullerPermissionName != null && checkSinglePermissionInternalLocked(uidState,
                    fullerPermissionName, isInstantApp)) {
                return PackageManager.PERMISSION_GRANTED;
            }
        }
        return PackageManager.PERMISSION_DENIED;
    }

PermissionManagerService的判断原则如下:

  1.申请检查者拥有shareuserid且是instant app的情况不通过;

  2.申请检查者的userid在系统中未启动的情况不通过;

  3.不通过filterAppAccess过滤规则的不通过,主要是关于instant app的;

  4.检查包信息内部的授权情况,授权了的话就通过;(核心)

  5.如果检查的权限是ACCESS_COARSE_LOCATION,只要ACCESS_FINE_LOCATION被授权了就通过;如果检查的权限是INTERACT_ACROSS_USERS,只要INTERACT_ACROSS_USERS_FULL被授权了就通过。

 

shouldShowRequestPermissionRationale展示权限申请原因

  这个函数的调用流程是Activity#shouldShowRequestPermissionRationale->PackageManager#shouldShowRequestPermissionRationale-》ApplicationPackageManager#shouldShowRequestPermissionRationale-》。

  先介绍权限的4个flag:FLAG_PERMISSION_SYSTEM_FIXED,FLAG_PERMISSION_POLICY_FIXED,FLAG_PERMISSION_USER_FIXED,FLAG_PERMISSION_USER_SET。

  FLAG_PERMISSION_SYSTEM_FIXED:系统固定,意味着这个权限是系统设定的,应用无法通过grantRuntimePermission/revokeRuntimePermission修改运行时权限的授权状况。这个flag一般由开机授权组件DefaultPermissionGrantPolicy添加,非system的UID不能改动这个flag。

  FLAG_PERMISSION_POLICY_FIXED:设备管理器(DevicePolicyManager)固定,意味着这个权限是DevicePolicyManager设定的,例如全局设置DevicePolicyManager#setPermissionPolicy或者单一应用权限设置 setPermissionGrantState。app除非拥有ADJUST_RUNTIME_PERMISSIONS_POLICY权限,否则无法通过grantRuntimePermission/revokeRuntimePermission修改运行时权限的授权状况,同样,除非拥有ADJUST_RUNTIME_PERMISSIONS_POLICY权限,否则无法改动这个flag。

  FLAG_PERMISSION_USER_FIXED:用户固定,在用户曾经点击过一次拒绝权限的情况下,再点击一次“拒绝,不再询问”的选项后就会为这个权限设置这个flag。这个flag被设置后,运行时权限处于未授权状态,而且不会再弹出相关对话框让用户选择。只有在“应用信息“”的“权限”选项中设置才能重新授权。

  FLAG_PERMISSION_USER_SET:用户设置,在用户首次点击拒绝权限的请款修改被设置。如果上面的FLAG_PERMISSION_XXX_FIXED的flag没有被设置,FLAG_PERMISSION_USER_SET的flag被设置了,shouldShowRequestPermissionRationale就会返回true,这个时候应该向用户展示需要这个权限的原因。

ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)
public static boolean shouldShowRequestPermissionRationale(@NonNull Activity activity,
            @NonNull String permission) {
        if (Build.VERSION.SDK_INT >= 23) {
            return Api23Impl.shouldShowRequestPermissionRationale(activity, permission);
        }
        return false;
    }
@DoNotInline
        static boolean shouldShowRequestPermissionRationale(Activity activity, String permission) {
            return activity.shouldShowRequestPermissionRationale(permission);
        }
public boolean shouldShowRequestPermissionRationale(@NonNull String permission) {
        return getPackageManager().shouldShowRequestPermissionRationale(permission);
    }
frameworks\base\core\java\android\content\pm\PackageManager.java
@SuppressWarnings("HiddenAbstractMethod")
    @UnsupportedAppUsage
    public abstract boolean shouldShowRequestPermissionRationale(@NonNull String permName);
frameworks\base\core\java\android\app\ApplicationPackageManager.java
public class ApplicationPackageManager extends PackageManager
@Override
    @UnsupportedAppUsage
    public boolean shouldShowRequestPermissionRationale(String permName) {
        return getPermissionManager().shouldShowRequestPermissionRationale(permName);
    }

frameworks\base\core\java\android\permission\PermissionManager.java

//@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
    public boolean shouldShowRequestPermissionRationale(@NonNull String permissionName) {
        try {
            final String packageName = mContext.getPackageName();
            return mPermissionManager.shouldShowRequestPermissionRationale(packageName,
                    permissionName, mContext.getUserId());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

frameworks\base\services\core\java\com\android\server\pm\permission\PermissionManagerService.java

@Override
    public boolean shouldShowRequestPermissionRationale(String packageName, String permName,
            @UserIdInt int userId) {
        final int callingUid = Binder.getCallingUid();
        if (UserHandle.getCallingUserId() != userId) {
            mContext.enforceCallingPermission(
                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
                    "canShowRequestPermissionRationale for user " + userId);
        }
        final int uid =
                mPackageManagerInt.getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, userId);
        if (UserHandle.getAppId(callingUid) != UserHandle.getAppId(uid)) {
            return false;
        }
        if (checkPermission(packageName, permName, userId)
                == PackageManager.PERMISSION_GRANTED) {
            return false;
        }
        final int flags;
        final long identity = Binder.clearCallingIdentity();
        try {
            flags = getPermissionFlagsInternal(packageName, permName, callingUid, userId);
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
        final int fixedFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
                | PackageManager.FLAG_PERMISSION_POLICY_FIXED
                | PackageManager.FLAG_PERMISSION_USER_FIXED;
        if ((flags & fixedFlags) != 0) {
            return false;
        }
        synchronized (mLock) {
            final Permission permission = mRegistry.getPermission(permName);
            if (permission == null) {
                return false;
            }
            if (permission.isHardRestricted()
                    && (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) == 0) {
                return false;
            }
        }
        final long token = Binder.clearCallingIdentity();
        try {
            if (permName.equals(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
                    && mPlatformCompat.isChangeEnabledByPackageName(BACKGROUND_RATIONALE_CHANGE_ID,
                    packageName, userId)) {
                return true;
            }
        } catch (RemoteException e) {
            Log.e(TAG, "Unable to check if compatibility change is enabled.", e);
        } finally {
            Binder.restoreCallingIdentity(token);
        }
        return (flags & PackageManager.FLAG_PERMISSION_USER_SET) != 0;
    }

请求权限requestPermissions

可以看到,requestPermissions主要是构建了一个action是"android.content.pm.action.REQUEST_PERMISSIONS",package是PermissionController的intent,然后唤起相关界面。关于PermissionController apk可以参考谷歌相关说明:

PermissionController

frameworks/base/core/java/android/app/Activity.java

public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
        if (requestCode < 0) {
            throw new IllegalArgumentException("requestCode should be >= 0");
        }
        if (mHasCurrentPermissionsRequest) {
            Log.w(TAG, "Can request only one set of permissions at a time");
            // Dispatch the callback with empty arrays which means a cancellation.
            onRequestPermissionsResult(requestCode, new String[0], new int[0]);
            return;
        }
        if (!getAttributionSource().getRenouncedPermissions().isEmpty()) {
            final int permissionCount = permissions.length;
            for (int i = 0; i < permissionCount; i++) {
                if (getAttributionSource().getRenouncedPermissions().contains(permissions[i])) {
                    throw new IllegalArgumentException("Cannot request renounced permission: "
                            + permissions[i]);
                }
            }
        }
        final Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
        startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
        mHasCurrentPermissionsRequest = true;
    }

frameworks\base\core\java\android\content\pm\PackageManager.java

@NonNull
    @UnsupportedAppUsage
    public Intent buildRequestPermissionsIntent(@NonNull String[] permissions) {
        if (ArrayUtils.isEmpty(permissions)) {
           throw new IllegalArgumentException("permission cannot be null or empty");
        }
        Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS);
        intent.putExtra(EXTRA_REQUEST_PERMISSIONS_NAMES, permissions);
        intent.setPackage(getPermissionControllerPackageName());
        return intent;
    }

packages\modules\Permission\PermissionController响应这个intent的是:packages\modules\Permission\PermissionController\src\com\android\permissioncontroller\permission\ui\GrantPermissionsActivity.java

  这个activity内容比较多,onCreate主要的内容是:

  1.授权回调GrantPermissionsViewHandlerImpl;

  2.权限分组AppPermissionGroup;

  3.展示授权交互界面showNextPermissionGroupGrantRequest;

权限分组AppPermissionGroup

  先从AppPermissionGroup说起。在Permission Controller的apk实现里面,每个运行时权限都属于一个AppPermissionGroup。

packages\modules\Permission\PermissionController\src\com\android\permissioncontroller\permission\model\AppPermissionGroup.java

public static AppPermissionGroup create(Context context, PackageInfo packageInfo,
            String permissionName, boolean delayChanges) {
        PermissionInfo permissionInfo;
        try {
            permissionInfo = context.getPackageManager().getPermissionInfo(permissionName, 0);
        } catch (PackageManager.NameNotFoundException e) {
            return null;
        }
        if ((permissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
                != PermissionInfo.PROTECTION_DANGEROUS
                || (permissionInfo.flags & PermissionInfo.FLAG_INSTALLED) == 0
                || (permissionInfo.flags & PermissionInfo.FLAG_REMOVED) != 0) {
            return null;
        }
        String group = Utils.getGroupOfPermission(permissionInfo);
        PackageItemInfo groupInfo = permissionInfo;
        if (group != null) {
            try {
                groupInfo = context.getPackageManager().getPermissionGroupInfo(group, 0);
            } catch (PackageManager.NameNotFoundException e) {
                /* ignore */
            }
        }
        List<PermissionInfo> permissionInfos = null;
        if (groupInfo instanceof PermissionGroupInfo) {
            try {
                permissionInfos = Utils.getPermissionInfosForGroup(context.getPackageManager(),
                        groupInfo.name);
            } catch (PackageManager.NameNotFoundException e) {
                /* ignore */
            }
        }
        return create(context, packageInfo, groupInfo, permissionInfos, delayChanges);
    }

PermissionController apk内部划分好的权限组有(下面表格按“|权限|权限组|”的形式呈现),如果在表格里面找不到所属的组,则所属权限组由其android:permissionGroup属性决定。需要注意的是:

  1.如果一个app需要若干运行时权限,那么相同组的权限会被放到一个AppPermissionGroup里面,有些前后台权限的情况除外,例如ACCESS_FINE_LOCATION前台权限和ACCESS_COARSE_LOCATION后台权限都属于android.Manifest.permission_group.LOCATION这个权限组,但是它们是放在两个不同的AppPermissionGroup里面的。

  2.同一个权限,由PermissionController apk内部划分好的权限组和定义权限时android:permissionGroup属性指定的权限组可能不是同一个。执行“adb shell pm list permissions -d -g”命令可以看到定义运行时权限时指定的权限组,其中定义ACCESS_FINE_LOCATION权限时指定的是android.permission-group.UNDEFINED,但是在PermissionController apk被划分到android.Manifest.permission_group.LOCATION。

  3.对于应用需要申请的权限集合mRequestedPermissions,对这个集合里面的所有权限按所在的AppPermissionGroup进行分组,形成一个授权分组GroupState。GroupState内部有一个状态值mState,可以是STATE_UNKNOWN(初始默认值),STATE_ALLOWED(允许授权),STATE_DENIED(拒绝授权),STATE_SKIPPED(跳过授权步骤)。GroupState内部还有一个affectedPermissions集合,包含这个授权分组影响到的所有权限。

前后台权限的AppPermissionGroup

  如果一个权限定义时用android:backgroundPermission指定了另一个权限,那么指定者权限被称为前台权限,被指定者权限被称为后台权限。顾名思义,前台权限指的是应用运行在前台可以获得的权限,后台权限指是应用运行在后台可以获得的权限。

  一个AppPermissionGroup包含的权限如果包含了后台权限,那么会将这些后台权限放到另一个AppPermissionGroup中,并记录在第一个AppPermissionGroup的mBackgroundPermissions中。


目录
相关文章
|
3月前
|
存储 安全 Android开发
"解锁Android权限迷宫:一场惊心动魄的动态权限请求之旅,让你的应用从平凡跃升至用户心尖的宠儿!"
【8月更文挑战第13天】随着Android系统的更新,权限管理变得至关重要。尤其从Android 6.0起,引入了动态权限请求,增强了用户隐私保护并要求开发者实现更精细的权限控制。本文采用问答形式,深入探讨动态权限请求机制与最佳实践,并提供示例代码。首先解释了动态权限的概念及其重要性;接着详述实现步骤:定义、检查、请求权限及处理结果;最后总结了六大最佳实践,包括适时请求、解释原因、提供替代方案、妥善处理拒绝情况、适应权限变更及兼容旧版系统,帮助开发者打造安全易用的应用。
73 0
|
2月前
|
存储 API Android开发
"解锁Android权限迷宫:一场惊心动魄的动态权限请求之旅,让你的应用从平凡跃升至用户心尖的宠儿!"
随着Android系统的更新,权限管理成为应用开发的关键。尤其在Android 6.0(API 级别 23)后,动态权限请求机制的引入提升了用户隐私保护,要求开发者进行更精细的权限管理。
65 2
|
6月前
|
XML API 数据库
Android权限
Android权限 【5月更文挑战第3天】
101 0
|
6月前
|
Android开发 开发者
Android打开开发者权限
Android打开开发者权限
72 0
|
6月前
|
Android开发
Android 9.0中sdcard 的权限和挂载问题
Android 9.0中sdcard 的权限和挂载问题
80 0
|
6月前
|
Android开发
Android修改默认system/bin/下可执行程序拥有者和权限,使用实例,只有root和系统app权限才能执行某个命令。
Android修改默认system/bin/下可执行程序拥有者和权限,使用实例,只有root和系统app权限才能执行某个命令。 【5月更文挑战第2天】
306 0
|
6月前
|
Android开发
android 12 U盘 /mnt/media_rw 下读取文件异常 没有权限
android 12 U盘 /mnt/media_rw 下读取文件异常 没有权限
263 0
|
6月前
|
XML Android开发 数据安全/隐私保护
android 11后文件读写访问权限申请
android 11后文件读写访问权限申请
241 0
|
5天前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
7天前
|
Android开发 Swift iOS开发
探索安卓与iOS开发的差异和挑战
【10月更文挑战第37天】在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统扮演着主角。它们各自拥有独特的特性、优势以及面临的开发挑战。本文将深入探讨这两个平台在开发过程中的主要差异,从编程语言到用户界面设计,再到市场分布的不同影响,旨在为开发者提供一个全面的视角,帮助他们更好地理解并应对在不同平台上进行应用开发时可能遇到的难题和机遇。