前言
最近和 PackageManagerService(PMS) 杠上了,3W 多行代码,着实精妙。网上有很多分析流程的文章,这里我就不再复述了,就来看下 PMS 的衍生修改实战吧。之前写过一篇 Android8.1 默认给第三方 app 授予所有权限,其实也能给系统 app 授权,原理大概为收到开机广播后去检查指定包名权限是否已经赋予,未授权则授权。但开机广播有时效性,有些场景不能满足要求,况且总需要去修改源码,不方便。
修改
大致思路如下,参考 MTK 的系统 app 可卸载配置方案,在
vendor\mediatek\proprietary\frameworks\base\data\etc\pms_sysapp_removable_system_list.txt
中增加可卸载系统 app 包名,在编译时拷贝至 out system/etc/permissions 路径下, 在 PmsExtImpl.java 中读取该文件,PMS 在扫描时判断包名是否在 pms_sysapp_removable_system_list 列表中,确定是否可卸载。
一、新建 pms_sysapp_grant_permission_list.txt
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_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 授权
frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java
@Override public void systemReady() { 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
frameworks\base\services\core\java\com\android\server\pm\MTKPackageManagerUtil.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 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, 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()); } } } }