【Android】权限等修改

系统:Android 9.0
需求详情:要求所有应用(系统/非系统)声明权限都放开,除位置和麦克风权限

1.放开所有权限

所有的应用,不管是系统应用还是非系统应用,只要在manifest中定义的权限,首次开机检测时通通默认赋予
\framework\base\services\core\java\com\android\server\pm\permission\PermissionManagerService.java

private void grantPermissions(PackageParser.Package pkg,boolean replace,
        String packageOfInterest,PermissionCallback callback) {
            ...
            if (sCtaManager.isCtaSupported()){
                //android原生赋值操作,对需要动态申请的为GRANT_RUNTIME
            } else {
                grant = GRANT_INSTALL;//默认开启所有的权限赋值
            }
        }

2.不开放位置和麦克风权限

在放开所有权限的基础上,不赋予位置和麦克风权限
\framework\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) {
                for (int i = 0; i < N; i++) {
                    final String permName = pkg.requestedPermissions.get(i);
                    if (permName.contains("ACCESS_FINE_LOCATION") || permName.contains("ACCESS_COARSE_LOCATION") || permName.contains("RECORD_AUDIO")) {
                        continue;
                    }
                }            
            }       
}

此外,还需要修改DefaultPermissionGrantPolicy.java
之前修改PMS中addAllPermissions跳过位置和麦克风权限,导致刷机后系统无法正常启动,去掉里面关于位置和麦克风权限授予后,正常开机

例如:对默认浏览器应用位置权限的申请:
//grantRuntimePermissions(browserPackage, LOCATION_PERMISSIONS, userId);

3.提供接口来注册指定包名应用的权限

定义

//调用注册和注销方法时传入的states值
private final String GRANT_RUNTIME_PERMISSION = "grantRuntimePermission";
private final String REOVKE_RUNTIME_PERMISSION = "revokeRuntimePermission";

使用反射的方法提供接口

private void grantOrRevokePermissions(String packageName,String permissionName,String states) {
    Log.i(TAG,"grantOrRevokePermissions states = " + states);
    try {
        UserManager um = UserManager.get(mContext);
        UserHandle user = UserHandle.of(um.getUserHandle());
        Method method = packageManager.getClass().getDeclaredMethod(states,String.class,String.class,UserHandle.class);
        method.setAccessible(true);
        if (permissionName.contains("ACCESS_FINE_LOCATION") || permissionName.contains("ACCESS_COARSE_LOCATION")) {
            method.invoke(packageManager, packageName,mLocationPermission[0] , Process.myUserHandle());
            method.invoke(packageManager, packageName,mLocationPermission[1] , Process.myUserHandle());
        } else {
            method.invoke(packageManager, packageName, permissionName, Process.myUserHandle());
        }
    }
}

\framework\base\services\core\java\com\android\server\pm\permission\PermissionManagerService.java

private void grantRuntimePermission(String permName, String packageName, boolean overridePolicy,
        int callingUid, final int userId, PermissionCallback callback) {
            ...
            //应用调用接口注册权限时,会提示SYSTEM_FIXED,生成失败,所以选择直接绕过报错继续执行
            if (DEBUG_PERMISSIONS) {//定义在PackagManagerServices中
                if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
                    throw new SecurityException("Cannot grant system fixed permission "
                            + permName + " for package " + packageName);
                }
            }
}

4.撤销权限

接口方法与注册方法共用,只是states值记得传REOVKE_RUNTIME_PERMISSION
\framework\base\services\core\java\com\android\server\pm\permission\PermissionManagerService.java
绕开注销权限错误

private void revokeRuntimePermission(String permName, String packageName,
        boolean overridePolicy, int callingUid, int userId, PermissionCallback callback) {
        ...
            if (DEBUG_PERMISSIONS) {
                if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0
                                    && UserHandle.getCallingAppId() != Process.SYSTEM_UID) {
                    throw new SecurityException("Non-System UID cannot revoke system fixed permission "
                                + permName + " for package " + packageName);
                }
            }
}

同时还需要修改PermissionsState.java中
调用接口revoke时,会走hasPermission以及permissionData.revoke,而在android settings中手动关闭时不会走这几处,直接用userid进行判断

    private int revokePermission(BasePermission permission, int userId) {
        final String permName = permission.getName();
        //add start
        if (!hasPermission(permName, userId) && userId == UserHandle.USER_ALL) {
            return PERMISSION_OPERATION_FAILURE;
        }
        //add end

        final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
        final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;

        PermissionData permissionData = mPermissions.get(permName);

        //add start
        if (!permissionData.revoke(userId) && userId == UserHandle.USER_ALL) {
            return PERMISSION_OPERATION_FAILURE;
        }
        //add  end

        if (permissionData.isDefault()) {
            ensureNoPermissionData(permName);
        }

        if (hasGids) {
            final int[] newGids = computeGids(userId);
            if (oldGids.length != newGids.length) {
                return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
            }
        }

        return PERMISSION_OPERATION_SUCCESS;
    }

5.默认开放悬浮窗权限

默认已获取

6.保留已授权权限

修改后发现放置在除system/app/下的应用外,再次install -r -d,申请的位置和麦克风权限也会被收回
类似于手机应用覆盖安装升级,不会收回已赋予的权限
没找到修改方法 ,只能采用白名单方式进行判别

后面找到了修改的地方,比白名单操作方式更简洁
涉及文件
\frameworks\base\services\core\java\com\android\server\pm\permission\PermissionManagerService.java

原始修改

新增代码

     //白名单判断
     boolean isWhiteListApp(String packagename,String filePath){
        ArrayList<String> whiteListApp = readData(filePath);
        for (String whitelistItem : whiteListApp) {
            if (whitelistItem.equals(packagename)) {
                return true;
            }
        }
        return false;
    }
    //读取指定文件地址
    private ArrayList<String> readData(String filePath) {
        ArrayList<String> whiteListApp = new ArrayList<String>();
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath)));
            String line ="";
            while ((line = br.readLine()) != null){
                whiteListApp.add(line);
            }
            br.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return whiteListApp;
    }
    //将包名写入白名单
    private boolean writeToWhiteList(String packagename,String filePath) {
        String sFilePath = filePath;
        File file = new File(sFilePath);
        try {
            if (!file.exists()) {
                file.createNewFile();
            }
            if (file.exists()) {
                //读数据
                ArrayList<String> whiteListApp =  readData(sFilePath);
                Iterator<String> it = whiteListApp.iterator();

                if (whiteListApp.isEmpty()) {
                    whiteListApp.add(packagename);
                    writeDataToList(whiteListApp,sFilePath);
                } else {
                    //判断包名是否已存在
                    if (isWhiteListApp(packagename,sFilePath)) {
                        return true;
                    } else {
                        whiteListApp.add(packagename);
                        writeDataToList(whiteListApp,sFilePath);
                    }
                }
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }
    //具体实现写入函数
    private void writeDataToList(ArrayList<String> data,String filePath) throws IOException {
        File file = new File(filePath);
        FileOutputStream fileOutputStream = new FileOutputStream(file);
        String newLine = System.getProperty("line.separator");
        for (String s : data) {
            fileOutputStream.write(s.getBytes());
            fileOutputStream.write(newLine.getBytes());
        }
        fileOutputStream.close();
    }
    //撤销权限时,将包名移出白名单
    private void removeToWhiteList(String packagename,String filepath) throws IOException {
        String sFilePath = filepath;
        //先判断是否存在于白名单之中
        if (isWhiteListApp(packagename,sFilePath)) {
            //移除
            ArrayList<String> whiteListApp = readData(filepath);
            //因为写入时已经确保list中最多只存在一个相匹配的包名,直接用remove即可
            whiteListApp.remove(packagename);
            writeDataToList(whiteListApp,filepath);
        }
    }

调用处理

grantRuntimePermission()
在if(callback!=null)之前进行判断就行了

if (permName.contains("ACCESS_FINE_LOCATION") || permName.contains("ACCESS_COARSE_LOCATION")) {
            //检查是否添加白名单
            if (!isWhiteListApp(packageName, sLocateFilePath)) {
                writeToWhiteList(packageName, sLocateFilePath);
            }
        }
        if (permName.contains("RECORD_AUDIO")) {
            if (!isWhiteListApp(packageName,sRecordFilePath)) {
                writeToWhiteList(packageName,sRecordFilePath);
            }
        }

revokeRuntimePermission()
也是在判断callback不等于null之前运行前即可

if (permName.contains("ACCESS_FINE_LOCATION") || permName.contains("ACCESS_COARSE_LOCATION")) {
            try {
                removeToWhiteList(packageName,sLocateFilePath);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (permName.contains("RECORD_AUDIO")) {
            try {
                removeToWhiteList(packageName,sRecordFilePath);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

最终修改方案

PermissionManagerService.java中
grantPermissions方法中修改,覆盖安装、升级安装时对于权限的处理与已经安装好的应用最大的区别为调用grantPermissions时传入的replace值不同
在Android9.0中,会将原来已赋值的权限重置,再继续走下面的权限生成,所以之前的判断方式也要同步进行修改

        if (replace) {
            ps.setInstallPermissionsFixed(false);
            //add for handle permission rest when replaced app 20231113 start
            if (ps.isSharedUser()) {
                synchronized (mLock) {
                    updatedUserIds = revokeUnusedSharedUserPermissionsLocked(
                            ps.getSharedUser(), UserManagerService.getInstance().getUserIds());
                    if (!ArrayUtils.isEmpty(updatedUserIds)) {
                        runtimePermissionsRevoked = true;
                    }
                }
            }
//            if (!ps.isSharedUser()) {
//                //origPermissions = new PermissionsState(permissionsState);
//                //permissionsState.reset();
//            } else {
//                // We need to know only about runtime permission changes since the
//                // calling code always writes the install permissions state but
//                // the runtime ones are written only if changed. The only cases of
//                // changed runtime permissions here are promotion of an install to
//                // runtime and revocation of a runtime from a shared user.
//                synchronized (mLock) {
//                    updatedUserIds = revokeUnusedSharedUserPermissionsLocked(
//                            ps.getSharedUser(), UserManagerService.getInstance().getUserIds());
//                    if (!ArrayUtils.isEmpty(updatedUserIds)) {
//                        runtimePermissionsRevoked = true;
//                    }
//                }
//            }
            //add for handle permission rest when replaced app 20231113 end
        }

而此时,权限的生成修改如下:

        synchronized (mLock) {
            final int N = pkg.requestedPermissions.size();
            /// M: CTA requirement - permission control
            boolean pkgReviewRequired = isPackageNeedsReview(pkg,  ps.getSharedUser());

            for (int i = 0; i < N; i++) {
                final String permName = pkg.requestedPermissions.get(i);
                //add for skip location and record permission 20231014 start
                if (permName.contains("ACCESS_FINE_LOCATION") || permName.contains("ACCESS_COARSE_LOCATION")) {
                    if (!replace) {
                        continue;
                    }
                }
                if (permName.contains("RECORD_AUDIO")) {
                    if (!replace) {
                        continue;
                    }
                }
                //add for skip location and record permission 20231014 end
        }

总结

有一些应用在设置中进行权限授予时,会报错,提示信息为grantRuntimePermission方法中:

if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
                Slog.w(TAG, "Cannot grant runtime permission to a legacy app");
                return;
            }

对于这一部分,修不修改皆可,主要是应用低版本难以兼容

你可能感兴趣的:(Android,android,车载系统)