前言
androidQ 中权限部分做了略微改动,MTK 又增加了访问数据和 WLAN 网络权限控制,app 首次请求网络时会弹出权限框询问,大致原理就是第一次启动时扫描设备上已经安装的apk包名维护一个数据库,初始化时都为 CheckedPermRecord.STATUS_FIRST_CHECK,当首次请求网络时,拦截弹框授权。后面会详细分析,这里先跳过。
系统的运行时权限弹框风格做了调整,以前源码在 PackageInstaller 中,androidQ 中改到了 PermissionController中。
文件清单
device/mediatek/system/common/device.mk
frameworks/base/services/core/java/com/android/server/pm/MTKPackageManagerUtil.java
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
vendor/mediatek/proprietary/frameworks/base/data/etc/pms_sysapp_grant_permission_list.txt
vendor/mediatek/proprietary/packages/apps/NetworkDataController/NetworkDataControllerService/src/com/mediatek/security/datamanager/DatabaseManager.java
vendor/mediatek/proprietary/packages/apps/NetworkDataController/NetworkDataControllerService/src/com/mediatek/security/service/NetworkDataControllerService.java
vendor/mediatek/proprietary/packages/apps/PermissionController/AndroidManifest.xml
vendor/mediatek/proprietary/packages/apps/PermissionController/src/com/android/packageinstaller/PackageChangedService.java
vendor/mediatek/proprietary/packages/apps/PermissionController/src/com/android/packageinstaller/PermissionGrantHelper.java
vendor/mediatek/proprietary/packages/apps/PermissionController/src/com/android/packageinstaller/TemporaryFileManager.java
系统app权限全开
还是通过 pms_sysapp_grant_permission_list.txt 文件来控制要全开的 app 包名
copy pms_sysapp_grant_permission_list.txt 文件到 out 的位置有所变动,不在是 device/mediatek/common/device.mk 中,改到 system 路径下
device/mediatek/system/common/device.mk
ifneq ($(strip $(MTK_BASIC_PACKAGE)), yes)
+ PRODUCT_COPY_FILES += $(call add-to-product-copy-files-if-exists,vendor/mediatek/proprietary/frameworks/base/data/etc/pms_sysapp_grant_permission_list.txt:system/etc/permissions/pms_sysapp_grant_permission_list.txt)
PRODUCT_COPY_FILES += $(call add-to-product-copy-files-if-exists,vendor/mediatek/proprietary/frameworks/base/data/etc/pms_sysapp_removable_system_list.txt:system/etc/permissions/pms_sysapp_removable_system_list.txt)
PRODUCT_COPY_FILES += $(call add-to-product-copy-files-if-exists,vendor/mediatek/proprietary/frameworks/base/data/etc/pms_sysapp_removable_vendor_list.txt:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/pms_sysapp_removable_vendor_list.txt)
endif
vendor\mediatek\proprietary\frameworks\base\data\etc\pms_sysapp_grant_permission_list.txt
com.android.dialer
com.android.soundrecorder
com.android.browser
com.mediatek.filemanager
com.android.calendar
com.android.email
PMS 启动完成读取 txt 给对应的 app 授权
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
public void systemReady() {
enforceSystemOrRoot("Only the system can claim the system is ready");
+ // add for system app grant permission S
+ if (mFirstBoot) {
+ MTKPackageManagerUtil.slientGrantRuntimePermission(mContext, mPermissionManager);
+ }
+ // add for system app grant permission E
+
mSystemReady = true;
final ContentResolver resolver = mContext.getContentResolver();
同级目录下新增 MTKPackageManagerUtil.java,相比之前 BasePermission 通过 PermissionManagerServiceInternal 来获取
final BasePermission bp = (BasePermission) mPermissionManager.getPermissionTEMP(permission);
bp 过滤只需非 RuntimePermission,bp 无需过滤 development 相关
frameworks/base/services/core/java/com/android/server/pm/MTKPackageManagerUtil.java
package com.android.server.pm;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.os.Process;
import android.text.TextUtils;
import android.util.Log;
import com.android.server.pm.permission.BasePermission;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import android.content.pm.IPackageManager;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.Manifest;
import com.android.internal.util.ArrayUtils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;
public class MTKPackageManagerUtil{
private static String TAG = "MTKPackageManagerUtil";
//copy from vendor\mediatek\proprietary\frameworks\base\services\core\java\com\mediatek\server\pm\PmsExtImpl.java
private static final File GRANT_SYS_APP_LIST_SYSTEM = Environment
.buildPath(Environment.getRootDirectory(), "etc", "permissions",
"pms_sysapp_grant_permission_list.txt");
private static HashSet<String> sGrantSystemAppSet = new HashSet<String>();
private static HashSet<String> sGrantPermissionSet = new HashSet<String>();
private static IPackageManager mIpm;
private static AppOpsManager mAppOpsManager;
public static void slientGrantRuntimePermission(Context mContext,
PermissionManagerServiceInternal mPermissionManager){
sGetGrantSystemAppFromFile(sGrantSystemAppSet, GRANT_SYS_APP_LIST_SYSTEM);
PackageManager mPackageManager = mContext.getPackageManager();
mIpm = AppGlobals.getPackageManager();
mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
Iterator<String> it = sGrantSystemAppSet.iterator();
Log.d(TAG, "sGrantSystemAppSet:");
while (it.hasNext()) {
sGrantPermissionSet.clear();
String pkgName = it.next();
Log.d(TAG, "pkgName="+pkgName);
try {
PackageInfo mPackageInfo = mPackageManager.getPackageInfo(pkgName, PackageManager.GET_PERMISSIONS);
for (String permission : mPackageInfo.requestedPermissions){
int status = mPackageManager.checkPermission(permission, pkgName);
//final BasePermission bp = mSettings.mPermissions.get(permission);
final BasePermission bp = (BasePermission) mPermissionManager.getPermissionTEMP(permission);
if (status != PackageManager.PERMISSION_GRANTED && bp != null) {
if (!bp.isRuntime() /*&& !bp.isDevelopment()*/) {
Log.d(TAG, "Permission " + bp.getName() + " is not a changeable permission type");
continue;
}
sGrantPermissionSet.add(permission);
}
}
Log.e(TAG, " need grantRuntimePermission size:"+sGrantPermissionSet.size());
for (String permission : sGrantPermissionSet) {
mPackageManager.grantRuntimePermission(pkgName,
permission, Process.myUserHandle());
}
if (checkInstallPackagesPermission(pkgName, mPackageInfo)) {
Log.e(TAG, pkgName + " need grant INSTALL_PACKAGES permission");
mAppOpsManager.setMode(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES,
mPackageInfo.applicationInfo.uid, pkgName, AppOpsManager.MODE_ALLOWED);
Log.e(TAG, "grant INSTALL_PACKAGES permission done");
}
} catch (Exception e) {
//e.printStackTrace();
Log.d(TAG, e.getMessage());
}
}
}
private static boolean checkInstallPackagesPermission(String packageName, PackageInfo mPackageInfo){
int uid = mPackageInfo.applicationInfo.uid;
//boolean permissionGranted = hasPermission(Manifest.permission.REQUEST_INSTALL_PACKAGES, uid);
boolean permissionRequested = hasRequestedAppOpPermission(Manifest.permission.REQUEST_INSTALL_PACKAGES, packageName);
int appOpMode = getAppOpMode(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES, uid, packageName);
return appOpMode != AppOpsManager.MODE_DEFAULT || permissionRequested;
}
private static int getAppOpMode(int appOpCode, int uid, String packageName) {
return mAppOpsManager.checkOpNoThrow(appOpCode, uid, packageName);
}
private static boolean hasRequestedAppOpPermission(String permission, String packageName) {
try {
String[] packages = mIpm.getAppOpPermissionPackages(permission);
return ArrayUtils.contains(packages, packageName);
} catch (Exception exc) {
Log.e(TAG, "PackageManager dead. Cannot get permission info");
return false;
}
}
private static boolean hasPermission(String permission, int uid) {
try {
int result = mIpm.checkUidPermission(permission, uid);
return result == PackageManager.PERMISSION_GRANTED;
} catch (Exception e) {
Log.e(TAG, "PackageManager dead. Cannot get permission info");
return false;
}
}
/**
* Get removable system app list from config file
*
* @param resultSet
* Returned result list
* @param file
* The config file
*/
private static void sGetGrantSystemAppFromFile(
HashSet<String> resultSet, File file) {
resultSet.clear();
FileReader fr = null;
BufferedReader br = null;
try {
if (file.exists()) {
fr = new FileReader(file);
} else {
Log.d(TAG, "file in " + file + " does not exist!");
return;
}
br = new BufferedReader(fr);
String line;
while ((line = br.readLine()) != null) {
line = line.trim();
if (!TextUtils.isEmpty(line)) {
Log.d(TAG, "read line " + line);
resultSet.add(line);
}
}
Log.e(TAG,"GRANT_SYS_APP_LIST_SYSTEM size="+resultSet.size());
} catch (Exception io) {
Log.d(TAG, io.getMessage());
} finally {
try {
if (br != null) {
br.close();
}
if (fr != null) {
fr.close();
}
} catch (IOException io) {
Log.d(TAG, io.getMessage());
}
}
}
}
installded app 权限全开
运行时权限
仿照之前的方式通过监听 ACTION_PACKAGE_ADDED 广播,当新 app 安装成功,则获取app要申请的权限进行自动授权
在 PermissionController 中新增 PackageChangedService、TemporaryFileManager、PermissionGrantHelper
**vendor/mediatek/proprietary/packages/apps/PermissionController/AndroidManifest.xml **
+++ b/alps/vendor/mediatek/proprietary/packages/apps/PermissionController/AndroidManifest.xml
@@ -6,7 +6,7 @@
coreApp="true"
android:versionCode="290000000"
android:versionName="29 system image"
->
+ android:sharedUserId="android.uid.system">
<original-package android:name="com.android.permissioncontroller" />
@@ -76,6 +76,18 @@
</intent-filter>
</receiver>
+ <!-- add S -->
+ <receiver android:name="com.android.packageinstaller.TemporaryFileManager"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.BOOT_COMPLETED" />
+ </intent-filter>
+ </receiver>
+
+ <service android:name="com.android.packageinstaller.PackageChangedService"
+ android:exported="false"/>
+ <!-- add E -->
+
<service android:name="com.android.packageinstaller.permission.service.LocationAccessCheck$LocationAccessCheckJobService"
android:permission="android.permission.BIND_JOB_SERVICE" />
PackageChangedService、TemporaryFileManager 代码和之前一样,不在贴出,PermissionGrantHelper.java 和之前有不同
AppPermissions() 构造函数类型变动,AppPermissions(Context context, PackageInfo packageInfo, boolean sortGroups, boolean delayChanges, Runnable onErrorCallback)
delayChanges 传递 false 时,app 安装完成后权限都已经默认授权,但第一次进入 app 时仍然会显示 ReviewPermissionsActivity,需要再次点击授权才能进入app 主页。
delayChanges 传递 true 时, 运行时会报 SecurityException ADJUST_RUNTIME_PERMISSIONS_POLICY
3887-3887/com.android.permissioncontroller E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.android.permissioncontroller, PID: 3887
java.lang.SecurityException: updatePermissionFlags requires android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY
at android.os.Parcel.createException(Parcel.java:2071)
at android.os.Parcel.readException(Parcel.java:2039)
at android.os.Parcel.readException(Parcel.java:1987)
at android.content.pm.IPackageManager$Stub$Proxy.updatePermissionFlags(IPackageManager.java:5569)
at android.app.ApplicationPackageManager.updatePermissionFlags(ApplicationPackageManager.java:713)
at com.android.packageinstaller.permission.model.AppPermissionGroup.persistChanges(AppPermissionGroup.java:1280)
at com.android.packageinstaller.permission.model.AppPermissions.persistChanges(AppPermissions.java:196)
at com.android.packageinstaller.permission.ui.handheld.ReviewPermissionsFragment.confirmPermissionsReviewforCTA(ReviewPermissionsFragment.java:643)
at com.android.packageinstaller.permission.ui.handheld.ReviewPermissionsFragment.onClick(ReviewPermissionsFragment.java:173)
根据堆栈信息找到 PermissionManagerService.java 中,屏蔽对应的异常
frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -2908,8 +2908,10 @@ public class PermissionManagerService {
"updatePermissionFlags");
if ((flagMask & FLAG_PERMISSION_POLICY_FIXED) != 0 && !overridePolicy) {
- throw new SecurityException("updatePermissionFlags requires "
- + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY);
+ //20200421 annnotation for PermissionController app AppPermissionGroup.java:1280
+ //mPackageManager.updatePermissionFlags don't throw exception
+ /*throw new SecurityException("updatePermissionFlags requires "
+ + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY);*/
}
// Only the system can change these flags and nothing else.
现在运行授权时不报异常了,但是发现授权为空,app 并未授权,再次检查代码,发现遗漏了关键行代码
mAppPermissions.persistChanges(true);
完整的 PermissionGrantHelper 类如下
vendor/mediatek/proprietary/packages/apps/PermissionController/src/com/android/packageinstaller/PermissionGrantHelper.java
package com.android.packageinstaller;
import android.content.Context;
import android.util.Log;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import com.android.packageinstaller.permission.model.AppPermissionGroup;
import com.android.packageinstaller.permission.model.AppPermissions;
import com.android.packageinstaller.permission.model.Permission;
import com.android.packageinstaller.permission.utils.ArrayUtils;
import com.android.packageinstaller.permission.utils.Utils;
import java.util.List;
public class PermissionGrantHelper{
public static void slientGrantRuntimePermission(Context context, String packageName){
PackageInfo packageInfo;
try {
packageInfo = context.getPackageManager().getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
} catch (PackageManager.NameNotFoundException e) {
Log.e("permission", "can't get PackageInfo for packageName="+ packageName);
return;
}
AppPermissions mAppPermissions = new AppPermissions(context, packageInfo, false, true,
new Runnable() {
@Override
public void run() {
}
});
Log.e("permission", " AppPermissionGroup size=="+mAppPermissions.getPermissionGroups().size());
if (mAppPermissions.getPermissionGroups().isEmpty()) {
Log.e("permission", "mAppPermissions size isEmpty");
return;
}
for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
String[] permissionsToGrant = null;
final int permissionCount = group.getPermissions().size();
for (int j = 0; j < permissionCount; j++) {
final Permission permission = group.getPermissions().get(j);
if (!permission.isGranted()) {
permissionsToGrant = ArrayUtils.appendString(
permissionsToGrant, permission.getName());
Log.e("permission", "permissionName=" + permission.getName());
}
}
if (permissionsToGrant != null) {
group.grantRuntimePermissions(false, permissionsToGrant);
Log.i("permission", "grantRuntimePermissions permissionsToGrant");
//group.revokeRuntimePermissions(false);
}
//group.resetReviewRequired();
}
mAppPermissions.persistChanges(true);
}
}
network 权限
烧写完第一次开机时,提示 以下系统应用需要访问数据流量和WLAN网络 界面,查找到 NetworkDataController 中
当初始化时给我们需要授权的系统应用默认赋权,这里偷懒了,用 list 包含要授权的系统app 包名,当然也可以按照之前的通过读取 txt 方式来获取包名。
vendor/mediatek/proprietary/packages/apps/NetworkDataController/NetworkDataControllerService/src/com/mediatek/security/datamanager/DatabaseManager.java
@@ -107,10 +107,15 @@ public class DatabaseManager {
Log.d(TAG, "all = " + packageList.size() + ", old = "
+ storedPackages.size());
Set<Integer> uids = packageList.keySet();
+ //20200421 add for system orignal app don't need check data permission
+ List<String> pkgNames = getSystemGrantApp();
for (Integer uid : uids) {
+ String packageName = packageList.get(uid);
+ Log.e(TAG, "packageName = "+ packageName);
dataBaseHelper.addNetworkDataPerm(new CheckedPermRecord(
- packageList.get(uid), uid,
- CheckedPermRecord.STATUS_FIRST_CHECK));
+ packageName, uid, //CheckedPermRecord.STATUS_FIRST_CHECK));
+ pkgNames.contains(packageName)
+ ? CheckedPermRecord.STATUS_GRANTED : CheckedPermRecord.STATUS_FIRST_CHECK));
}
// delete all uninstalled package in the databases.
for (int uid : storedPackages) {
@@ -119,6 +124,15 @@ public class DatabaseManager {
}
}
+ //20200421 add skip check data permission app list
+ private List<String> getSystemGrantApp(){
+ String[] pkgs = {"com.android.nfc", "com.android.bluetooth", "com.adups.fota",
+ "com.android.settings;com.qti.factory;com.android.dynsystem",
+ "com.android.phone;com.android.mms.service;com.mediatek.mms.appservice",
+ "com.android.networkstack","com.android.browser"};
+ return java.util.Arrays.asList(pkgs);
+ }
+
public void setDataIntoCache() {
mPkgPermissionsCache = dataBaseHelper.initNetworkDataPermCache();
Log.d(TAG, "setDataIntoCache" + "cachesize: "
@@ -141,7 +155,9 @@ public class DatabaseManager {
if (premRecord == null) {
dataBaseHelper
.addNetworkDataPerm(new CheckedPermRecord(packageName,
- uid, CheckedPermRecord.STATUS_FIRST_CHECK));
+ uid, //CheckedPermRecord.STATUS_FIRST_CHECK));
+ CheckedPermRecord.STATUS_GRANTED));
+ //20200421 change STATUS_FIRST_CHECK to granted for install app data permission
isAdded = true;
} else if (!premRecord.mPackageName.contains(packageName)) {
premRecord.mergePkgName(packageName);
监听到 app 安装成功后,默认授予数据访问权限
vendor/mediatek/proprietary/packages/apps/NetworkDataController/NetworkDataControllerService/src/com/mediatek/security/service/NetworkDataControllerService.java
@@ -282,7 +282,9 @@ public class NetworkDataControllerService extends AsyncService {
+ packageName);
if (mDatabaseManager.add(uid, packageName)) {
PermControlUtils.setFirewallPolicy(this, uid, -1,
- CheckedPermRecord.STATUS_FIRST_CHECK);
+ CheckedPermRecord.STATUS_GRANTED);
+ //CheckedPermRecord.STATUS_FIRST_CHECK);
+ //20200421 change STATUS_FIRST_CHECK to granted for install app data permission
PermControlUtils.updateCtaAppStatus(uid, true);
}
return true;