Android P,开机给第三方应用授权
问题来源:
客户的一堆app预置到/system/preloadapp之后,开机通过shell脚本拷贝到/data/app安装正常,使用也正常,但问题在于首次使用时会有一堆确认授权的弹框,因为产品的特殊性,客户要求这种预置应用的授权不能让用户参与,也就是开机必须保证所有授权都能静默完成。
解决方法:
单独写了个应用,监听开机广播,在收到广播时进行授权处理。(最早是放在Provision应用中做的,但客户说最好能每次都检查,所以就这么处理了)
AndroidManifext.xml
【Android P,开机给第三方应用授权】如下:
arrays.xml:配置需要授权的应用白名单 如下:
- com.autonavi.amapautolite
- ctrip.android.view
- com.ecar.assistantnew
- com.hdsc.edog
- cn.kuwo.kwmusiccar
- com.mapgoo.diruite
Android.mk:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)LOCAL_MODULE_TAGS := optionalLOCAL_SRC_FILES := $(call all-java-files-under, src)LOCAL_PACKAGE_NAME := ServiceDemo
LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_DEX_PREOPT := false
include $(BUILD_PACKAGE)# Use the folloing include to make our test apk.
include $(call all-makefiles-under,$(LOCAL_PATH))
BootReceiver.java:
package com.example.servicedemo;
import java.io.File;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
import android.app.AppOpsManager;
import android.app.AppOpsManager.PackageOps;
import android.app.AppOpsManager.OpEntry;
import android.util.Log;
import android.os.Environment;
import android.os.Process;
public class BootReceiver extends BroadcastReceiver {private static final String TAG = "BootReceiver";
private JSONArray mFileList;
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Log.d(TAG, "----onReceive " + intent.toString());
long stime = System.currentTimeMillis();
String[] appPackages = context.getResources().getStringArray(
R.array.app_packages);
for (String appPackage : appPackages) {
setAppPermission(appPackage, context);
}PackageManager pm = context.getPackageManager();
boolean debugAutoGrant = false;
//这个是遍历/data/app下面所有apk,根据apk解析包名再进行授权,应该是存在缺陷的
if (false) {
mFileList = new JSONArray();
getAllFiles("/data/app/", ".apk");
for (int i = 0;
i < mFileList.length();
i++) {
try {
JSONObject jsonObject = mFileList.getJSONObject(i);
String pathString = jsonObject.getString("path");
Log.d(TAG, "-------apk path: " + pathString);
PackageInfo info = pm.getPackageArchiveInfo(pathString,
PackageManager.GET_ACTIVITIES);
if (info != null) {
ApplicationInfo appInfo = info.applicationInfo;
String appName = pm.getApplicationLabel(appInfo)
.toString();
String packname = appInfo.packageName;
String version = info.versionName;
Log.d(TAG, "-------package name: " + packname);
setAppPermission(packname, context);
} else {
Log.e(TAG, "========failed to get package info from "
+ pathString);
}
} catch (JSONException ex) {
ex.printStackTrace();
}
}
}
long etime = System.currentTimeMillis();
Log.d(TAG, "grant permission time: " + (etime - stime) + " ms");
}//这个是获取/data/app下面的所有apk文件路径
private void getAllFiles(String dirPath, String fileType) {
Log.d(TAG, "dirPath: " + dirPath + " fileType: " + fileType);
File f = new File(dirPath);
if (!f.exists()) {
Log.e(TAG, "folder doesn't exist");
return;
}
File[] files = f.listFiles();
if (files == null) {
Log.e(TAG, "empty folder");
return;
}for (File file : files) {
if (file.isFile() && file.getName().endsWith(fileType)) {
String name = file.getName();
String filePath = file.getAbsolutePath();
Log.d(TAG, "apk file is : " + filePath);
try {
JSONObject info = new JSONObject();
info.put("name", name);
info.put("path", filePath);
mFileList.put(info);
} catch (Exception e) {
}
} else if (file.isDirectory()) {
getAllFiles(file.getAbsolutePath(), fileType);
}
}}//授权关键函数
private void setAppPermission(String pkgName, Context context) {
Log.d(TAG, "setAppPermission for package: " + pkgName);
PackageManager pm = context.getPackageManager();
AppOpsManager mAppOps = context.getSystemService(AppOpsManager.class);
try {
PackageInfo pkgInfo = pm.getPackageInfo(pkgName,
PackageManager.GET_PERMISSIONS);
String sharedPkgList[] = pkgInfo.requestedPermissions;
for (int i = 0;
i < sharedPkgList.length;
i++) {
String permName = sharedPkgList[i];
PermissionInfo permissionInfo;
try {
permissionInfo = pm.getPermissionInfo(permName, 0);
} catch (PackageManager.NameNotFoundException e) {
continue;
}
final int opCode = AppOpsManager.permissionToOpCode(permName);
boolean hasAopsCode = opCode > AppOpsManager.OP_NONE
&& opCode < AppOpsManager._NUM_OP;
String aopStr = AppOpsManager.permissionToOp(permName);
boolean hasAopsOp = aopStr != null;
boolean onlyAops = false;
final boolean granted = (pkgInfo.requestedPermissionsFlags[i] & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0;
if ((permissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) != PermissionInfo.PROTECTION_DANGEROUS
|| (permissionInfo.flags & PermissionInfo.FLAG_INSTALLED) == 0
|| (permissionInfo.flags & PermissionInfo.FLAG_REMOVED) != 0) {
if (permName.startsWith("android.")
&& (hasAopsCode || hasAopsOp)) {
onlyAops = true;
Log.d(TAG, "permissionName=" + permName + ",opCode="
+ opCode);
} else {
continue;
}
}
boolean isAopsAllowed = mAppOps.checkOpNoThrow(opCode,
pkgInfo.applicationInfo.uid, pkgInfo.packageName) == AppOpsManager.MODE_ALLOWED;
if ((hasAopsCode || onlyAops) && !isAopsAllowed) {
mAppOps.setMode(opCode, pkgInfo.applicationInfo.uid,
pkgInfo.packageName, AppOpsManager.MODE_ALLOWED);
}
if ((hasAopsOp || onlyAops) && !isAopsAllowed) {
mAppOps.setUidMode(aopStr, pkgInfo.applicationInfo.uid,
AppOpsManager.MODE_ALLOWED);
}
if (!granted && !onlyAops) {
Log.d(TAG, "grant permission " + permName + " to pkg: "
+ pkgName);
pm.grantRuntimePermission(pkgName, permName,
Process.myUserHandle());
}
// set flags
pm.updatePermissionFlags(
permName,
pkgName,
PackageManager.FLAG_PERMISSION_USER_FIXED
| PackageManager.FLAG_PERMISSION_USER_SET
| PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED,
0, Process.myUserHandle());
}
List ops = mAppOps.getOpsForPackage(
pkgInfo.applicationInfo.uid, pkgName, null);
if (ops == null || ops.size() == 0) {
return;
}
for (PackageOps packageOps : ops) {
List entries = packageOps.getOps();
if (entries == null || entries.size() == 0) {
continue;
}
for (OpEntry opEntry : entries) {
mAppOps.setMode(opEntry.getOp(), packageOps.getUid(),
pkgName, AppOpsManager.MODE_ALLOWED);
}
}
} catch (Exception e) {
Log.e(TAG, "setAppPermission exception, the packageName is "
+ pkgName);
e.printStackTrace();
}
}
}
其他: 通过白名单授权测试通过。
同时也测试了遍历/data/app下apk,再根据apk解析包名进行授权的解决方法,实测下来会有一点问题。个别apk无法解析包名,从而无法做授权确认。代码保留了做个参考吧。
参考: https://www.cxyzjd.com/articl...
参考了上述文章,补全了其他细节(manifest、白名单机制、mk文件等)
推荐阅读
- android第三方框架(五)ButterKnife
- 喂,你结婚我给你随了个红包
- 成交的种子咖啡冥想
- 一百二十三夜,请嫁给我
- 每日一话(49)——一位清华教授在朋友圈给大学生的9条建议
- Android中的AES加密-下
- 带有Hilt的Android上的依赖注入
- 历史教学书籍
- 写给陈羡
- android|android studio中ndk的使用