Android8.1 默认给第三方 app 授予所有权限

前言

说下大体修改思路,app 安装成功、卸载、升级分别对应 Intent.ACTION_PACKAGE_ADDED、Intent.ACTION_PACKAGE_REMOVED、Intent.ACTION_PACKAGE_REPLACED 广播

这样可以在收到安装成功的广播时给 app 授权,在 8.1 中收不到静态注册的广播,所以需要动态注册监听 ACTION_PACKAGE_ADDED。之前看过 PackageInstaller 的源码,通过

app 的包名可获取到需要授权的权限清单列表并进行授权。

安装成功权限列表

enbPAO.png

安装时授权的log

enHon0.png

代码

源码位置 vendor\mediatek\proprietary\packages\apps\PackageInstaller

1、新增 PackageChangedService.java 服务,在服务中动态注册 app 状态改变的广播


package com.android.packageinstaller;


import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.util.Log;
import android.net.Uri;

public class PackageChangedService extends Service {

    private final String TAG = "permission";

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "onCreate OK");
    }

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e(TAG, "onStartCommand OK");
        packageChangedBroadcastReceiver = new PackageChangedBroadcastReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
        intentFilter.addDataScheme("package");
        registerReceiver(packageChangedBroadcastReceiver, intentFilter);

        return super.onStartCommand(intent, flags, startId);
    }


    @Override
    public void onDestroy() {
        try{
            unregisterReceiver(packageChangedBroadcastReceiver);
        }catch(Exception e){
            e.printStackTrace();
        }
        super.onDestroy();
    }



    private PackageChangedBroadcastReceiver packageChangedBroadcastReceiver;

    private class PackageChangedBroadcastReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            try{
                String action = intent.getAction();
                String packageName = intent.getData().getSchemeSpecificPart();
                Log.e(TAG, "PackageChangedBroadcastReceiver action==" + action);
                Log.i(TAG, "PackageChangedBroadcastReceiver packageName==" + packageName);

                if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
                    //给 app 授权
                    PermissionGrantHelper.slientGrantRuntimePermission(context, packageName);

                } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {

                } else if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
                    //收到 app 替换成功的广播,将广播转发给用户,通过增加 0x01000000,可以通过静态注册接收
                    Intent ccIntent = new Intent();
                    ccIntent.setAction("android.intent.action.MY_PACKAGE_REPLACED");
                    ccIntent.setData(Uri.parse("package:" + packageName));
                    ccIntent.addFlags(0x01000000);
                    context.sendBroadcast(ccIntent);
                }
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }
}

上面的代码主要干了两件事,

一、收到 app 安装成功的广播,获取 app 的包名,传递 app 包名进行权限清单查询并判断未授权则自动授权

二、解决 app 静态注册无法收到 升级成功替换的广播,此处收到动态注册的 PACKAGE_REPLACED 广播,通过增加 addFlags(0x01000000) 属性,以 MY_PACKAGE_REPLACED 的方式转发出去

2、新增 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, null, false,
                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);
            }
        }
    }
    
}

3、在配置文件中注册 PackageChangedService,并收到开机广播后启动 PackageChangedService

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.android.packageinstaller" coreApp="true"
          android:sharedUserId="android.uid.system">
    
        ....

        <application android:label="@string/app_name"
            android:allowBackup="false"
            android:theme="@style/DialogWhenLarge"
            android:supportsRtl="true"
            android:defaultToDeviceProtectedStorage="true"
            android:directBootAware="true">

            <receiver android:name=".TemporaryFileManager"
                android:exported="true">
                <intent-filter>
                    <action android:name="android.intent.action.BOOT_COMPLETED" />
                </intent-filter>
            </receiver>
    
            <service android:name=".PackageChangedService" android:exported="false"/>

            .....
        </application>

</manifest>

注意上面配置文件中的 android:sharedUserId="android.uid.system",源码默认未添加此属性,8.1 中普通 app 的后台开启服务会报错,

java.lang.IllegalStateException: Not allowed to start service Intent { flg=0x10000000 cmp=XXXX }: app is in background uid UidRecord{9327d82 u0a489 RCVR bg:+5m35s828ms idle change:uncached

查阅网上说的大都是将 startService(intent) 替换为 startForegroundService(intent),还需要在 service 的 onStartCommand 中,调用 startForeground(1, new Notification()) 来保活

既然我们是安卓源码直接添加 uid 属性完事。

再在 TemporaryFileManager 中收到开机广播后添加 context.startService(new Intent(context, PackageChangedService.class));

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容

  • 1、动态注册过程源码分析: 在Activity中动态注册广播室,在注册方法之前其实省略了Context,也就是实际...
    骑着猪的蜗牛阅读 717评论 0 1
  • 这是四大组件的第一篇(其他还没整理好:) ),之前有个习惯,就是把一些笔记记在书上,但是随着书越来越多,翻阅的时候...
    画个圈圈当地盘阅读 1,939评论 0 0
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,383评论 0 17
  • Intent.ACTION_AIRPLANE_MODE_CHANGED; //关闭或打开飞行模式时的广播 Inte...
    Ling912阅读 916评论 0 1
  • (一) 孜孜不倦三年,只为寻梦六月。 挥汗如雨两日,尘埃落定今天。 鲜花喜迎娇女,从此高考靠边。 (二) 年少纯真...
    碾河闲人阅读 135评论 0 0