Android 在Framework默认授予launcher runtime权限

Android版本: 8.1
问题描述: 要在launcher显示通话记录calllog 和未读短信等内容,涉及运行时权限,导致必须申请运行时权限,这样就会弹窗提示,客户的需求当然是不许弹窗咯,第一次开机就要默认授权。


一,privapp-permissions-mediatek.xml无效了

记得以前做这种权限问题,都是直接在privapp-permissions权限白名单里加上对应权限,但是在当前项目不支持,找不到具体原因,猜测是签名不同导致的,然而具体原因,在此只能打问号了。总之权限白名单失效了。

三年后,现在我知道了权限白名单为什么不生效的原因了。。。
Android 开机权限白名单默认授予系统app权限


二,context.checkSelfPermission() 的流程

在前面白名单里已经写了,但是checkSelfPermission的时候还是返回了deny,于是我开始追寻checkSelfPermission的检测过程了。

Context.java

    @PackageManager.PermissionResult
    public abstract int checkSelfPermission(@NonNull String permission);

checkSelfPermission是抽象方法,自然它的实际实现者就是ContextImpl了

ContextImpl.java

    @Override
    public int checkSelfPermission(String permission) {
        if (permission == null) {
            throw new IllegalArgumentException("permission is null");
        }

        return checkPermission(permission, Process.myPid(), Process.myUid());
    }
@Override
    public int checkPermission(String permission, int pid, int uid) {
        if (permission == null) {
            throw new IllegalArgumentException("permission is null");
        }
        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(TAG, "Missing ActivityManager; assuming " + uid + " holds " + permission);
                return PackageManager.PERMISSION_GRANTED;
            }
        }
        try {
            return am.checkPermission(permission, pid, uid);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

这里是通过IActivityManager接口来处理,当然实现IActivityManager接口的才是实体类,ActivityManagerService extends IActivityManager.Stub

在ActivityManagerService 里 checkPermission

    @Override
    public int checkPermission(String permission, int pid, int uid) {
        if (permission == null) {
            return PackageManager.PERMISSION_DENIED;
        }
        return checkComponentPermission(permission, pid, uid, -1, true);
    }
    int checkComponentPermission(String permission, int pid, int uid,
            int owningUid, boolean exported) {
        if (pid == MY_PID) {
            return PackageManager.PERMISSION_GRANTED;
        }
        return ActivityManager.checkComponentPermission(permission, uid,
                owningUid, exported);
    }

接着进入了ActivityManager里

/** @hide */
    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();
        }
    }

前面都是判断特殊情况,比如

  1. appId == Process.ROOT_UID 意思就是appId是属于root 用户就直接返回PERMISSION_GRANTED
  2. exported == false,意思就是说比如这个组件 contentprovider, 或者activity 设置了exported false, 就直接返回PERMISSION_DENIED

而其余普通情况就是走进AppGlobals.getPackageManager() .checkUidPermission

这里获取到的是Packagemanager接口

    public static IPackageManager getPackageManager() {
        return ActivityThread.getPackageManager();
    }

实际对象是PackageManagerService

@Override
    public int checkUidPermission(String permName, int uid) {
        final int callingUid = Binder.getCallingUid();
        final int callingUserId = UserHandle.getUserId(callingUid);
        final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
        final boolean isUidInstantApp = getInstantAppPackageName(uid) != null;
        final int userId = UserHandle.getUserId(uid);
        if (!sUserManager.exists(userId)) {
            return PackageManager.PERMISSION_DENIED;
        }
        /// M: CTA requirement - permission control  @{
        sCtaManager.reportPermRequestUsage(permName, uid);
        //@}
        synchronized (mPackages) {
            Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
            if (obj != null) {
                if (obj instanceof SharedUserSetting) {
                    if (isCallerInstantApp) {
                        return PackageManager.PERMISSION_DENIED;
                    }
                } else if (obj instanceof PackageSetting) {
                    final PackageSetting ps = (PackageSetting) obj;
                    if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
                        return PackageManager.PERMISSION_DENIED;
                    }
                }
                final SettingBase settingBase = (SettingBase) obj;
                final PermissionsState permissionsState = settingBase.getPermissionsState();
                if (permissionsState.hasPermission(permName, userId)) {
                    if (isUidInstantApp) {
                        BasePermission bp = mSettings.mPermissions.get(permName);
                        if (bp != null && bp.isInstant()) {
                            return PackageManager.PERMISSION_GRANTED;
                        }
                    } else {
                        return PackageManager.PERMISSION_GRANTED;
                    }
                }
                // Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION
                if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState
                        .hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) {
                    return PackageManager.PERMISSION_GRANTED;
                }
            } else {
                ArraySet<String> perms = mSystemPermissions.get(uid);
                if (perms != null) {
                    if (perms.contains(permName)) {
                        return PackageManager.PERMISSION_GRANTED;
                    }
                    if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && perms
                            .contains(Manifest.permission.ACCESS_FINE_LOCATION)) {
                        return PackageManager.PERMISSION_GRANTED;
                    }
                }
            }
        }

        return PackageManager.PERMISSION_DENIED;
    }

这个方法很重要,结果就是在这里判断的,
根据打印可知,mSettings.getUserIdLPr 返回对应于launcher id的object不是空,于是obj != null
而检测结果显示
permissionsState.hasPermission(permName, userId) == false
这就说明 ,在launcher的PermissionsState里是不包含我们所要的运行权限的。

但是这个permissionsState是系统初始化的时候就对应好了的,也就是初始化的时候根本就没有这个授予权限。

三,从系统初始化找为什么没有自动授权

在系统初始化完成后PackageManagerService自然也要调用systemReady;

PackageManagerService–systemReady()

    @Override
    public void systemReady() {
		...
        // If we upgraded grant all default permissions before kicking off.
        for (int userId : grantPermissionsUserIds) {
            mDefaultPermissionPolicy.grantDefaultPermissions(userId);
        }
        ...
   }

这里有个默认授权的地方。

    public void grantDefaultPermissions(int userId) {
        if (mService.hasSystemFeature(PackageManager.FEATURE_EMBEDDED, 0)) {
            grantAllRuntimePermissions(userId);
        } else {
            grantPermissionsToSysComponentsAndPrivApps(userId);
            grantDefaultSystemHandlerPermissions(userId);
            grantDefaultPermissionExceptions(userId);
        }
    }

这里PackageManager.FEATURE_EMBEDDED是没有支持的,接着走下面三个方法,重点在第一个方法里。

grantPermissionsToSysComponentsAndPrivApps
看这方法名不就是授权给系统组件和特权app吗? 但是为什么launcher明明是priv app里的,还是没有权限?

    private void grantPermissionsToSysComponentsAndPrivApps(int userId) {
        Log.i(TAG, "Granting permissions to platform components for user " + userId);

        synchronized (mService.mPackages) {
            for (PackageParser.Package pkg : mService.mPackages.values()) {
                if (!isSysComponentOrPersistentPlatformSignedPrivAppLPr(pkg)
                        || !doesPackageSupportRuntimePermissions(pkg)
                        || pkg.requestedPermissions.isEmpty()) {
                    continue;
                }
                grantRuntimePermissionsForPackageLocked(userId, pkg);
            }
        }
    }

遍历系统packages, 做了三个判断,只有通过判断才能走下去

  1. isSysComponentOrPersistentPlatformSignedPrivAppLPr
  2. doesPackageSupportRuntimePermissions
  3. pkg.requestedPermissions.isEmpty()

这里第一个方法就是判断该组件是不是系统组件或者是Persistent的系统签名的app

private boolean isSysComponentOrPersistentPlatformSignedPrivAppLPr(PackageParser.Package pkg) {
        if (UserHandle.getAppId(pkg.applicationInfo.uid) < FIRST_APPLICATION_UID) {
            return true;
        }
        if (!pkg.isPrivilegedApp()) {
            return false;
        }
        PackageSetting sysPkg = mService.mSettings.getDisabledSystemPkgLPr(pkg.packageName);
        if (sysPkg != null && sysPkg.pkg != null) {
            if ((sysPkg.pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
                return false;
            }
        } else if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
            return false;
        }
        return PackageManagerService.compareSignatures(mService.mPlatformPackage.mSignatures,
                pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
    }

对laucnher来说pkg.isPrivilegedApp()肯定是true ,但是这里的ApplicationInfo.FLAG_PERSISTENT,launcher是没有这个属性的,然后后面就是系统签名匹配,pkg.mSignatures,
也就是LOCAL_CERTIFICATE := platform ,launcher也是没有设置的。所以这里返回肯定是false。

然后第二项,检查package 的targetSdkVersion是不是大于22,也就是android 6.0

    private static boolean doesPackageSupportRuntimePermissions(PackageParser.Package pkg) {
        return pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
    }

第三项,pkg.requestedPermissions.isEmpty()
权限列表是否为空,

根据条件判断,这里必然三项检查返回必须都是false才能走下去,也就是 必须是 系统签名且FLAG_PERSISTENT的app 且 targetsdk>22 且 permissions 不为空才会往下走,否则continue跳到下一个package

只有满足条件的app
才能走后面的grantRuntimePermissionsForPackageLocked

    private void grantRuntimePermissionsForPackageLocked(int userId, PackageParser.Package pkg) {
        Set<String> permissions = new ArraySet<>();
        for (String permission :  pkg.requestedPermissions) {
            BasePermission bp = mService.mSettings.mPermissions.get(permission);
            if (bp != null && bp.isRuntime()) {
                permissions.add(permission);
            }
        }
        if (!permissions.isEmpty()) {
            grantRuntimePermissionsLPw(pkg, permissions, true, userId);
        }
    }
    private void grantRuntimePermissionsLPw(PackageParser.Package pkg, Set<String> permissions,
            boolean systemFixed, int userId) {
        grantRuntimePermissionsLPw(pkg, permissions, systemFixed, false, userId);
    }

接下来所做的就是授权了

    private void grantRuntimePermissionsLPw(PackageParser.Package pkg, Set<String> permissions,
            boolean systemFixed, boolean isDefaultPhoneOrSms, int userId) {
		......
		mService.grantRuntimePermission(pkg.packageName, permission, userId);
		......
		省略
private void grantRuntimePermission(String packageName, String name, final int userId,
            boolean overridePolicy) {
        ......
		final int result = permissionsState.grantRuntimePermission(bp, userId);
		......
}

也就是前面提到的permissionsState里给与对应权限,

    public int grantRuntimePermission(BasePermission permission, int userId) {
        enforceValidUserId(userId);
        if (userId == UserHandle.USER_ALL) {
            return PERMISSION_OPERATION_FAILURE;
        }
        return grantPermission(permission, userId);
    }

流程就是这样,后面不看了,对于launcher的问题,是出在前面的判断里,
打印知道,在launcher包做判断检查是否系统组件的时候返回了false,这就导致系统初始化的时候并不会自动授权launcher的运行权限。
而关键的地方

(pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0

PackageManagerService.compareSignatures(mService.mPlatformPackage.mSignatures,
                pkg.mSignatures) == PackageManager.SIGNATURE_MATCH

这两项必须满足,所以要在 <application 标签里添加

android:persistent="true"

以及在makefile里添加

LOCAL_CERTIFICATE := platform

只有这样才会满足前面的条件。

重新编译,launcher 再检查权限,就是返回已经授权通话相关权限了。解决了默认runtime授权。

int result = mcontext.checkSelfPermission("android.permission.READ_PHONE_STATE")
result = PackageManager.PERMISSION_GRANTED

其实对于开发来说,还有更粗暴的方式,就是直接在前面判断launcher包名返回true,不过这样实在太粗暴了

if (pkg.packageName.contains("launcher3")) {
            return true;
}

总结:

由于运行权限的问题,导致好几个地方不能正确获取权限,而又不允许弹窗申请,所以必须默认授权,这就一定要走源码,通过走一下权限检查流程,加深了我对权限这一块的理解。以后再有这种问题,就直接上了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值