Android9.0/8.1/6.0 默认给系统 app 授予所有权限

469 阅读4分钟

前言

最近和 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() {//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

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());
            }
        }
    }
}