Android P,开机给第三方应用授权

问题来源:

客户的一堆app预置到/system/preloadapp之后,开机通过shell脚本拷贝到/data/app安装正常,使用也正常,但问题在于首次使用时会有一堆确认授权的弹框,因为产品的特殊性,客户要求这种预置应用的授权不能让用户参与,也就是开机必须保证所有授权都能静默完成。

解决方法:

单独写了个应用,监听开机广播,在收到广播时进行授权处理。(最早是放在Provision应用中做的,但客户说最好能每次都检查,所以就这么处理了)

AndroidManifext.xml

如下:



    

    
    
    
    
    
    
    
    
        
            
                
            
        
    


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 := optional

LOCAL_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)