系统:Android 9.0
需求详情:要求所有应用(系统/非系统)声明权限都放开,除位置和麦克风权限
所有的应用,不管是系统应用还是非系统应用,只要在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;//默认开启所有的权限赋值
}
}
在放开所有权限的基础上,不赋予位置和麦克风权限
\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);
定义
//调用注册和注销方法时传入的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);
}
}
}
接口方法与注册方法共用,只是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;
}
默认已获取
修改后发现放置在除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;
}
对于这一部分,修不修改皆可,主要是应用低版本难以兼容