Android版本: 8.1
问题描述: 要在launcher显示通话记录calllog 和未读短信等内容,涉及运行时权限,导致必须申请运行时权限,这样就会弹窗提示,客户的需求当然是不许弹窗咯,第一次开机就要默认授权。
记得以前做这种权限问题,都是直接在privapp-permissions权限白名单里加上对应权限,但是在当前项目不支持,找不到具体原因,猜测是签名不同导致的,然而具体原因,在此只能打问号了。总之权限白名单失效了。
在前面白名单里已经写了,但是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();
}
}
前面都是判断特殊情况,比如
而其余普通情况就是走进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, 做了三个判断,只有通过判断才能走下去
这里第一个方法就是判断该组件是不是系统组件或者是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
这两项必须满足,所以要在
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;
}
由于运行权限的问题,导致好几个地方不能正确获取权限,而又不允许弹窗申请,所以必须默认授权,这就一定要走源码,通过走一下权限检查流程,加深了我对权限这一块的理解。以后再有这种问题,就直接上了。