前言
最近和 PackageManagerService(PMS) 杠上了,3W 多行代码,着实精妙。网上有很多分析流程的文章,这里我就不再复述了,就来看下 PMS 的衍生修改实战吧。之前写过一篇 Android8.1 默认给第三方 app 授予所有权限,其实也能给系统 app 授权,原理大概为收到开机广播后去检查指定包名权限是否已经赋予,未授权则授权。但开机广播有时效性,有些场景不能满足要求,况且总需要去修改源码,不方便。
修改
大致思路如下,参考 MTK 的系统 app 可卸载配置方案,在
vendormediatekproprietaryframeworksasedataetcpms_sysapp_removable_system_list.txt
中增加可卸载系统 app 包名,在编译时拷贝至 out system/etc/permissions 路径下, 在 PmsExtImpl.java 中读取该文件,PMS 在扫描时判断包名是否在 pms_sysapp_removable_system_list 列表中,确定是否可卸载。
一、新建 pms_sysapp_grant_permission_list.txt
vendormediatekproprietaryframeworksasedataetcpms_sysapp_grant_permission_list.txt
com.android.dialer
com.android.soundrecorder
com.android.browser
com.mediatek.filemanager
com.android.calendar
com.android.email
二、拷贝 pms_sysapp_grant_permission_list.txt 至 out 目录下
device/mediatek/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
三、PMS 中系统启动完成读取 pms_sysapp_grant_permission_list.txt,给 app 授权
frameworksaseservicescorejavacomandroidserverpmPackageManagerService.java
@Override
public void systemReady() {//pjz
enforceSystemOrRoot("Only the system can claim the system is ready");
//cczheng add for system app grant permission S
if (mFirstBoot) {
MTKPackageManagerUtil.slientGrantRuntimePermission(mContext);
}
//cczheng add for system app grant permission E
mSystemReady = true;
final ContentResolver resolver = mContext.getContentResolver();
ContentObserver co = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
mEphemeralAppsDisabled =
(Global.getInt(resolver, Global.ENABLE_EPHEMERAL_FEATURE, 1) == 0) ||
(Secure.getInt(resolver, Secure.INSTANT_APPS_ENABLED, 1) == 0);
}
};
mContext.getContentResolver().registerContentObserver(android.provider.Settings.Global
.getUriFor(Global.ENABLE_EPHEMERAL_FEATURE),
false, co, UserHandle.USER_SYSTEM);
mContext.getContentResolver().registerContentObserver(android.provider.Settings.Global
.getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_SYSTEM);
co.onChange(true);
新增 MTKPackageManagerUtil 类
1、读取 pms_sysapp_grant_permission_list.txt 中包名
2、遍历包名集合获取需要申请的权限集合
3、根据权限名称判断当前包名是否已经授权,未授权则授权
此处有几个坑说一下,
坑一、根据包名获取到的权限集合中有很多不在BasePermission中, 系统不能正常授权,会抛异常,导致后续的权限不能正常授权,需要剔除这部分。
2019-01-01 08:23:51.605 656-656/system_process W/System.err: at com.android.server.pm.PackageManagerService.grantRuntimePermission(PackageManagerService.java:5732)
2019-01-01 08:23:51.605 656-656/system_process W/System.err: at com.android.server.pm.PackageManagerService.grantRuntimePermission(PackageManagerService.java:5703)
2019-01-01 08:23:51.605 656-656/system_process W/System.err: at android.app.ApplicationPackageManager.grantRuntimePermission(ApplicationPackageManager.java:627)
2019-01-01 08:23:51.605 656-656/system_process W/System.err: at com.android.server.pm.MTKPackageManagerUtil.slientGrantRuntimePermission(MTKPackageManagerUtil.java:90)
2019-01-01 08:23:51.605 656-656/system_process W/System.err: at com.android.server.pm.PackageManagerService.systemReady(PackageManagerService.java:22551)
2019-01-01 08:23:51.605 656-656/system_process D/MTKPackageManagerUtil: Unknown permission: com.qualcomm.permission.USE_PHONE_SERVICE
坑二、用户自己声明的权限和非运行时权限,需要剔除这部分。
2019-01-01 08:43:51.605 656-656/system_process W/System.err: at com.android.server.pm.PackageManagerService.enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(PackageManagerService.java:5696)
2019-01-01 08:43:51.605 656-656/system_process W/System.err: at com.android.server.pm.PackageManagerService.grantRuntimePermission(PackageManagerService.java:5740)
2019-01-01 08:43:51.605 656-656/system_process W/System.err: at com.android.server.pm.PackageManagerService.grantRuntimePermission(PackageManagerService.java:5703)
2019-01-01 08:43:51.605 656-656/system_process W/System.err: at android.app.ApplicationPackageManager.grantRuntimePermission(ApplicationPackageManager.java:627)
2019-01-01 08:43:51.605 656-656/system_process W/System.err: at com.android.server.pm.MTKPackageManagerUtil.slientGrantRuntimePermission(MTKPackageManagerUtil.java:90)
2019-01-01 08:43:51.605 656-656/system_process W/System.err: at com.android.server.pm.PackageManagerService.systemReady(PackageManagerService.java:22551)
2019-01-01 08:43:51.605 656-656/system_process D/MTKPackageManagerUtil: Permission android.permission.ACCESS_BLUETOOTH_SHARE is not a changeable permission type
frameworksaseservicescorejavacomandroidserverpmMTKPackageManagerUtil.java
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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 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 = "KEPackageManagerUtil";
//copy from vendormediatekproprietaryframeworksaseservicescorejavacommediatekserverpmPmsExtImpl.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, Settings mSettings){
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);
if (status != PackageManager.PERMISSION_GRANTED && bp != null) {
if (!bp.isRuntime() && !bp.isDevelopment()) {
Log.d(TAG, "Permission " + bp.name + " is not a changeable permission type");
continue;
}
sGrantPermissionSet.add(permission);
}
}
Log.d(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());
}
}
}
}