实现BrocastReceiver的插件之前,先来了解一下应用程序是如何被解析AndroidManifest.xml中各种标签的。
系统启动过程中,会扫描某些目录的程序,安装加载过程,为应用程序分配Linux的用户Id和Linux的用户组Id,也会解析程序。
先来看下
scanPackageLI():
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
// 关键点 包解析器
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {
parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;
}
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final PackageParser.Package pkg;
try {
//解析 程序的信息,存储在Package对象中。
pkg = pp.parsePackage(scanFile, parseFlags);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);
}
这是一个关键点,根据指定路径下的apk,解析出里面信息,存储在Package对象中。
根据这个思路,就可以获取到插件中一些标签,例如 Activity、Service、广播等。
接下来查看,PackageParser是如何解析apk的。
PackageParser类
parsePackage():
public Package parsePackage(File packageFile, int flags) throws PackageParserException {
if (packageFile.isDirectory()) {
return parseClusterPackage(packageFile, flags);
} else {
return parseMonolithicPackage(packageFile, flags);
}
}
parseMonolithicPackage():
public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
if (mOnlyCoreApps) {
if (!lite.coreApp) {
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Not a coreApp: " + apkFile);
}
}
final AssetManager assets = new AssetManager();
try {
final Package pkg = parseBaseApk(apkFile, assets, flags);
pkg.setCodePath(apkFile.getAbsolutePath());
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
} finally {
IoUtils.closeQuietly(assets);
}
}
parseBaseApk():
private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
String[] outError) throws XmlPullParserException, IOException {
final String splitName;
final String pkgName;
try {
Pair<String, String> packageSplit = parsePackageSplitNames(parser, parser);
pkgName = packageSplit.first;
splitName = packageSplit.second;
if (!TextUtils.isEmpty(splitName)) {
outError[0] = "Expected base APK, but found split " + splitName;
mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
return null;
}
} catch (PackageParserException e) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
return null;
}
final Package pkg = new Package(pkgName);
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifest);
pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
pkg.baseRevisionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);
pkg.mVersionName = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_versionName, 0);
if (pkg.mVersionName != null) {
pkg.mVersionName = pkg.mVersionName.intern();
}
pkg.coreApp = parser.getAttributeBooleanValue(null, "coreApp", false);
sa.recycle();
return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
}
parseBaseApkCommon():
private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
IOException {
mParseInstrumentationArgs = null;
mParseActivityArgs = null;
mParseServiceArgs = null;
mParseProviderArgs = null;
// ...省略部分源码
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (tagName.equals(TAG_APPLICATION)) {
if (foundApp) {
if (RIGID_PARSER) {
outError[0] = " has more than one " ;
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
} else {
Slog.w(TAG, " has more than one " );
XmlUtils.skipCurrentTag(parser);
continue;
}
}
foundApp = true;
//解析Application标签中的四大组件和其他标签
if (!parseBaseApplication(pkg, res, parser, flags, outError)) {
return null;
}
}
}
}
parseBaseApplication():
private boolean parseBaseApplication(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError)
throws XmlPullParserException, IOException {
final ApplicationInfo ai = owner.applicationInfo;
final String pkgName = owner.applicationInfo.packageName;
// ...省略部分源码
final int innerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
String tagName = parser.getName();
if (tagName.equals("activity")) { // Activity标签
Activity a = parseActivity(owner, res, parser, flags, outError, false,
owner.baseHardwareAccelerated);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.activities.add(a);
} else if (tagName.equals("receiver")) {
// 注意点:将Broadcast当成Activity来处理的。
Activity a = parseActivity(owner, res, parser, flags, outError, true, false);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.receivers.add(a);
} else if (tagName.equals("service")) { // 解析Service标签
Service s = parseService(owner, res, parser, flags, outError);
if (s == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.services.add(s);
} else if (tagName.equals("provider")) {
Provider p = parseProvider(owner, res, parser, flags, outError);
if (p == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.providers.add(p);
}
//... 省略部分源码
}
return true;
}
从上可知:解析好的广播会当Activity一样处理,存储在Package对象中的receivers集合中。
PackageParser类中的静态内部类Package
/**
* Representation of a full package parsed from APK files on disk. A package
* consists of a single base APK, and zero or more split APKs.
*/
public final static class Package {
public final ArrayList<Permission> permissions = new ArrayList<Permission>(0);
public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0);
public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
public final ArrayList<Provider> providers = new ArrayList<Provider>(0);
public final ArrayList<Service> services = new ArrayList<Service>(0);
}
只需要拿到插件的Package对象,就可以拿到插件中静态注册的广播信息。
思路:
插件中静态广播处理方式:
插件中动态广播处理方式:
根据思路分析,开始编码实现。
在插件中编写一个广播类
public class PluginReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context.getApplicationContext()," 插件的广播收到 订阅信息",Toast.LENGTH_SHORT).show();
}
}
在AndroidManifest.xml中注册:
<receiver android:name="com.xingen.plugin.receiver.PluginReceiver">
<intent-filter>
<action android:name="com.xingen.plugin.receiver.PluginReceiver"></action>
</intent-filter>
</receiver>
在宿主中开始解析插件apk获取到对应的广播信息。
/**
* 解析插件中的广播
* @param apkFilePath
*/
private static void preloadParseReceiver(String apkFilePath) {
try {
// 先获取PackageParser对象
Class<?> packageParserClass = Class.forName("android.content.pm.PackageParser");
Object packageParser = packageParserClass.newInstance();
//接着获取PackageParser.Package
Method parsePackageMethod = packageParserClass.getDeclaredMethod("parsePackage", File.class, int.class);
parsePackageMethod.setAccessible(true);
Object packageParser$package = parsePackageMethod.invoke(packageParser, new File(apkFilePath), PackageManager.GET_RECEIVERS);
// 接着获取到Package中的receivers列表
Class<?> packageParser$package_Class = packageParser$package.getClass();
Field receiversField= packageParser$package_Class.getDeclaredField("receivers");
receiversField.setAccessible(true);
List receiversList= (List) receiversField.get(packageParser$package);
Class<?> packageParser$Activity_Class =Class.forName("android.content.pm.PackageParser$Activity");
// intent-filter过滤器
Field intentsFiled=packageParser$Activity_Class.getField("intents");
intentsFiled.setAccessible(true);
// 获取 name
Field infoField=packageParser$Activity_Class.getDeclaredField("info");
infoField.setAccessible(true);
for (Object receiver:receiversList){
ActivityInfo info=(ActivityInfo) infoField.get(receiver);
List<? extends IntentFilter> intentFiltersList= (List<? extends IntentFilter>) intentsFiled.get(receiver);
receivers.put(info,intentFiltersList);
}
} catch (Exception e) {
e.printStackTrace();
}
}
获取到插件信息后,开始动态注册:
/**
* 注册插件中的广播
* @param context
*/
private static void registerPluginReceiver(Context context){
try {
ClassLoader classLoader= ReceiverHookManager.class.getClassLoader();
for ( ActivityInfo activityInfo:receivers.keySet()){
List<? extends IntentFilter> intentFilters=receivers.get(activityInfo);
BroadcastReceiver broadcastReceiver=(BroadcastReceiver) classLoader.loadClass(activityInfo.name).newInstance();
if (intentFilters!=null&&broadcastReceiver!=null){
for ( IntentFilter intentFilter:intentFilters){
context.getApplicationContext().registerReceiver(broadcastReceiver,intentFilter);
}
}
}
}catch (Exception e){
e.printStackTrace();
}
}
测试验证:
private void sendActionBroadcast() {
Intent intent = new Intent();
intent.setAction(PluginConfig.receiver_action);
sendBroadcast(intent);
}
项目地址:https://github.com/13767004362/HookDemo