美文网首页
Android8.1 默认给第三方 app 授予所有权限

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

作者: cczhengv | 来源:发表于2019-07-29 18:42 被阅读0次

    前言

    说下大体修改思路,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));

    相关文章

      网友评论

          本文标题:Android8.1 默认给第三方 app 授予所有权限

          本文链接:https://www.haomeiwen.com/subject/steirctx.html