前言
說(shuō)下大體修改思路盯腌,app 安裝成功、卸載、升級(jí)分別對(duì)應(yīng) Intent.ACTION_PACKAGE_ADDED蠢挡、Intent.ACTION_PACKAGE_REMOVED儒鹿、Intent.ACTION_PACKAGE_REPLACED 廣播
這樣可以在收到安裝成功的廣播時(shí)給 app 授權(quán)化撕,在 8.1 中收不到靜態(tài)注冊(cè)的廣播,所以需要?jiǎng)討B(tài)注冊(cè)監(jiān)聽 ACTION_PACKAGE_ADDED约炎。之前看過(guò) PackageInstaller 的源碼植阴,通過(guò)
app 的包名可獲取到需要授權(quán)的權(quán)限清單列表并進(jìn)行授權(quán)。
安裝成功權(quán)限列表
安裝時(shí)授權(quán)的log
代碼
源碼位置 vendor\mediatek\proprietary\packages\apps\PackageInstaller
1圾浅、新增 PackageChangedService.java 服務(wù)掠手,在服務(wù)中動(dòng)態(tài)注冊(cè) app 狀態(tài)改變的廣播
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 授權(quán)
PermissionGrantHelper.slientGrantRuntimePermission(context, packageName);
} else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
} else if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
//收到 app 替換成功的廣播,將廣播轉(zhuǎn)發(fā)給用戶狸捕,通過(guò)增加 0x01000000喷鸽,可以通過(guò)靜態(tài)注冊(cè)接收
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 包名進(jìn)行權(quán)限清單查詢并判斷未授權(quán)則自動(dòng)授權(quán)
二鸡岗、解決 app 靜態(tài)注冊(cè)無(wú)法收到 升級(jí)成功替換的廣播混槐,此處收到動(dòng)態(tài)注冊(cè)的 PACKAGE_REPLACED 廣播,通過(guò)增加 addFlags(0x01000000) 屬性轩性,以 MY_PACKAGE_REPLACED 的方式轉(zhuǎn)發(fā)出去
2声登、新增 PermissionGrantHelper.java 授權(quán)工具類,通過(guò)包名查詢權(quán)限清單并授權(quán)
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、在配置文件中注冊(cè) PackageChangedService悯嗓,并收到開機(jī)廣播后啟動(dòng) 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"件舵,源碼默認(rèn)未添加此屬性,8.1 中普通 app 的后臺(tái)開啟服務(wù)會(huì)報(bào)錯(cuò)脯厨,
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
查閱網(wǎng)上說(shuō)的大都是將 startService(intent) 替換為 startForegroundService(intent)芦圾,還需要在 service 的 onStartCommand 中,調(diào)用 startForeground(1, new Notification()) 來(lái)倍砣希活
既然我們是安卓源碼直接添加 uid 屬性完事个少。
再在 TemporaryFileManager 中收到開機(jī)廣播后添加 context.startService(new Intent(context, PackageChangedService.class));