[ Android源码分析 ] 静态注册在源码中是如何实现的
尊重原创,转载请注明出处!
前言
前段时间研究了广播的发送流程,发送广播时需要先获取接收该广播的静态注册和动态注册的 Receiver,再将广播加入到 BroadcastQueue 中进行处理。
当时主要是为了解决客户的一个问题,比较紧急,所以看源码时也是为了理清广播发送的主要流程,对很多细节实现都没有太过纠结。
现在空闲下来了,还是得抽空研究下静态注册的实现流程。
应用实现方式
对于静态注册的实现方式,相信所有的 Android 开发者都非常清楚了,流程上应付一下,放一个最简单的接收开机广播的代码。当然,权限啥的就不贴了,简单看看就好。
<?xml version="1.0" encoding="utf-8"?>
<manifest>
......
<application>
......
<receiver android:name=".BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
</application>
</manifest>
发送广播给静态注册接收器
很容易想到,在发送广播之前、或者在发送广播的时候,必须要获取到静态注册了该广播的 Receiver,这样才能确保广播能正确地发送给对应的 Receiver。
在之前写的博客 [ Android实战 ] 开机时通过广播启动应用,但是很长时间才能接收到,如何解决?中,分析过 Android 发送广播的流程。应用调用 sendBroadcast 后,经过调用流程,走到 AMS 里面的 broadcastIntentLocked 方法。
/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
......
// Figure out who all will receive this broadcast.
List receivers = null;
// Need to resolve the intent to interested receivers...
if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
== 0) {
// 这里获取静态注册了该intent的receiver,放到receivers队列中
receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
}
......
return ActivityManager.BROADCAST_SUCCESS;
}
collectReceiverComponents,顾名思义,就是用来收集静态注册的 Receiver 的方法。它的实现中,最关键的代码就是调用了 PackageManagerService 中的 queryIntentReceivers 方法,查询对应的 Receiver。
private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
int callingUid, int[] users) {
// TODO: come back and remove this assumption to triage all broadcasts
int pmFlags = STOCK_PM_FLAGS | MATCH_DEBUG_TRIAGED_MISSING;
List<ResolveInfo> receivers = null;
try {
......
for (int user : users) {
......
List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
.queryIntentReceivers(intent, resolvedType, pmFlags, user).getList();
......
}
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
}
return receivers;
}
queryIntentReceivers 的实现其实就是调用了 queryIntentReceiversInternal,并对结果进行封装。
而 queryIntentReceiversInternal 中的核心代码则是调用了 mReceivers.queryIntent,查询 intent 对应的 receiver 列表。
/frameworks/base/service/core/java/com/android/server/pm/PackageManagerService.java
@Override
public @NonNull ParceledListSlice<ResolveInfo> queryIntentReceivers(Intent intent,
String resolvedType, int flags, int userId) {
return new ParceledListSlice<>(
queryIntentReceiversInternal(intent, resolvedType, flags, userId,
false /*allowDynamicSplits*/));
}
private @NonNull List<ResolveInfo> queryIntentReceiversInternal(Intent intent,
String resolvedType, int flags, int userId, boolean allowDynamicSplits) {
......
// reader
synchronized (mPackages) {
String pkgName = intent.getPackage();
if (pkgName == null) {
// 重点是这行代码,从mReceivers中查询对应的Receiver列表
final List<ResolveInfo> result =
mReceivers.queryIntent(intent, resolvedType, flags, userId);
// 过滤结果,移除一些instant app以及安装失败的应用,暂时不管
return applyPostResolutionFilter(
result, instantAppPkgName, allowDynamicSplits, callingUid, userId);
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
// 重点是这行代码,从mReceivers中查询对应的Receiver列表
final List<ResolveInfo> result = mReceivers.queryIntentForPackage(
intent, resolvedType, flags, pkg.receivers, userId);
return applyPostResolutionFilter(
result, instantAppPkgName, allowDynamicSplits, callingUid, userId);
}
return Collections.emptyList();
}
}
mReceiver 的定义,很清楚地说明了它就是 PKMS 中用来存放所有解析出来的静态注册的 Receiver 的一个容器。
// All available receivers, for your resolving pleasure.
final ActivityIntentResolver mReceivers = new ActivityIntentResolver();
看到这里,第一阶段的流程已经很清楚了:发送广播的时候,会从 PKMS 的 mReceivers 中查询得到 intent 对应静态注册的 receiver 列表,再逐一进行发送。
那么,我们接下来的问题就变成了,mReceivers 这个对象是如何维护的,静态注册的 Receiver 是在什么时候解析并插入到 mReceivers 中的?
解析静态注册接收器
众所周知,Android 启动后创建了第一个进程 init,而 init 解析 init.rc 脚本启动了 zygote,其他所有应用进程都是由它 fork 出来的,其中就包括 system_server。
在 SystemServer 中,启动了大量 Android 运行过程中所需的服务,其中就包括我们很熟悉的 PackageManagerService。
/frameworks/base/service/java/com/android/server/SystemServer.java
private void startBootstrapServices() {
......
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
......
}
PackageManagerService.main 就是通过构造函数 new 出了一个 PackageManagerService 的实例,并调用 ServiceManager.addService 方法,注册了 package 的系统服务。
/frameworks/base/service/core/java/com/android/server/pm/PackageManagerService.java
public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// Self-check for initial settings.
PackageManagerServiceCompilerMapping.checkProperties();
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
m.enableSystemUserPackages();
ServiceManager.addService("package", m);
final PackageManagerNative pmn = m.new PackageManagerNative();
ServiceManager.addService("package_native", pmn);
return m;
}
在 PKMS 的构造函数中,忽略其他的一些流程,主要调用了 scanDirTracedLI,对我们熟知的 /system/app、/vendor/app、/data/app 等路径下的应用进行扫描。截取部分代码如下:
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
......
// Collect vendor overlay packages. (Do this before scanning any apps.)
// For security and version matching reason, only consider
// overlay packages if they reside in the right directory.
scanDirTracedLI(new File(VENDOR_OVERLAY_DIR), mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_TRUSTED_OVERLAY, scanFlags | SCAN_TRUSTED_OVERLAY, 0);
mParallelPackageParserCallback.findStaticOverlayPackages();
// Find base frameworks (resource packages without code).
scanDirTracedLI(frameworkDir, mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED,
scanFlags | SCAN_NO_DEX, 0);
// Collected privileged system packages.
final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
scanDirTracedLI(privilegedAppDir, mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);
// Collect ordinary system packages.
final File systemAppDir = new File(Environment.getRootDirectory(), "app");
scanDirTracedLI(systemAppDir, mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
// Collect all vendor packages.
File vendorAppDir = new File("/vendor/app");
try {
vendorAppDir = vendorAppDir.getCanonicalFile();
} catch (IOException e) {
// failed to look up canonical path, continue with original one
}
scanDirTracedLI(vendorAppDir, mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
// Collect all OEM packages.
final File oemAppDir = new File(Environment.getOemDirectory(), "app");
scanDirTracedLI(oemAppDir, mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
......
if (!mOnlyCore) {
scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
scanDirTracedLI(mDrmAppPrivateInstallDir, mDefParseFlags
| PackageParser.PARSE_FORWARD_LOCK,
scanFlags | SCAN_REQUIRE_KNOWN, 0);
......
}
......
}
scanDirTracedLI 也只是对 scanDirLI 的一个简单封装调用,并调用 trace 记录相关信息。
private void scanDirTracedLI(File dir, final int parseFlags, int scanFlags, long currentTime) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + dir.getAbsolutePath() + "]");
try {
scanDirLI(dir, parseFlags, scanFlags, currentTime);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
scanDirLI 解析指定目录中的所有应用,调用 scanPackageLI 遍历解析后的每一个 package,进行进一步的处理。
private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
final File[] files = dir.listFiles();
// 并行解析目录下的所有应用
......
// 遍历处理解析后的结果
for (; fileCount > 0; fileCount--) {
......
if (throwable == null) {
......
try {
if (errorCode == PackageManager.INSTALL_SUCCEEDED) {
scanPackageLI(parseResult.pkg, parseResult.scanFile, parseFlags, scanFlags,
currentTime, null);
}
} catch (PackageManagerException e) {
errorCode = e.error;
Slog.w(TAG, "Failed to scan " + parseResult.scanFile + ": " + e.getMessage());
}
}
......
}
......
}
scanPackageLI 继续往下调,到 scanPackageInternalLI 中进一步处理。
PS:这里有一个有意思的词:childPackages。之前没有接触过这个概念,在网上真正能搜到对它介绍的博客或文章也几乎没有,立个 flag,后续有时间的话去研究一下,写篇博客做个介绍。
/**
* Scans a package and returns the newly parsed package.
* @throws PackageManagerException on a parse error.
*/
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, File scanFile,
final int policyFlags, int scanFlags, long currentTime, @Nullable UserHandle user)
throws PackageManagerException {
......
// Scan the parent
PackageParser.Package scannedPkg = scanPackageInternalLI(pkg, scanFile, policyFlags,
scanFlags, currentTime, user);
// Scan the children
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageParser.Package childPackage = pkg.childPackages.get(i);
scanPackageInternalLI(childPackage, scanFile, policyFlags, scanFlags,
currentTime, user);
}
......
return scannedPkg;
}
Fine。。。 scanPackageInternalLI 中又调回 scanPackageLI 了,不过是另一个重载的函数。
/**
* Scans a package and returns the newly parsed package.
* @throws PackageManagerException on a parse error.
*/
private PackageParser.Package scanPackageInternalLI(PackageParser.Package pkg, File scanFile,
int policyFlags, int scanFlags, long currentTime, @Nullable UserHandle user)
throws PackageManagerException {
......
// Note that we invoke the following method only if we are about to unpack an application
PackageParser.Package scannedPkg = scanPackageLI(pkg, policyFlags, scanFlags
| SCAN_UPDATE_SIGNATURE, currentTime, user);
......
return scannedPkg;
}
scanPackageLI 又是一个简单的封装,继续往下调,到 scanPackageDirtyLI 中进一步处理。
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, final int policyFlags,
int scanFlags, long currentTime, @Nullable UserHandle user)
throws PackageManagerException {
boolean success = false;
try {
final PackageParser.Package res = scanPackageDirtyLI(pkg, policyFlags, scanFlags,
currentTime, user);
success = true;
return res;
} finally {
if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
// DELETE_DATA_ON_FAILURES is only used by frozen paths
destroyAppDataLIF(pkg, UserHandle.USER_ALL,
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
destroyAppProfilesLIF(pkg, UserHandle.USER_ALL);
}
}
}
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg,
final int policyFlags, final int scanFlags, long currentTime, @Nullable UserHandle user)
throws PackageManagerException {
......
if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
......
} else {
final int userId = user == null ? 0 : user.getIdentifier();
// Modify state for the given package setting
commitPackageSettings(pkg, pkgSetting, user, scanFlags,
(policyFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/);
......
}
......
return pkg;
}
commitPackageSettings 的方法说明已经说得很清楚了,将一个扫描过的 package 加载到系统。执行完本函数后,这个应用可以被查询到。
在这个函数中,主要将解析得到的 provider,service,receiver,activity 等信息保存起来,便于后续使用过程中进行查询。
/**
* Adds a scanned package to the system. When this method is finished, the package will
* be available for query, resolution, etc...
*/
private void commitPackageSettings(PackageParser.Package pkg, PackageSetting pkgSetting,
UserHandle user, int scanFlags, boolean chatty) throws PackageManagerException {
final String pkgName = pkg.packageName;
......
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
synchronized (mPackages) {
......
// 将签名信息保存KeySetManagerService,将解析出来的ContentProvider保存到mProviders,Service保存到mServices
......
// 遍历解析得到的Receiver进行处理
N = pkg.receivers.size();
r = null;
for (i=0; i<N; i++) {
PackageParser.Activity a = pkg.receivers.get(i);
a.info.processName = fixProcessName(pkg.applicationInfo.processName,
a.info.processName);
// 核心代码,将解析出来的receiver都添加到mReceivers中
mReceivers.addActivity(a, "receiver");
......
}
// 将解析出来的Activity保存到mActivities,instrumentation保存到mInstrumentation,以及PermissionGroup、Permission的变更等
......
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
当然,除了开机后扫描不同目录下的应用,还需要考虑新安装的应用。但是原理是一样的,虽然是走的 install 的流程,但最终还是会经过调用流程走到 commitPackageSettings,再将解析出来的 receiver 添加保存到 mReceivers 中。
至此,mRecivers 是如何得到的就很清晰明了了。
总结
最后简单做下总结:
Android 在 启动过程中,会扫描 /vendor/app,/system/app,/data/app 等目录下的应用,并进行解析,得到 Activity,Service,Provider,Receiver,Instrumentation 等信息,并保存起来。
应用安装、升级过程中,同样会进行解析,并将解析得到的信息也保存到系统中。
发送广播时,会从之前保存起来的 mReceivers 中,查询广播对应的静态注册的 Receiver,再通过 binder 调用启动应用进程,并在应用启动完成后,调用 BroadcastReceiver.onReceive 进行接收。
所以系统中只是保留了静态注册的 Receiver 信息,真正启动应用并接收广播是在广播发出后,通过查询信息得到对应的 Receiver,再进行相关操作的。
Android 系统的源码太过庞大,分析源码的过程中,经常会遇到跳来跳去,这里调那里的情况。如果在看源码的过程中太过注重细节,每个环节都想搞懂,往往会浪费自己大量的时间,并且跳出去后就很难回到主流程上。时间长了,慢慢地也放弃了。
所以一定要明确自己此次分析源码的目的是什么,先沿着自己关注的问题看下去。在此过程中,很多方法的调用都可以通过方法说明或博客先大概了解就行。先大概理清楚整个调用的主流程,再回过头来,看具体的细节,这样效率反而更高。