Android 中的自启行为浅析 # Broadcast(一)
在 Android 越来越普及的当下,Android 手机在用户心里最尖锐的问题总结起来就是两个关键词:卡慢、费电,恰好本文系列讲述跟这两项都有关系,Android 的普通应用程序的自启动行为直接导致了内存紧张、CPU占用以至于手机 UI 卡慢、耗电快。这里我们说的 “自启动” 这个字眼实际上指的是:用户未有主动启动目标应用程序,目标应用程序进程却被启动的行为,说是自启动,其实是有其他进程把应用程序给带起来了。
本文将从 Broadcast 的角度来分析第三方应用是如何通过广播来自启的。
1. 广播的发送与接收
1.1 从广播发送者到 AMS
<center>
图 1.1 广播的接收与发送 (Android 4.4)</center>
如图 1.1 所示为在 Android 4.4 中广播的发送、接收流程。
步骤1. sendBroadcast
这个函数自然是 Context 的虚函数,通过ContextWrapper 从代理 ContextImpl 直接调用的,上述这个过程都司空见惯就不再累述了,如下代码(ContextImpl.java):
public void sendBroadcast(Intent intent) {
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
1. intent.prepareToLeaveProcess();
2. ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, false,
getUserId());
} catch (RemoteException e) {
}
}
步骤1:Intent.prepareToLeaveProcess 方法是 StrictMode 在 Intent 即将传输之际检查是否有 "file://" 的 Uri 暴漏出来,如果有的话就会曝出 StrictMode 的异常;
步骤2:Binder调用 AMS 的 broadcastIntent 方法;
步骤2. Binder调用过程
这一步跨进程调用在本文就不再展开了。
步骤3. broadcastIntent
跨进程调用到 AMS 的 broadcastIntent 函数中:
public final int broadcastIntent(IApplicationThread caller,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle map,
String requiredPermission, int appOp, boolean serialized, boolean sticky, int userId) {
1. enforceNotIsolatedCaller("broadcastIntent");
synchronized(this) {
2. intent = verifyBroadcastLocked(intent);
final ProcessRecord callerApp = getRecordForAppLocked(caller);
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
3. int res = broadcastIntentLocked(callerApp,
callerApp != null ? callerApp.info.packageName : null,
intent, resolvedType, resultTo,
resultCode, resultData, map, requiredPermission, appOp, serialized, sticky,
callingPid, callingUid, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
}
步骤1:隔离的进程不能发广播;
步骤2:检查是否有文件句柄泄漏,AMS 没初始化完成时也禁止发广播;
步骤3:真正的发广播;
步骤4. broadcastIntentLocked
private final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle map, String requiredPermission, int appOp,
boolean ordered, boolean sticky, int callingPid, int callingUid,
int userId) {
intent = new Intent(intent);
// By default broadcasts do not go to stopped apps.
1. intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
......
/*
* Prevent non-system code (defined here to be non-persistent
* processes) from sending protected broadcasts.
*/
int callingAppId = UserHandle.getAppId(callingUid);
2. if (callingAppId == Process.SYSTEM_UID || callingAppId == Process.PHONE_UID
|| callingAppId == Process.SHELL_UID || callingAppId == Process.BLUETOOTH_UID ||
callingUid == 0) {
// Always okay.
} else if (callerApp == null || !callerApp.persistent) {
......
}
// Handle special intents: if this broadcast is from the package
// manager about a package being removed, we need to remove all of
// its activities from the history stack.
final boolean uidRemoved = Intent.ACTION_UID_REMOVED.equals(
intent.getAction());
3. if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())
|| Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())
|| Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())
|| uidRemoved) {
......
} else if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) {
Uri data = intent.getData();
String ssp;
if (data != null && (ssp=data.getSchemeSpecificPart()) != null) {
mCompatModePackages.handlePackageAddedLocked(ssp,
intent.getBooleanExtra(Intent.EXTRA_REPLACING, false));
}
}
/*
* If this is the time zone changed action, queue up a message that will reset the timezone
* of all currently running processes. This message will get queued up before the broadcast
* happens.
*/
if (intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
mHandler.sendEmptyMessage(UPDATE_TIME_ZONE);
}
if (intent.ACTION_CLEAR_DNS_CACHE.equals(intent.getAction())) {
mHandler.sendEmptyMessage(CLEAR_DNS_CACHE_MSG);
}
if (Proxy.PROXY_CHANGE_ACTION.equals(intent.getAction())) {
ProxyProperties proxy = intent.getParcelableExtra("proxy");
mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG, proxy));
}
// Add to the sticky list if requested.
4. if (sticky) {
......
}
......
// Figure out who all will receive this broadcast.
List receivers = null;
List<BroadcastFilter> registeredReceivers = null;
// Need to resolve the intent to interested receivers...
if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
== 0) {
5. receivers = collectReceiverComponents(intent, resolvedType, users);
}
if (intent.getComponent() == null) {
6. registeredReceivers = mReceiverResolver.queryIntent(intent,
resolvedType, false, userId);
}
final boolean replacePending =
(intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
if (DEBUG_BROADCAST) Slog.v(TAG, "Enqueing broadcast: " + intent.getAction()
+ " replacePending=" + replacePending);
int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
if (!ordered && NR > 0) {
// If we are not serializing this broadcast, then send the
// registered receivers separately so they don't wait for the
// components to be launched.
final BroadcastQueue queue = broadcastQueueForIntent(intent);
7. BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType, requiredPermission,
appOp, registeredReceivers, resultTo, resultCode, resultData, map,
ordered, sticky, false, userId);
if (DEBUG_BROADCAST) Slog.v(
TAG, "Enqueueing parallel broadcast " + r);
final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
if (!replaced) {
8. queue.enqueueParallelBroadcastLocked(r);
9. queue.scheduleBroadcastsLocked();
}
registeredReceivers = null;
NR = 0;
}
// Merge into one list.
int ir = 0;
if (receivers != null) {
// A special case for PACKAGE_ADDED: do not allow the package
// being added to see this broadcast. This prevents them from
// using this as a back door to get run as soon as they are
// installed. Maybe in the future we want to have a special install
// broadcast or such for apps, but we'd like to deliberately make
// this decision.
String skipPackages[] = null;
10. if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())
|| Intent.ACTION_PACKAGE_RESTARTED.equals(intent.getAction())
|| Intent.ACTION_PACKAGE_DATA_CLEARED.equals(intent.getAction())) {
Uri data = intent.getData();
if (data != null) {
String pkgName = data.getSchemeSpecificPart();
if (pkgName != null) {
skipPackages = new String[] { pkgName };
}
}
} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(intent.getAction())) {
skipPackages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
}
if (skipPackages != null && (skipPackages.length > 0)) {
for (String skipPackage : skipPackages) {
if (skipPackage != null) {
int NT = receivers.size();
for (int it=0; it<NT; it++) {
ResolveInfo curt = (ResolveInfo)receivers.get(it);
if (curt.activityInfo.packageName.equals(skipPackage)) {
receivers.remove(it);
it--;
NT--;
}
}
}
}
}
11. int NT = receivers != null ? receivers.size() : 0;
int it = 0;
ResolveInfo curt = null;
BroadcastFilter curr = null;
while (it < NT && ir < NR) {
if (curt == null) {
curt = (ResolveInfo)receivers.get(it);
}
if (curr == null) {
curr = registeredReceivers.get(ir);
}
if (curr.getPriority() >= curt.priority) {
// Insert this broadcast record into the final list.
receivers.add(it, curr);
ir++;
curr = null;
it++;
NT++;
} else {
// Skip to the next ResolveInfo in the final list.
it++;
curt = null;
}
}
}
while (ir < NR) {
if (receivers == null) {
receivers = new ArrayList();
}
receivers.add(registeredReceivers.get(ir));
ir++;
}
if ((receivers != null && receivers.size() > 0)
|| resultTo != null) {
BroadcastQueue queue = broadcastQueueForIntent(intent);
12. BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType,
requiredPermission, appOp, receivers, resultTo, resultCode,
resultData, map, ordered, sticky, false, userId);
if (DEBUG_BROADCAST) Slog.v(
TAG, "Enqueueing ordered broadcast " + r
+ ": prev had " + queue.mOrderedBroadcasts.size());
if (DEBUG_BROADCAST) {
int seq = r.intent.getIntExtra("seq", -1);
Slog.i(TAG, "Enqueueing broadcast " + r.intent.getAction() + " seq=" + seq);
}
boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
if (!replaced) {
13. queue.enqueueOrderedBroadcastLocked(r);
14. queue.scheduleBroadcastsLocked();
}
}
return ActivityManager.BROADCAST_SUCCESS;
}
第一步:Intent.FLAG_EXCLUDE_STOPPED_PACKAGES 是确保所有广播默认都不会发给已经停止运行的应用,这个点在后面会提到;
第二步:系统进程发的广播没有顾虑,如果不是的话对某些特殊广播有一定的限制;
第三步:针对应用被卸载的广播、安装应用广播、时区变化、DNS cache 清除、代理变化等广播的特殊处理;
第四步:sticky 广播的处理,将会把该广播储存起来,关于 sticky 广播大家可以自行调研;
第五步:从这一步开始是真正的处理广播的步骤。根据广播 Intent(靠PMS)搜集匹配的静态广播接收器组件,在这一步我们滤掉了已经被停止的应用;
<span id="one_four_six">第六步:(靠AMS)搜集匹配的动态注册的组件,实际就是用 AMS 的继承与 IntentResolver 的匿名内部类对象 mReceiverResolver 解析的,它也就是解析动态注册接收器的 IntentResolver;</span>
第七步:创建动态广播信息 BroadcastRecord 对象;
第八步:将上一步创建的动态广播信息 BroadcastRecord 插入 BroadcastQueue.mParallelBroadcasts(能够立即执行不等待其他广播接收器处理的广播队列);
第九步:执行广播发送;
第十步:对于静态广播,如果是应用安装的广播,就会忽略这个新安装包(不然岂不是安装了就立即起来了);
第十一步:如果该广播指定了 order = true,那么第八步中动态广播接收器不会处理,而是在本步跟静态广播接收器一起算优先级然后排列;
第十二步:创建静态广播信息 BroadcastRecord 对象;
第十三步:将上一步创建的静态广播信息 BroadcastRecord 插入 BroadcastQueue.mOrderedBroadcasts(有序广播列表,排序靠后的广播接收器需要等前面的执行结束);
第十四步:执行广播发送。
步骤5. scheduleBroadcastsLocked
这一步其实就是发消息 BROADCAST_INTENT_MSG 到 mHandler 开始轮询广播处理,如果发过消息并没有开始处理的话就不发了:
public void scheduleBroadcastsLocked() {
if (DEBUG_BROADCAST) Slog.v(TAG, "Schedule broadcasts ["
+ mQueueName + "]: current="
+ mBroadcastsScheduled);
if (mBroadcastsScheduled) {
return;
}
mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
mBroadcastsScheduled = true;
}
步骤6. processNextBroadcast
final void processNextBroadcast(boolean fromMsg) {
synchronized(mService) {
......
// First, deliver any non-serialized broadcasts right away.
while (mParallelBroadcasts.size() > 0) {
r = mParallelBroadcasts.remove(0);
r.dispatchTime = SystemClock.uptimeMillis();
r.dispatchClockTime = System.currentTimeMillis();
final int N = r.receivers.size();
......
1. for (int i=0; i<N; i++) {
Object target = r.receivers.get(i);
......
deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
}
addBroadcastToHistoryLocked(r);
......
}
// Now take care of the next serialized one...
// If we are waiting for a process to come up to handle the next
// broadcast, then do nothing at this point. Just in case, we
// check that the process we're waiting for still exists.
2. if (mPendingBroadcast != null) {
if (DEBUG_BROADCAST_LIGHT) {
Slog.v(TAG, "processNextBroadcast ["
+ mQueueName + "]: waiting for "
+ mPendingBroadcast.curApp);
}
boolean isDead;
synchronized (mService.mPidsSelfLocked) {
ProcessRecord proc = mService.mPidsSelfLocked.get(mPendingBroadcast.curApp.pid);
isDead = proc == null || proc.crashing;
}
if (!isDead) {
// It's still alive, so keep waiting
return;
} else {
......
mPendingBroadcast.state = BroadcastRecord.IDLE;
mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
mPendingBroadcast = null;
}
}
boolean looped = false;
3. do {
......
r = mOrderedBroadcasts.get(0);
......
if (r.receivers == null || r.nextReceiver >= numReceivers
|| r.resultAbort || forceReceive) {
...
if (r.resultTo != null) {
try {
......
performReceiveLocked(r.callerApp, r.resultTo,
new Intent(r.intent), r.resultCode,
r.resultData, r.resultExtras, false, false, r.userId);
// Set this to null so that the reference
// (local and remote) isn't kept in the mBroadcastHistory.
r.resultTo = null;
} catch (RemoteException e) {
Slog.w(TAG, "Failure ["
+ mQueueName + "] sending broadcast result of "
+ r.intent, e);
}
}
if (DEBUG_BROADCAST) Slog.v(TAG, "Cancelling BROADCAST_TIMEOUT_MSG");
cancelBroadcastTimeoutLocked();
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Finished with ordered broadcast "
+ r);
// ... and on to the next...
addBroadcastToHistoryLocked(r);
mOrderedBroadcasts.remove(0);
r = null;
looped = true;
continue;
}
} while (r == null);
// Get the next receiver...
int recIdx = r.nextReceiver++;
......
Object nextReceiver = r.receivers.get(recIdx);
if (nextReceiver instanceof BroadcastFilter) {
// Simple case: this is a registered receiver who gets
// a direct call.
BroadcastFilter filter = (BroadcastFilter)nextReceiver;
if (DEBUG_BROADCAST) Slog.v(TAG,
"Delivering ordered ["
+ mQueueName + "] to registered "
+ filter + ": " + r);
4. deliverToRegisteredReceiverLocked(r, filter, r.ordered);
if (r.receiver == null || !r.ordered) {
......
r.state = BroadcastRecord.IDLE;
scheduleBroadcastsLocked();
}
return;
}
// Hard case: need to instantiate the receiver, possibly
// starting its application process to host it.
ResolveInfo info =
(ResolveInfo)nextReceiver;
ComponentName component = new ComponentName(
info.activityInfo.applicationInfo.packageName,
info.activityInfo.name);
boolean skip = false;
5. int perm = mService.checkComponentPermission(info.activityInfo.permission,
r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
info.activityInfo.exported);
if (perm != PackageManager.PERMISSION_GRANTED) {
......
skip = true;
}
6. if (info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID &&
r.requiredPermission != null) {
try {
perm = AppGlobals.getPackageManager().
checkPermission(r.requiredPermission,
info.activityInfo.applicationInfo.packageName);
} catch (RemoteException e) {
perm = PackageManager.PERMISSION_DENIED;
}
if (perm != PackageManager.PERMISSION_GRANTED) {
......
skip = true;
}
}
7. if (r.appOp != AppOpsManager.OP_NONE) {
int mode = mService.mAppOpsService.noteOperation(r.appOp,
info.activityInfo.applicationInfo.uid, info.activityInfo.packageName);
if (mode != AppOpsManager.MODE_ALLOWED) {
......
skip = true;
}
}
if (!skip) {
8. skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid);
}
......
9. if ((info.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
if (ActivityManager.checkUidPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS,
info.activityInfo.applicationInfo.uid)
!= PackageManager.PERMISSION_GRANTED) {
......
skip = true;
}
}
10. if (r.curApp != null && r.curApp.crashing) {
// If the target process is crashing, just skip it.
......
skip = true;
}
if (!skip) {
boolean isAvailable = false;
try {
11. isAvailable = AppGlobals.getPackageManager().isPackageAvailable(
info.activityInfo.packageName,
UserHandle.getUserId(info.activityInfo.applicationInfo.uid));
} catch (Exception e) {
......
}
if (!isAvailable) {
......
skip = true;
}
}
if (skip) {
......
r.receiver = null;
r.curFilter = null;
r.state = BroadcastRecord.IDLE;
12. scheduleBroadcastsLocked();
return;
}
......
// Broadcast is being executed, its package can't be stopped.
try {
13. AppGlobals.getPackageManager().setPackageStoppedState(
r.curComponent.getPackageName(), false, UserHandle.getUserId(r.callingUid));
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ r.curComponent.getPackageName() + ": " + e);
}
// Is this receiver's application already running?
ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
info.activityInfo.applicationInfo.uid, false);
if (app != null && app.thread != null) {
try {
app.addPackage(info.activityInfo.packageName, mService.mProcessStats);
14. processCurBroadcastLocked(r, app);
return;
} catch (RemoteException e) {
......
} catch (RuntimeException e) {
......
}
}
......
15. if ((r.curApp=mService.startProcessLocked(targetProcess,
info.activityInfo.applicationInfo, true,
r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
"broadcast", r.curComponent,
(r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
== null) {
......
logBroadcastReceiverDiscardLocked(r);
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();
r.state = BroadcastRecord.IDLE;
return;
}
mPendingBroadcast = r;
mPendingBroadcastRecvIndex = recIdx;
}
}
第一步:通过函数 deliverToRegisteredReceiverLocked 处理动态广播列表 mParallelBroadcasts;
第二步:判断 mPendingBroadcast 中的广播是否还有效;
第三步:这个 do-while 循环里将按顺序遍历广播,如果是指定了 resultTo 域(receivers为空)的广播则通过函数 performReceiveLocked 立即处理,直到遇到 receivers 不为空的广播;
第四步:对于有序广播的动态广播接收器,立即通过函数 deliverToRegisteredReceiverLocked 处理;
第五步:检查接收器组件要求的权限广播是否满足,不满足则略过;
第六步:检查广播要求的权限接收器是否满足,不满足则略过;
第七步:检查广播要求的 AppOps 权限针对接收器的设置是否满足,不满足则略过;
第八步:靠 IntentFirewall 过滤广播;
第九步:如果接收器含 FLAG FLAG_SINGLE_USER,检查其是否有权限 android.Manifest.permission.INTERACT_ACROSS_USERS;
第十步:广播发送者如果崩溃了,则略过;
第十一步:判断接收器所在应用对当前用户是否可用,在多用户系统上可能存在这个问题;
第十二步:如果因为第5-11步的检查断定该广播要略过,则直接执行 scheduleBroadcastsLocked 进行下一轮循环;
第十三步:将接收器对应的 PackageSettiong 的 stopped 状态置为 false
第十四步:如果接收器所在进程在,通过函数 processCurBroadcastLocked 静态广播发送;
第十五步:如果接收器所在进程不在,启动进程。
1.2 从 AMS 到广播接收器
从上一节看出,在函数 processNextBroadcast 中有三个函数去处理不同情况下的广播:deliverToRegisteredReceiverLocked、performReceiveLocked、processCurBroadcastLocked,我们挨个来看,在此之前先来说说动态广播接收器是如何注册的。
1.2.0 registerReceiver
注册广播接收器的代码自然是从客户端开始,即 ContextImpl.registerReceiver 方法,其最终调用的是 ContextImpl.registerReceiverInternal:
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context) {
IIntentReceiver rd = null;
if (receiver != null) {
if (mPackageInfo != null && context != null) {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
1. rd = mPackageInfo.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
} else {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = new LoadedApk.ReceiverDispatcher(
receiver, context, scheduler, null, true).getIIntentReceiver();
}
}
try {
2. return ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission, userId);
} catch (RemoteException e) {
return null;
}
}
步骤1:mPackageInfo 是每个安卓App应用进程都有的 LoadedApk 对象,每个进程每个包对应同一个 LoadedApk 对象,该对象中对每个 Context 的每一个广播接收器都有一个 ReceiverDispatcher 对应,通过 getReceiverDispatcher 返回的接口就是针对当前 Context 的某一个广播接收器对应的 ReceiverDispatcher 的Binder操作接口,该接口有一个方法 performReceive,该Binder接口会传到 AMS 端,收到广播时 AMS 会通过此接口去执行广播接收器的 onReceive 方法;
步骤2:调用 AMS 的 registerReceiver 方法;
来看 AMS 的 registerReceiver 方法:
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
enforceNotIsolatedCaller("registerReceiver");
int callingUid;
int callingPid;
synchronized(this) {
ProcessRecord callerApp = null;
if (caller != null) {
1. callerApp = getRecordForAppLocked(caller);
if (callerApp == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when registering receiver " + receiver);
}
if (callerApp.info.uid != Process.SYSTEM_UID &&
!callerApp.pkgList.containsKey(callerPackage) &&
!"android".equals(callerPackage)) {
throw new SecurityException("Given caller package " + callerPackage
+ " is not running in process " + callerApp);
}
callingUid = callerApp.info.uid;
callingPid = callerApp.pid;
} else {
callerPackage = null;
callingUid = Binder.getCallingUid();
callingPid = Binder.getCallingPid();
}
userId = this.handleIncomingUser(callingPid, callingUid, userId,
true, true, "registerReceiver", callerPackage);
2. List allSticky = null;
// Look for any matching sticky broadcasts...
Iterator actions = filter.actionsIterator();
if (actions != null) {
while (actions.hasNext()) {
String action = (String)actions.next();
allSticky = getStickiesLocked(action, filter, allSticky,
UserHandle.USER_ALL);
allSticky = getStickiesLocked(action, filter, allSticky,
UserHandle.getUserId(callingUid));
}
} else {
allSticky = getStickiesLocked(null, filter, allSticky,
UserHandle.USER_ALL);
allSticky = getStickiesLocked(null, filter, allSticky,
UserHandle.getUserId(callingUid));
}
// The first sticky in the list is returned directly back to
// the client.
Intent sticky = allSticky != null ? (Intent)allSticky.get(0) : null;
if (DEBUG_BROADCAST) Slog.v(TAG, "Register receiver " + filter
+ ": " + sticky);
if (receiver == null) {
return sticky;
}
3. ReceiverList rl
= (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());
if (rl == null) {
rl = new ReceiverList(this, callerApp, callingPid, callingUid,
userId, receiver);
if (rl.app != null) {
rl.app.receivers.add(rl);
} else {
try {
receiver.asBinder().linkToDeath(rl, 0);
} catch (RemoteException e) {
return sticky;
}
rl.linkedToDeath = true;
}
mRegisteredReceivers.put(receiver.asBinder(), rl);
} else ......
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId);
4. rl.add(bf);
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadast");
}
5. mReceiverResolver.addFilter(bf);
// Enqueue broadcasts for all existing stickies that match
// this filter.
if (allSticky != null) {
ArrayList receivers = new ArrayList();
receivers.add(bf);
int N = allSticky.size();
for (int i=0; i<N; i++) {
Intent intent = (Intent)allSticky.get(i);
BroadcastQueue queue = broadcastQueueForIntent(intent);
BroadcastRecord r = new BroadcastRecord(queue, intent, null,
null, -1, -1, null, null, AppOpsManager.OP_NONE, receivers, null, 0,
null, null, false, true, true, -1);
6. queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();
}
}
return sticky;
}
}
步骤1:caller 是从客户端传过来的 ApplicationThread 对象,代表一个客户端 token,进程信息 ProcessRecord 也是用此 token (间接)作 key 存储的,通过此 token 查询进程信息 ProcessRecord 判断合法性;
步骤2:搜集对应的 sticky 广播列表 allSticky;
步骤3:获取存储在 mRegisteredReceivers 中 caller 对应的 ReceiverList 对象,如果为空则创建一个,一个广播接收器可以重复注册;
步骤4:这次注册的接收器封装为 BroadcastFilter 对象,加入到上一步创建的 ReceiverList 对象中,一个广播接收器可以重复注册,每次注册都会为其增加一个 BroadcastFilter 对象,代表着要做一次处理,ReceiverList 就是个 BroadcastFilter 的列表,每个 BroadcastFilter 里都包含对 ReceiverList 的引用;
步骤5:将上一步创建的 BroadcastFilter 对象加入到动态接收组件解析器 mReceiverResolver 中,以便在广播来时能够解析出对应的接收器,这在上一节“步骤4. broadcastIntentLocked”的第6步中已经说明了;
步骤6:将步骤2中解析的 sticky 广播插入动态广播队列,立即执行。
1.2.1 deliverToRegisteredReceiverLocked
在上一小节看完了广播接收器的注册我们来看下这个处理动态广播的方法 deliverToRegisteredReceiverLocked:
private final void deliverToRegisteredReceiverLocked(BroadcastRecord r,
BroadcastFilter filter, boolean ordered) {
boolean skip = false;
if (filter.requiredPermission != null) {
1. int perm = mService.checkComponentPermission(filter.requiredPermission,
r.callingPid, r.callingUid, -1, true);
if (perm != PackageManager.PERMISSION_GRANTED) {
......
skip = true;
}
}
if (!skip && r.requiredPermission != null) {
2. int perm = mService.checkComponentPermission(r.requiredPermission,
filter.receiverList.pid, filter.receiverList.uid, -1, true);
if (perm != PackageManager.PERMISSION_GRANTED) {
......
skip = true;
}
}
if (r.appOp != AppOpsManager.OP_NONE) {
3. int mode = mService.mAppOpsService.noteOperation(r.appOp,
filter.receiverList.uid, filter.packageName);
if (mode != AppOpsManager.MODE_ALLOWED) {
......
skip = true;
}
}
if (!skip) {
4. skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
r.callingPid, r.resolvedType, filter.receiverList.uid);
}
5. if (filter.receiverList.app == null || filter.receiverList.app.crashing) {
......
skip = true;
}
if (!skip) {
......
try {
......
6. performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId);
if (ordered) {
r.state = BroadcastRecord.CALL_DONE_RECEIVE;
}
} catch (RemoteException e) {
......
}
}
}
步骤1:filter 就是上一节说的每次动态注册都会创建的 BroadcastFilter 了,这一步将检查发出的广播是否拥有接收器要求的权限,如果没有则略过;
步骤2:检查广播接收器是否拥有广播所要求的权限,如果没有则略过;
步骤3:检查广播接收器是否通过广播所要求的 AppOps 权限,如果没有则略过;
步骤4:IntentFirewall 过滤广播;
步骤5:如果广播接收器所在进程崩溃,则略过;
步骤6:通过 performReceiveLocked 处理广播,也就是说 deliverToRegisteredReceiverLocked 其实也是通过 performReceiveLocked 函数处理广播的;
1.2.2 performReceiveLocked
private static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
// Send the intent to the receiver asynchronously using one-way binder calls.
if (app != null && app.thread != null) {
// If we have an app thread, do the call through that so it is
// correctly ordered with other one-way calls.
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
data, extras, ordered, sticky, sendingUser, app.repProcState);
} else {
receiver.performReceive(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
}
}
app.thread.scheduleRegisteredReceiver 是一个 Binder oneway 调用,最终调用到 ApplicationThread.scheduleRegisteredReceiver 方法,这个方法实际也最中调用到 receiver.performReceiver(),至于为什么要用 scheduleRegisteredReceiver 不直接用 performReceiver,注释的解释是这样的:
// This function exists to make sure all receiver dispatching is
// correctly ordered, since these are one-way calls and the binder driver
// applies transaction ordering per object for such calls.
奇怪的是这个 performReceiver 也是 oneway 的 binder 调用,知道两者区别的朋友希望能尽快告知。
下面来看 performReceive 方法,这个方法在 1.2.0 节提到过:
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
LoadedApk.ReceiverDispatcher rd = mDispatcher.get();
......
if (rd != null) {
* rd.performReceive(intent, resultCode, data, extras,
ordered, sticky, sendingUser);
} else {
......
}
}
实际上就是调用 ReceiverDispatcher.performRecieve():
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
......
Args args = new Args(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
* if (!mActivityThread.post(args)) {
......
}
}
Args 其实是一个 Runnable 接口实现,其run方法是最终调用广播接收器 onReceive 方法的地方,可以发现该的真正执行是通过 Handle.post 发到了主线程(UI)去做了,我们来看 Args 的 run 方法:
public void run() {
......
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveReg");
try {
ClassLoader cl = mReceiver.getClass().getClassLoader();
intent.setExtrasClassLoader(cl);
setExtrasClassLoader(cl);
receiver.setPendingResult(this);
1. receiver.onReceive(mContext, intent);
} catch (Exception e) {
......
}
if (receiver.getPendingResult() != null) {
2. finish();
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
第一步:调用了 BroadcastReceiver.onReceive();
第二步:本次广播处理完成,Args 继承于 BroadcastReceiver.PendingResult,这里执行的 finish 方法实际上是 BroadcastReceiver.PendingResult.finish() 方法:
public final void finish() {
if (mType == TYPE_COMPONENT) {
...... // 这里是用于静态组建
} else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
......
final IActivityManager mgr = ActivityManagerNative.getDefault();
* sendFinished(mgr);
}
}
mOrderedHint 指是否为有序广播,也就是说动态广播接收器只有接收有序广播时才会给AMS回传消息。
我们来看下如果是有序广播消息如何回传,回传的方法是 BroadcastReceiver.PendingResult.sendFinish():
public void sendFinished(IActivityManager am) {
synchronized (this) {
if (mFinished) {
throw new IllegalStateException("Broadcast already finished");
}
mFinished = true;
try {
if (mResultExtras != null) {
mResultExtras.setAllowFds(false);
}
if (mOrderedHint) {
* am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,
mAbortBroadcast);
} else {
// This broadcast was sent to a component; it is not ordered,
// but we still need to tell the activity manager we are done.
am.finishReceiver(mToken, 0, null, null, false); // 静态广播接收器
}
} catch (RemoteException ex) {
}
}
}
带*的这一行就是告诉 AMS 广播处理完了,mToken 就是在 1.2.0 节所说的 IIntentReceiver,mAbortBroadcast 指是否停止有序广播继续传给下一个接收器。我们来看 AMS 的 finishReceiver 方法
public void finishReceiver(IBinder who, int resultCode, String resultData,
Bundle resultExtras, boolean resultAbort) {
......
final long origId = Binder.clearCallingIdentity();
try {
boolean doNext = false;
BroadcastRecord r;
synchronized(this) {
1. r = broadcastRecordForReceiverLocked(who);
if (r != null) {
2. doNext = r.queue.finishReceiverLocked(r, resultCode,
resultData, resultExtras, resultAbort, true);
}
}
if (doNext) {
3. r.queue.processNextBroadcast(false);
}
trimApplications();
} finally {
Binder.restoreCallingIdentity(origId);
}
}
步骤1:拿到对应 BroadcastRecord 对象;
步骤2:正确修改 BroadcastRecord 对象的各个域,返回是否要发给下一个广播接收器;
步骤3:执行下一轮广播处理;
1.2.3 processCurBroadcastLocked
前面两种说的都是动态广播接收器的处理办法,静态广播接收器则用的是 processCurBroadcastLocked 方法:
private final void processCurBroadcastLocked(BroadcastRecord r,
ProcessRecord app) throws RemoteException {
......
boolean started = false;
try {
...
mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
* app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
app.repProcState);
...
started = true;
} finally {
......
}
}
可以发现静态广播接收器的处理调用到了 ApplicationThread.scheduleReceiver 方法(动态广播接收器是 scheduleRegisteredReceiver):
public final void scheduleReceiver(Intent intent, ActivityInfo info,
CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
boolean sync, int sendingUser, int processState) {
updateProcessState(processState, false);
* ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
sync, false, mAppThread.asBinder(), sendingUser);
r.info = info;
r.compatInfo = compatInfo;
* sendMessage(H.RECEIVER, r);
}
广播相关信息都保存在 ReceiverData 对象中,这个 ReceiverData 跟前面的 Args 一样也是继承于 BroadcastReceiver.PendingResult,进入主线程消息循环,即也是在主线程执行的,最终执行的是 handleReceiver 方法:
private void handleReceiver(ReceiverData data) {
......
String component = data.intent.getComponent().getClassName();
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
IActivityManager mgr = ActivityManagerNative.getDefault();
BroadcastReceiver receiver;
try {
java.lang.ClassLoader cl = packageInfo.getClassLoader();
data.intent.setExtrasClassLoader(cl);
data.setExtrasClassLoader(cl);
1. receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();
} catch (Exception e) {
......
}
try {
Application app = packageInfo.makeApplication(false, mInstrumentation);
......
ContextImpl context = (ContextImpl)app.getBaseContext();
sCurrentBroadcastIntent.set(data.intent);
receiver.setPendingResult(data);
2. receiver.onReceive(context.getReceiverRestrictedContext(),
data.intent);
} catch (Exception e) {
......
} finally {
sCurrentBroadcastIntent.set(null);
}
if (receiver.getPendingResult() != null) {
3. data.finish();
}
}
步骤1:创建目标组件实例,我们会发现,静态广播接收器组件,对于每一个广播都会创建一个新对象去处理;
步骤2:执行接收器组件(BroadcastReceiver)的 onReceive 方法;
步骤3:广播处理结束,这里跟动态广播接收器的逻辑一样,执行的是 BroadcastReceiver.PendingResult.finish():
public final void finish() {
if (mType == TYPE_COMPONENT) {
final IActivityManager mgr = ActivityManagerNative.getDefault();
1. if (QueuedWork.hasPendingWork()) {
......
QueuedWork.singleThreadExecutor().execute( new Runnable() {
@Override public void run() {
......
2. sendFinished(mgr);
}
});
} else {
......
sendFinished(mgr);
}
} else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
...... // 动态广播接收器的逻辑
}
}
不一样的是这回执行的是不同的分支逻辑.
步骤1:如果 QueuedWork 中还有工作没做,将 sendFinish 放到这些工作之后执行,目前来讲,QueuedWork 只用于了 SharedPreference 的写操作;
步骤2:执行 sendFinished,上面动态广播接收器已经说过了,这里就不再展开了。与动态广播接收器不同的是,不管有序还是无序广播,静态广播接收器一定会告诉 AMS 广播处理完了,而动态广播接收器对于无序广播处理完 是不走finish方法的。
2. 安全的考虑 - FLAG_EXCLUDE_STOPPED_PACKAGES
第一节我们了解了广播如何发送和广播队列如何循环处理的,在 AMS 的 broadcastIntentLocked 方法的开头我们提到,会给所有广播的 Intent 加入标志位 Intent.FLAG_EXCLUDE_STOPPED_PACKAGES,这是确保所有广播默认都不会发给已经停止运行的应用,这一点有效的防止了一大批应用靠广播自启的行为。
上面1.1节步骤4的第5小步,我们提到函数 collectReceiverComponents 通过广播的 Intent 解析所有静态广播接收器:
private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
int[] users) {
List<ResolveInfo> receivers = null;
try {
HashSet<ComponentName> singleUserReceivers = null;
boolean scannedFirstReceivers = false;
for (int user : users) {
* List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
.queryIntentReceivers(intent, resolvedType, STOCK_PM_FLAGS, user);
......
if (newReceivers != null && newReceivers.size() == 0) {
newReceivers = null;
}
if (receivers == null) {
receivers = newReceivers;
} else if (newReceivers != null) {
......
}
}
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
}
return receivers;
}
带*那行代码就是通过 PMS 去查询所有匹配的组件了,PMS 解析 Intent 有四个主要接口:queryIntentActivities 是解析出所有匹配的 Activity,queryIntentServices 是解析出所有匹配的 Service,queryIntentReceivers 是解析出所有匹配的 BroadcastReceiver,queryIntentContentProviders 是解析出所有匹配的 ContentProvider,返回值都是 ResolveInfo 的列表。
我们直接来看 PMS 的 queryIntentReceivers 方法:
public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags,
int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
intent = intent.getSelector();
comp = intent.getComponent();
}
}
if (comp != null) {
List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
1. ActivityInfo ai = getReceiverInfo(comp, flags, userId);
if (ai != null) {
ResolveInfo ri = new ResolveInfo();
ri.activityInfo = ai;
list.add(ri);
}
return list;
}
// reader
synchronized (mPackages) {
String pkgName = intent.getPackage();
if (pkgName == null) {
2. return mReceivers.queryIntent(intent, resolvedType, flags, userId);
}
......
return null;
}
}
步骤1:如果 Intent 中指定了组件,那么就直接包装为 ResolveInfo 对象返回了(当然 getReceiverInfo 函数期间还得判断该组件是否可用);
步骤2:mReceivers 就是个 ActivityIntentResolver,虽然是解析 BroadcastReceiver,名字也叫 mReceivers, 但是和 mActivities 一样都是 ActivityIntentResolver:
// All available receivers, for your resolving pleasure.
final ActivityIntentResolver mReceivers = new ActivityIntentResolver();
来看 ActivityIntentResolver 的 queryIntent 方法:
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags,
int userId) {
if (!sUserManager.exists(userId)) return null;
mFlags = flags;
return super.queryIntent(intent, resolvedType,
(flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId);
}
父类 IntentResolver 的 queryIntent 方法:
public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly,
int userId) {
...... //一大波逻辑
FastImmutableArraySet<String> categories = getFastIntentCategories(intent);
if (firstTypeCut != null) {
buildResolveList(intent, categories, debug, defaultOnly,
resolvedType, scheme, firstTypeCut, finalList, userId);
}
if (secondTypeCut != null) {
buildResolveList(intent, categories, debug, defaultOnly,
resolvedType, scheme, secondTypeCut, finalList, userId);
}
if (thirdTypeCut != null) {
buildResolveList(intent, categories, debug, defaultOnly,
resolvedType, scheme, thirdTypeCut, finalList, userId);
}
if (schemeCut != null) {
buildResolveList(intent, categories, debug, defaultOnly,
resolvedType, scheme, schemeCut, finalList, userId);
}
sortResults(finalList);
......
return finalList;
}
由于本文并不是讲 IntentResolver 到底是如何过滤组件返回结果的,那一大波逻辑都不再展开了,可以看出大体就是四步 buildResolveList 定结果,我们要说的关键其实就在 buildResolveList 函数中:
private void buildResolveList(Intent intent, FastImmutableArraySet<String> categories,
boolean debug, boolean defaultOnly,
String resolvedType, String scheme, F[] src, List<R> dest, int userId) {
......
1. final boolean excludingStopped = intent.isExcludingStopped();
......
for (i=0; i<N && (filter=src[i]) != null; i++) {
int match;
...
2. if (excludingStopped && isFilterStopped(filter, userId)) {
...
continue;
}
// Is delivery being limited to filters owned by a particular package?
3. if (packageName != null && !isPackageForFilter(packageName, filter)) {
...
continue;
}
// Do we already have this one?
4. if (!allowFilterResult(filter, dest)) {
...
continue;
}
5. match = filter.match(action, resolvedType, scheme, data, categories, TAG);
if (match >= 0) {
...
if (!defaultOnly || filter.hasCategory(Intent.CATEGORY_DEFAULT)) {
final R oneResult = newResult(filter, match, userId);
if (oneResult != null) {
6. dest.add(oneResult);
...
}
} else {
hasNonDefaults = true;
}
} else {
......
}
}
......
}
步骤1:判断 Intent 中是否有标志位 Intent.FLAG_EXCLUDE_STOPPED_PACKAGES,如果有这个标志位,则 excludingStopped=true;
步骤2:如果 excludingStopped=true 也就是有标志位 Intent.FLAG_EXCLUDE_STOPPED_PACKAGES,且 isFilterStopped() 函数返回也为 true 的话该组件直接略过,我们来看看 isFilterStopped 函数,这里也就是调用了 IntentResolverActivities.isFilterStopped():
protected boolean isFilterStopped(PackageParser.ActivityIntentInfo filter, int userId) {
if (!sUserManager.exists(userId)) return true;
PackageParser.Package p = filter.activity.owner;
if (p != null) {
PackageSetting ps = (PackageSetting)p.mExtras;
if (ps != null) {
// System apps are never considered stopped for purposes of
// filtering, because there may be no way for the user to
// actually re-launch them.
* return (ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0
&& ps.getStopped(userId);
}
}
return false;
}
注意带*这行,ps.getStopped(userId) 返回的就是该应用是否有明确的被停止(exactly stopped),什么是“明确的被停止”?目前来讲其实就是被 AMS 给 forceStopPackage 了,AMS 什么时候会执行 forceStopPackage 呢?体现在普通的手机上,如果被用户在设置里点了停止按钮,当该应用还未再启动这个按钮还处在不可点的状态时,就是被明确的停止了(关于 PackageSetting 我们会在讲 PMS 的文章中来说)。
步骤3:如果 Intent 指定了包名,判断包名是否一致;
步骤4:重复加过的结果就不用再加了;
步骤5:判断这个 filter 是否满足 action、resolvedType、scheme、data、categories;
步骤6:满足的话构造 ResolveInfo 加入到结果中;
从步骤2可以看出,被明确停止了的非系统应用的广播接收器组件是无法接收到广播的,然而系统应用的静态广播接收器组件无论是否被停止被启动过,都会接收广播 —— 某些黑心rom的系统应用频繁自启耗电问题就诞生了。一个普通应用防止其广播自启的根本方法其实就是在设置里干掉它(没有root的话),不过应用里如果注册一堆静态系统广播接收器的话,从某些程度上也能拉活,比如若因内存不足的原因被系统回收、被直接kill掉进程等,广播都是能直接把它重新启动起来的。
<<未完待续>>