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文件等)

    推荐阅读