前言
此次使用Android9.0做一个动态权限白名单的功能,即客户要求他们的app默认就该应用所有的权限,在运行过程中,不需要去动态申请权限,即不弹窗。
在Android M之前, Runtime permissions是直接被当作是install permissons,即在安装的时候就直接grant了。
而在 Android M以后, Android加入了runtime permissions, 也就是dangerous permissons, 这些权限有可能会刺探用户隐私等等危害。
这样系统在安装APP的时候就不会默认grant runtime permissions.
修改思路
申请时,拒绝申请
一开始想着在应用提出动态申请的请求时,就将该请求掐断,这样就不会弹窗出来,即在如下方法中直接返回:
frameworks/base/core/java/android/app/Activity.java
public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
return; //在这里直接return
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;
}
Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
mHasCurrentPermissionsRequest = true;
}
这样做,确实不会弹窗,但其实应用并没有获取到想要的动态权限,此路不通。
在check权限时直接返回授权
这里是在检测app权限的时候,直接给app返回授权,但其实这里也没有真正的授权,是一种假授权。
从update permission入手
对Android来说,既然有动态权限的申请,那么就会有一个地方用于处理权限更新的地方,这次决定从这里入手。
通过强大的grep指令,在frameworks目录下进行搜索,最终定位到updatePermission方法中,该方法用于更新应用权限,该方法最终调用了grantPermissions方法,源码如下:
//这里由于该方法太长,只贴出部分的上下文代码
frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
private void grantPermissions(PackageParser.Package pkg, boolean replace,
String packageOfInterest, PermissionCallback callback) {
......
synchronized (mLock) {
final int N = pkg.requestedPermissions.size();
for (int i = 0; i < N; i++) {
final String permName = pkg.requestedPermissions.get(i);
final BasePermission bp = mSettings.getPermissionLocked(permName);
final boolean appSupportsRuntimePermissions =
pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M;
if (DEBUG_INSTALL) {
Log.i(TAG, "Package " + pkg.packageName + " checking " + permName + ": " + bp);
}
if (bp == null || bp.getSourcePackageSetting() == null) {
if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
if (DEBUG_PERMISSIONS) {
Slog.i(TAG, "Unknown permission " + permName
+ " in package " + pkg.packageName);
}
}
continue;
}
// Limit ephemeral apps to ephemeral allowed permissions.
if (pkg.applicationInfo.isInstantApp() && !bp.isInstant()) {
if (DEBUG_PERMISSIONS) {
Log.i(TAG, "Denying non-ephemeral permission " + bp.getName()
+ " for package " + pkg.packageName);
}
continue;
}
if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) {
if (DEBUG_PERMISSIONS) {
Log.i(TAG, "Denying runtime-only permission " + bp.getName()
+ " for package " + pkg.packageName);
}
continue;
}
final String perm = bp.getName();
boolean allowedSig = false;
int grant = GRANT_DENIED;
// Keep track of app op permissions.
if (bp.isAppOp()) {
mSettings.addAppOpPackage(perm, pkg.packageName);
}
if (bp.isNormal()) {
// For all apps normal permissions are install time ones.
grant = GRANT_INSTALL;
} else if (bp.isRuntime()) {
// If a permission review is required for legacy apps we represent
// their permissions as always granted runtime ones since we need
// to keep the review required permission flag per user while an
// install permission's state is shared across all users.
if (!appSupportsRuntimePermissions && !mSettings.mPermissionReviewRequired) {
// For legacy apps dangerous permissions are install time ones.
grant = GRANT_INSTALL;
} else if (origPermissions.hasInstallPermission(bp.getName())) {
// For legacy apps that became modern, install becomes runtime.
grant = GRANT_UPGRADE;
} else if (isLegacySystemApp) {
// For legacy system apps, install becomes runtime.
// We cannot check hasInstallPermission() for system apps since those
// permissions were granted implicitly and not persisted pre-M.
grant = GRANT_UPGRADE;
} else {
/ For modern apps keep runtime permissions unchanged.
//permission check whitelist app @{
if (!isneedCheckPermission(pkg.packageName)) {
grant = GRANT_INSTALL;
} else {
grant = GRANT_RUNTIME;
}
//permission check whitelist app @}
}
} else if (bp.isSignature()) {
// For all apps signature permissions are install time ones.
allowedSig = grantSignaturePermission(perm, pkg, bp, origPermissions);
if (allowedSig) {
grant = GRANT_INSTALL;
}
}
......
}
在Android6.0之前是没有动态权限这个概念的,只有在Android6.0之后才有。在Android6.0之前,对于权限,都将其进行安装,不需要进行GRANT_RUNTIME,统一都是GRANT_INSTALL,而到了Android6.0之后,则多出了一个GRANT_RUNTIME,即运行时权限,如果是normal级别的权限,则会apk安装的过程中,就直接授权,但如果是危险级别的,涉及到用户隐秘的,则需要在调用的时候,弹窗让用户选择是否授权,也就是所谓的GRANT_RUNTIME。
所以我们的方法很简单,就是找到GRANT_RUNTIME ,然后使用GRANT_INSTALL代替即可。
白名单
这里还涉及到一个白名单配置方法,主要是新增一个cfg配置文件,文件内容是每一个应用的包名,这里的包名可以不完整,但一定要有明确的关键词,避免跟其他的应用混淆。该方法如下:
frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
private boolean isneedCheckPermission(String packagename) {
ArrayList<String> whiteListApp = new ArrayList<String>();
try {
BufferedReader br = new BufferedReader(new java.io.InputStreamReader(
new FileInputStream("/system/etc/WhiteListPermissionFilter.cfg")));
String line = "";
while ((line = br.readLine()) != null) {
whiteListApp.add(line);
}
br.close();
} catch (java.io.FileNotFoundException ex) {
Log.d(TAG, "WhiteListPermissionFilter.cfg - FileNotFoundException");
return true;
} catch(java.io.IOException ex) {
Log.d(TAG, "WhiteListPermissionFilter.cfg - IOException");
return true;
}
Iterator<String> it = whiteListApp.iterator();
while (it.hasNext()) {
String whitelistItem = it.next();
Log.d(TAG, "whitelistItem:" + whitelistItem);
if (packagename.contains(whitelistItem)) {
return false;
}
}
return true;
}
最后
若想关注更多Android系统开发相关文章,可扫码关注如下微信公众号。