前言
说下大体修改思路,app 安装成功、卸载、升级分别对应 Intent.ACTION_PACKAGE_ADDED、Intent.ACTION_PACKAGE_REMOVED、Intent.ACTION_PACKAGE_REPLACED 广播
这样可以在收到安装成功的广播时给 app 授权,在 8.1 中收不到静态注册的广播,所以需要动态注册监听 ACTION_PACKAGE_ADDED。之前看过 PackageInstaller 的源码,通过
app 的包名可获取到需要授权的权限清单列表并进行授权。
安装成功权限列表
安装时授权的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));
网友评论