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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章