申请获取权限无效问题分析

最近拿到高通的新机器,开发中突然有同事发现一个问题,Contacts替换后android.permission.CALL_PHONE权限获取不到,系统的联系人应用居然没法打电话

看日志中拉起GrantPermissionsActivity的intent是已经发送的,那么就看权限对话框为何没有出现。

权限弹框没有出现

android/packages/apps/PackageInstaller/src/com/android/packageinstaller/permission/ui/GrantPermissionsActivity.java

    public void onCreate(Bundle icicle) {
        ...
        if (!showNextPermissionGroupGrantRequest()) {
            setResultAndFinish();
        }

    }
添加日志看到onCreate末尾的if语句条件符合所以activity直接finish了,那么就是系统判断无需申请权限

    private boolean showNextPermissionGroupGrantRequest() {
        final int groupCount = mRequestGrantPermissionGroups.size();

        int currentIndex = 0;
        for (GroupState groupState : mRequestGrantPermissionGroups.values()) {
            if (groupState.mState == GroupState.STATE_UNKNOWN) {
                ...
                return true;
            }

            currentIndex++;
        }
        return false;
    }
如果mRequestGrantPermissionGroups不是空的话,会返回true的,那即是mRequestGrantPermissionGroups中没有任何元素

public void onCreate(Bundle icicle) {
 ...
 for (String requestedPermission : mRequestedPermissions) {
        	Log.i(LOG_TAG, "requestedPermission = " + requestedPermission );
            AppPermissionGroup group = null;
      //mAppPermissions是app中AndroidManifest中声明的权限
            for (AppPermissionGroup nextGroup : mAppPermissions.getPermissionGroups()) {
                if (nextGroup.hasPermission(requestedPermission)) {
                    group = nextGroup;
                    break;
                }
            }
            if (group == null) {
                continue;
            }
            // We allow the user to choose only non-fixed permissions. A permission
            // is fixed either by device policy or the user denying with prejudice.
            if (!group.isUserFixed() && !group.isPolicyFixed()) {
                switch (permissionPolicy) {
                    ...

                    default: {
                        if (AppPermissionGroup.isStrictOpEnable()) { 
              //这里是从系统属性判断,adb shell getprop命令查看这个是false的
                            if (!group.checkRuntimePermission(null)) {
                                mRequestGrantPermissionGroups.put(group.getName(),
                                        new GroupState(group));
                            } else {
                                group.grantRuntimePermissions(false);
                                updateGrantResults(group);
                            }
                        } else {
                            if (!group.areRuntimePermissionsGranted()) {
                //日志查看最终是这条分支没有走
                                mRequestGrantPermissionGroups.put(group.getName(),
                                        new GroupState(group));
                            } else {
                                group.grantRuntimePermissions(false);
                                updateGrantResults(group);
                            }
                        }
                    }
                    break;
                }
            } else {
             	Log.i(LOG_TAG, "requestedPermission22222 = "  );
                // if the permission is fixed, ensure that we return the right request result
                updateGrantResults(group);
            }
        }
 ...

}
mRequestGrantPermissionGroups是在onCreate中添加元素的,日志分析结果居然是权限组已经是赋权的状态。

packages/apps/PackageInstaller/src/com/android/packageinstaller/permission/model/AppPermissionGroup.java

    public boolean areRuntimePermissionsGranted() {
        return areRuntimePermissionsGranted(null);
    }

public boolean areRuntimePermissionsGranted(String[] filterPermissions) {
        if (LocationUtils.isLocationGroupAndProvider(mName, mPackageInfo.packageName)) {
            return LocationUtils.isLocationEnabled(mContext);
        }
        final int permissionCount = mPermissions.size();
        for (int i = 0; i < permissionCount; i++) {
            Permission permission = mPermissions.valueAt(i);
            if (filterPermissions != null
                    && !ArrayUtils.contains(filterPermissions, permission.getName())) {
                continue; //filterPermissions过滤掉了不参加判断的权限
            }
            if (mAppSupportsRuntimePermissions) {
                if (permission.isGranted()) {
                    //权限组中只有有一条是赋权的,既满足条件
                    return true;
                }
            } else if (permission.isGranted() && (permission.getAppOp() == null
                    || permission.isAppOpAllowed())) {
                return true;
            }
        }
        return false;
    }
从代码看出权限组中只要有一条权限满足,就返回true

权限组和权限的概念可百度,基本的一条是如果已经给予权限组中的一条权限,那么后续同组的其它权限申请时会直接给予,而不用弹框。

例如拨号权限
frameworks/base/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java

    private static final Set<String> PHONE_PERMISSIONS = new ArraySet<>();
    static {
        PHONE_PERMISSIONS.add(Manifest.permission.READ_PHONE_STATE);
        PHONE_PERMISSIONS.add(Manifest.permission.CALL_PHONE);
        PHONE_PERMISSIONS.add(Manifest.permission.READ_CALL_LOG);
        PHONE_PERMISSIONS.add(Manifest.permission.WRITE_CALL_LOG);
        PHONE_PERMISSIONS.add(Manifest.permission.ADD_VOICEMAIL);
        PHONE_PERMISSIONS.add(Manifest.permission.USE_SIP);
        PHONE_PERMISSIONS.add(Manifest.permission.PROCESS_OUTGOING_CALLS);
    }
可见PHONE组权限有7条

日志分析问题手机Contact中READ_PHONE_STATE权限是有效的,但是其余权限无效,所以导致申请权限每次都会直接finish掉。

对比问题手机基线代码和另一平台代码,发现mRequestGrantPermissionGroups的很多if判断是多出来的,尚不确定是google还是高通添加的。


系统app默认权限赋予

问题分析到这里,发现问题的同事突然告诉我知道问题怎么解决了

是AndroidManifest中一条属性

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
    package="com.android.contacts"
    android:sharedUserId="android.uid.shared" >
对比代码发现高通的Contacts中没有  android:sharedUserId="android.uid.shared"这一条

删除后编译app并且push权限问题解决。在第一次使用的时候会申请全部的五条权限,而有share uid的Contacts.apk只会申请三条

packages/apps/ContactsCommon/src/com/android/contacts/common/activity/RequestPermissionsActivity.java

    @Override
    protected String[] getDesiredPermissions() {
        return new String[]{
                permission.ACCESS_FINE_LOCATION, // Location Group
                permission.READ_CONTACTS, // Contacts group
                permission.READ_CALL_LOG, // Permission group phone
                permission.READ_CALENDAR, // Calendar group
                permission.READ_SMS, // SMS group
        };
    }
Contacts申请权限的代码之前我有文章讲解过。

如代码中,有五个权限组,其中有share uid后Contacts group和Phone group的权限就不会弹框提示。

这个短短的一条属性如何会影响权限,其实这个在系统设置中可以看到一点端倪的。在系统设置中Dialer被当做普通程序,而Contact被当做系统应用

即有share uid且签名也是share签名,会被当做是系统应用。那么可以推测系统应用Android会特殊处理,例如默认赋予某些权限。例如Contacts默认拥有Contacts group和Phone group的权限不是非常自然的事情吗。研究下源码发现我的推测是正确的

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

    @Override
    public void systemReady() {
        ...
        for (int userId : grantPermissionsUserIds) {
            mDefaultPermissionPolicy.grantDefaultPermissions(userId);
        }
        ...
    }
包管理器在systemReady回调中启动了特殊uid apk的赋权

frameworks/base/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java

    public void grantDefaultPermissions(int userId) {
        grantPermissionsToSysComponentsAndPrivApps(userId);
        grantDefaultSystemHandlerPermissions(userId);
        grantDefaultPermissionExceptions(userId);
    }

    private void grantDefaultSystemHandlerPermissions(int userId) {
            ...
            // Contacts
            Intent contactsIntent = new Intent(Intent.ACTION_MAIN);
            contactsIntent.addCategory(Intent.CATEGORY_APP_CONTACTS);
            PackageParser.Package contactsPackage = getDefaultSystemHandlerActivityPackageLPr(
                    contactsIntent, userId);
            if (contactsPackage != null
                    && doesPackageSupportRuntimePermissions(contactsPackage)) {
                grantRuntimePermissionsLPw(contactsPackage, CONTACTS_PERMISSIONS, userId);
                grantRuntimePermissionsLPw(contactsPackage, PHONE_PERMISSIONS, userId);
            }
            ...
    }
Contacts被赋予了Contacts group和Phone group的权限


为何Phone group只有有READ_PHONE_STATE赋权

按照上述分析,有shared uid的话Phone group权限全部该赋权才对,为啥问题手机只有一条权限有效?还得继续分析源码

private void grantRuntimePermissionsLPw(PackageParser.Package pkg, Set<String> permissions,
            boolean systemFixed, boolean isDefaultPhoneOrSms, int userId) {
        ...
        List<String> requestedPermissions = pkg.requestedPermissions;
        Set<String> grantablePermissions = null;

        // If this is the default Phone or SMS app we grant permissions regardless
        // whether the version on the system image declares the permission as used since
        // selecting the app as the default Phone or SMS the user makes a deliberate
        // choice to grant this app the permissions needed to function. For all other
        // apps, (default grants on first boot and user creation) we don't grant default
        // permissions if the version on the system image does not declare them.
        if (!isDefaultPhoneOrSms && pkg.isUpdatedSystemApp()) {           
            //isDefaultPhoneOrSms对Contacts来说当然是false的
            //由于Contacts是push到手机替换原有Contacts的,所以这里肯定会走
            PackageSetting sysPs = mService.mSettings.getDisabledSystemPkgLPr(pkg.packageName);
            if (sysPs != null) {
                if (sysPs.pkg.requestedPermissions.isEmpty()) {
                    return;
                }
                if (!requestedPermissions.equals(sysPs.pkg.requestedPermissions)) {
          //这里grantablePermissions实际上就是新app声明的权限
                    grantablePermissions = new ArraySet<>(requestedPermissions);
          //而requestedPermissions实际上是原app声明的权限
                    requestedPermissions = sysPs.pkg.requestedPermissions;
                }
            }
        }

        final int grantablePermissionCount = requestedPermissions.size();
        for (int i = 0; i < grantablePermissionCount; i++) {
      //循环是以原app的声明列表为基础的,表示新app声明的新加权限虽然可能在permissions参数中但是并不会被赋予。
            String permission = requestedPermissions.get(i);

            // If there is a disabled system app it may request a permission the updated
            // version ot the data partition doesn't, In this case skip the permission.
            if (grantablePermissions != null && !grantablePermissions.contains(permission)) {
        //如果是新app没有声明的权限,当然就啥也不用做啦,直接跳出这次循环
                continue;
            }

            if (permissions.contains(permission)) {
        //具体的赋权代码
                ...
            }
        }
    }
可以看出系统app替换和第一次正常安装走的流程有点不一样,新app新增的权限不会被赋权,google的意图应该是新增的权限要用户手动点击确认。不过对比新老Contacts的AndroidManifest文件,发现android.permission.CALL_PHONE都是在列的。但是从代码分析看只有“新app新增的权限不会被赋权”这一条才能解释为啥phone group只有一条权限是起作用的。代码没有问题的话,就可能是Contacts旧的权限数据有问题,对比问题手机和正常手机,在设置app的权限列表明显是不同的,问题手机只有4个权限开关,正常的手机有7个。问题手机居然没有phone group的开关,这也就印证了为啥赋权失败的问题。

问题手机在恢复出厂设置后恢复正常,证明了不是应用的问题。后续恢复出厂后再拿有无share uid的apk多次push,也无复现该问题,app的数据出错具体原因也就很难追踪了。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值