[Android]Ams 广播发送原理(三)
来源:互联网 发布:会计资格证网络课程 编辑:程序博客网 时间:2024/06/06 03:12
[Android]Ams 广播发送原理(三)
- AndroidAms 广播发送原理三
- 发送广播的核心方法AMS
- broadcastIntentLocked
- broadcastIntentLocked 函数主要功能
- 1 移除Stopped Package来接收此广播
- 2 判断广播是否是PRE_Boot接收
- 3 某些特殊广播的处理
- 4 Protected broadcast
- 5 特殊ACTION处理
- 6 StickBroadcast
- 61 Sticky广播数据结构
- 7 根据Intent Figure out 对应的Receiver
- 8 优先处理并行广播
- 9 处理静态注册Receiver和Order动态广播
- 0 整理mReceiver和registeredReceivers
- 1 无Receiver接收此Intent仅记录
- 开始进入广播队列BroadcastQeueue
- 1 scheduleBroadcastsLocked
- 总结
- 发送广播的核心方法AMS
1. 发送广播的核心方法(AMS)
broadcastIntentLocked
broadcastIntentLocked(args ...)
2. broadcastIntentLocked 函数主要功能
2.1 移除Stopped Package来接收此广播
intent = new Intent(intent);// By default broadcasts do not go to stopped apps.intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
思考:
为什么intent要添加FLAG_EXCLUDE_STOPPED_PACKAGES标记呢?
原因是这样的,在Android 3.1之后,PKMS加强了对“处于停止状态的”应用的管理。如果一个应用在安装后从来没有启动过,或者已经被用户强制停止了,那么这个应用就处于停止状态(stopped state)。为了达到精细调整的目的,Android增加了2个flag以此来表示intent是否要激活“处于停止状态的”应用。
- FLAG_INCLUDE_STOPPED_PACKAGES
- FLAG_EXCLUDE_STOPPED_PACKAGES,
/** * If set, this intent will not match any components in packages that * are currently stopped. If this is not set, then the default behavior * is to include such applications in the result. */public static final int FLAG_EXCLUDE_STOPPED_PACKAGES = 0x00000010;/** * If set, this intent will always match any components in packages that * are currently stopped. This is the default behavior when * {@link #FLAG_EXCLUDE_STOPPED_PACKAGES} is not set. If both of these * flags are set, this one wins (it allows overriding of exclude for * places where the framework may automatically set the exclude flag). */public static final int FLAG_INCLUDE_STOPPED_PACKAGES = 0x00000020;
- 默认情况下会忽略掉未启动过的,或被用户禁用的APP;
- 若是想要这些用户收到,可使用==FLAG_INCLUDE_STOPPED_PACKAGES==
2.2 判断广播是否是PRE_Boot接收
// If we have not finished booting, don't allow this to launch new processes.if (!mProcessesReady && (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) { intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);}
思考:
为何系统未就绪则静态接收者不能接收广播?
如果系统未就绪,比如说Bootcomplete之前,那么只有注册的广播接收者才可以接收,而静态广播则不可接收因为,此时PMS可能未就绪,manifest.xml有可能未被成功解析
2.3 某些特殊广播的处理
2.4 Protected broadcast
判断广播是否为Protected,非系统应用不能使用Protect广播
// Verify that protected broadcasts are only being sent by system code, // and that system code is only sending protected broadcasts. final String action = intent.getAction(); final boolean isProtectedBroadcast; try { isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action); } catch (RemoteException e) { Slog.w(TAG, "Remote exception", e); return ActivityManager.BROADCAST_SUCCESS; } final boolean isCallerSystem; switch (UserHandle.getAppId(callingUid)) { case Process.ROOT_UID: case Process.SYSTEM_UID: case Process.PHONE_UID: case Process.BLUETOOTH_UID: case Process.NFC_UID: isCallerSystem = true; break; default: isCallerSystem = (callerApp != null) && callerApp.persistent; break; } // First line security check before anything else: stop non-system apps from // sending protected broadcasts. if (!isCallerSystem) { if (isProtectedBroadcast) { throw new SecurityException(msg); } else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action) || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) { // Special case for compatibility: we don't want apps to send this, // but historically it has not been protected and apps may be using it // to poke their own app widget. So, instead of making it protected, // just limit it to the caller. if (callerPackage == null) { String msg = "Permission Denial: not allowed to send broadcast " + action + " from unknown caller."; Slog.w(TAG, msg); throw new SecurityException(msg); } else if (intent.getComponent() != null) { // They are good enough to send to an explicit component... verify // it is being sent to the calling app. if (!intent.getComponent().getPackageName().equals( callerPackage)) { String msg = "Permission Denial: not allowed to send broadcast " + action + " to " + intent.getComponent().getPackageName() + " from " + callerPackage; Slog.w(TAG, msg); throw new SecurityException(msg); } } else { // Limit broadcast to their own package. intent.setPackage(callerPackage); } } }
NOTE:1
另外,还有一个“保护性广播”的概念,也要考虑进来。网上有一些人询问AndroidManifest.xml中的一级标记是什么意思。
简单地说,Google认为有一些广播是只能由系统发送的,如果某个系统级AndroidManifest.xml中写了这个标记,那么在PKMS解析该文件时,就会把“保护性广播”标记中的名字(一般是Action字符串)记录下来。在系统运作起来之后,==如果某个不具有系统权限的应用试图发送系统中的“保护性广播”,那么到AMS的broadcastIntentLocked()处就会被拦住,AMS会抛出异常,提示”Permission Denial: not allowed to send broadcast”.
- Root/System/PHone/Bluetooth/NFC 总是作为系统进程
- ProtectedBroadcast,主要定义在系统级应用的Manfiest.xml中,可见frameworks/base/core/res/AndroidManifest.xml
2.5 特殊ACTION处理
NOTE:(具体实现要看代码)
if (action != null) { switch (action) { case Intent.ACTION_UID_REMOVED: case Intent.ACTION_PACKAGE_REMOVED: case Intent.ACTION_PACKAGE_CHANGED: case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE: case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE: case Intent.ACTION_PACKAGES_SUSPENDED: case Intent.ACTION_PACKAGES_UNSUSPENDED: switch (action) { case Intent.ACTION_UID_REMOVED: case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE: case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE: case Intent.ACTION_PACKAGE_REMOVED: case Intent.ACTION_PACKAGE_CHANGED: case Intent.ACTION_PACKAGE_REPLACED: case Intent.ACTION_PACKAGE_ADDED: case Intent.ACTION_PACKAGE_DATA_CLEARED: case Intent.ACTION_TIMEZONE_CHANGED: case Intent.ACTION_TIME_CHANGED: case Intent.ACTION_CLEAR_DNS_CACHE: case Proxy.PROXY_CHANGE_ACTION: case android.hardware.Camera.ACTION_NEW_PICTURE: case android.hardware.Camera.ACTION_NEW_VIDEO: return ActivityManager.BROADCAST_SUCCESS; } }
2.6 StickBroadcast
NOTE: 处理stickyBroadcast并且更新Ams中的sticky广播表
// Add to the sticky list if requested. if (sticky) { /// 检查权限 if (checkPermission(android.Manifest.permission.BROADCAST_STICKY, callingPid, callingUid) != PackageManager.PERMISSION_GRANTED) { String msg = "Permission Denial: broadcastIntent() requesting a sticky broadcast from pid=" + callingPid + ", uid=" + callingUid + " requires " + android.Manifest.permission.BROADCAST_STICKY; Slog.w(TAG, msg); throw new SecurityException(msg); } if (requiredPermissions != null && requiredPermissions.length > 0) { Slog.w(TAG, "Can't broadcast sticky intent " + intent + " and enforce permissions " + Arrays.toString(requiredPermissions)); return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION; } // stickBroadcast不能指定target if (intent.getComponent() != null) { throw new SecurityException( "Sticky broadcasts can't target a specific component"); } // We use userId directly here, since the "all" target is maintained // as a separate set of sticky broadcasts. if (userId != UserHandle.USER_ALL) { // But first, if this is not a broadcast to all users, then // make sure it doesn't conflict with an existing broadcast to // all users. // ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get( UserHandle.USER_ALL); if (stickies != null) { ArrayList<Intent> list = stickies.get(intent.getAction()); if (list != null) { int N = list.size(); int i; for (i=0; i<N; i++) { if (intent.filterEquals(list.get(i))) { throw new IllegalArgumentException( "Sticky broadcast " + intent + " for user " + userId + " conflicts with existing global broadcast"); } } } } } // 所有的StickyBroadcast都会保存在这个集合里面 ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId); // 第一次发sticky广播时,这个userId对象的ArrayMap肯定是空,并且将其保存起来 if (stickies == null) { stickies = new ArrayMap<>(); mStickyBroadcasts.put(userId, stickies); } ArrayList<Intent> list = stickies.get(intent.getAction()); if (list == null) { list = new ArrayList<>(); stickies.put(intent.getAction(), list); } final int stickiesCount = list.size(); int i; for (i = 0; i < stickiesCount; i++) { if (intent.filterEquals(list.get(i))) { // This sticky already exists, replace it. list.set(i, new Intent(intent)); break; } } if (i >= stickiesCount) { list.add(new Intent(intent)); } }
这段代码主要任务:
- 权限检查
- 冲突检查
- 将每次发送的sticky 都保存到mStickyBroadcasts集合中**
思考: 什么sticky广播不能指定target?
2.6.1 Sticky广播数据结构
// 判断该广播是发给指定用户还是所有用户int[] users;if (userId == UserHandle.USER_ALL) { // Caller wants broadcast to go to all started users. users = mUserController.getStartedUserArrayLocked(); } else { // Caller wants broadcast to go to one specific user. users = new int[] {userId}; }
所有用户应该是指未指定target的广播
指定target的广播只能发给指定的APP
2.7 根据Intent Figure out 对应的Receiver
NOTE:
- receivers静态注册的接收者
- registeredReceivers所有动态注册的接收者
// Figure out who all will receive this broadcast.List receivers = null; // 存储静态注册的receiverList<BroadcastFilter> registeredReceivers = null; // 存储动态注册的接收者// Need to resolve the intent to interested receivers...if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) { // 收集PMS中通过manifest.xml解析出的receivers receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);}if (intent.getComponent() == null) { // adb shell 中通过shell user , am broadcast发送广播 if (userId == UserHandle.USER_ALL && callingUid == Process.SHELL_UID) { // Query one target user at a time, excluding shell-restricted users for (int i = 0; i < users.length; i++) { if (mUserController.hasUserRestriction( UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) { continue; } List<BroadcastFilter> registeredReceiversForUser = mReceiverResolver.queryIntent(intent, resolvedType, false, users[i]); if (registeredReceivers == null) { registeredReceivers = registeredReceiversForUser; } else if (registeredReceiversForUser != null) { registeredReceivers.addAll(registeredReceiversForUser); } } } else { // 根据IntentResolver 查询receivers,并且log中可查到匹配的结果有哪些 registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false, userId); }}
NOTE:
- IntentResolver 信息Debug ->
- Intent.setFlag(Intent.Flag_Debug_LOG_RESOLUTION)
2.8 优先处理并行广播
NOTE:
- 为什么先单独处理非order动态注册的广播?
If we are not serializing this broadcast, then send the registered receivers separately so they don’t wait for the components to be launched.- 动态注册的广播,proc已经是在运行中,广播发送后,不需要等待receiver启动
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. if (isCallerSystem) { checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid, isProtectedBroadcast, registeredReceivers); } final BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callingPid, callingUid, resolvedType, requiredPermissions, appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId); if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r); final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r); if (!replaced) { queue.enqueueParallelBroadcastLocked(r); queue.scheduleBroadcastsLocked(); } registeredReceivers = null; NR = 0;}
2.9 处理静态注册Receiver和Order动态广播
// 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; 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); } // 将满足特殊条件的receiver 从结果中移除 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--; } } } } } int NT = receivers != null ? receivers.size() : 0; int it = 0; ResolveInfo curt = null; BroadcastFilter curr = null; // order 广播,需要将NR/NT整合到一起 // 如果 NT > NR此处将一次性将registered中的元素转移到mReceivers中 while (it < NT && ir < NR) { if (curt == null) { curt = (ResolveInfo)receivers.get(it); } if (curr == null) { curr = registeredReceivers.get(ir); } // 将registered中的Filter 优先级高的移到mReceivers中 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; } } } // 如果 NT < NR ,那么上面的逻辑处理完后,NR中还有一部分,再由此处的代码将element移到mReceivers中 while (ir < NR) { if (receivers == null) { receivers = new ArrayList(); } receivers.add(registeredReceivers.get(ir)); ir++; } if (isCallerSystem) { checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid, isProtectedBroadcast, receivers); } // 将最终的结果发给BroadCastQueue if ((receivers != null && receivers.size() > 0) || resultTo != null) { BroadcastQueue queue = broadcastQueueForIntent(intent); BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callingPid, callingUid, resolvedType, requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId); if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r + ": prev had " + queue.mOrderedBroadcasts.size()); if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST, "Enqueueing broadcast " + r.intent.getAction()); boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r); if (!replaced) { queue.enqueueOrderedBroadcastLocked(r); queue.scheduleBroadcastsLocked(); } } else { // There was nobody interested in the broadcast, but we still want to record // that it happened. if (intent.getComponent() == null && intent.getPackage() == null && (intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) { // This was an implicit broadcast... let's record it for posterity. addBroadcastStatLocked(intent.getAction(), callerPackage, 0, 0, 0); } } return ActivityManager.BROADCAST_SUCCESS;
3.0 整理mReceiver和registeredReceivers
NOTE:
- 其中receivers主要用于记录“有序递送”的receiver,
- 而registeredReceivers则用于记录与intent相匹配的动态注册的receiver。
关于这两个list的大致运作是这样的,我们先利用包管理器的queryIntentReceivers()接口,查询出和intent匹配的所有静态receivers,此时所返回的查询结果本身已经排好序了, 因此,该返回值被直接赋值给了receivers变量;
代码如下:
List<Object> receivers = AppGlobals.getPackageManager().queryIntentReceivers(intent, resolvedType, STOCK_PM_FLAGS, userId);
对于动态注册的receiver信息,就不是从包管理器获取了,这些信息本来就记录在AMS之中,此时只需调用:
List<BroadcastFilter> registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false, userId);
NOTE:
此时返回的registeredReceivers中的子项是没有经过排序的
而关于PKMS的queryIntentReceivers(),我们可以参考PKMS的专题文档,此处不再赘述。2
NOTE: 请注意图中BroadcastRecord节点所携带的节点链。
- mParallelBroadcasts表中,每个BroadcastRecord只可能携带BroadcastFilter,因为平行处理的节点只会对应动态receiver,
- 而所有静态receiver只能是串行处理的,另一方面,在mOrderedBroadcasts表中BroadcastRecord中则既可能携带BroadcastFilter,也可能携带ResolveInfo。这个其实很容易理解,
mReceiver解析
- 首先,ResolveInfo对应静态receiver,放到这里自不待言,
- 其次,如果用户在发送广播时明确指定要按ordered方式发送的话,那么即使目标方的receiver是动态注册的,它对应的BroadcastFilter也会被强制放到这里。
3.1 无Receiver接收此Intent,仅记录
好,现在让我们再整合一下思路。BroadcastRecord节点内部的receivers列表,记录着和这个广播动作相关的目标receiver信息,该列表内部的子节点可能是ResolveInfo类型的,也可能是BroadcastFilter类型的。
ResolveInfo是从PKMS处查到的静态receiver的描述信息,它的源头是PKMS分析的那些AndroidManifest.xml文件。
而BroadcastFilter事实上来自于本文一开始阐述动态receiver时,提到的AMS端的mRegisteredReceivers哈希映射表。3
现在,我们再画一张示意图:
3. 开始进入广播队列BroadcastQeueue
// registeredBroadcast 处理并行广播final BroadcastQueue queue = broadcastQueueForIntent(intent);BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callingPid, callingUid, resolvedType, requiredPermissions, appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId);queue.enqueueParallelBroadcastLocked(r);queue.scheduleBroadcastsLocked();// receivers, 处理串行广播BroadcastQueue queue = broadcastQueueForIntent(intent);BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage, callingPid, callingUid, resolvedType, requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode, resultData, resultExtras, ordered, sticky, false, userId);queue.enqueueOrderedBroadcastLocked(r);queue.scheduleBroadcastsLocked();// BroadcastQueue.javapublic void scheduleBroadcastsLocked() { . . . . . . if (mBroadcastsScheduled) { return; } mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this)); mBroadcastsScheduled = true;}
3.1 scheduleBroadcastsLocked
如何解读最后一段代码?
1. 第一段只处理动态注册的广播(并且是不包含有序广播的); 处理完重置NR = 0且registeredReceiver = null
2. 开始处理静态注册,或动态注册的接收者
- 首先,将registeredReceivers 中优先级高的动态广播,添加到集合开始位置
- 其次,将剩余的广播全部加到集合尾部
- 最后,receives的顺序为:动态接收中优先级高的> 普通静态 > 其它动态接收者
思考:
1. 有序广播的有序接收是如何实现的,为何可以对有序广播进行abort操作?
2. 想下为何有些书上会说,某些广播的接收是无序的?
4. 总结
- 特殊Action处理
- 权限检查
- 根据intent收集对应的receivers
- 调用intent对应的BroadcastQueue分发广播,至此流程转入BroadcastQueue
- 参考品茗论道说广播(Broadcast内部机制讲解) tttps://my.oschina.net/youranhongcha/blog/226274 ↩
- 参考品茗论道说广播(Broadcast内部机制讲解) tttps://my.oschina.net/youranhongcha/blog/226274 ↩
- 参考品茗论道说广播(Broadcast内部机制讲解) tttps://my.oschina.net/youranhongcha/blog/226274 ↩
- [Android]Ams 广播发送原理(三)
- Android系统广播(2)--AMS端发送广播
- [Android]AMS广播注册(二)
- Android之广播三(发送自定义广播)
- Android AMS原理分析(1)
- Android AMS原理分析(2)
- 【Android】安卓学习笔记之广播(三)发送标准广播和有序广播
- Android之BroadcastReceiver(三):发送自定义广播
- Android发送广播的三种方式
- Android--广播接收者(发送有序广播)
- AmS内部原理(android内核学习记录)
- Android广播之发送自定义广播+本地广播(二)
- 豆瓣客户端(三)发送图文广播
- Android源码:AMS、PMS、WMS原理分析
- Android系统中的广播(Broadcast)机制注册注销发送原理总结
- Android中的广播机制(二)----- 发送广播
- Android---广播(Broadcast)---广播发送的过程分析
- Android入门:广播发送者与广播接收者(同步广播和有序广播)
- 相机围绕目标旋转
- 停止线程的三种方法
- CentOS 7 安装配置 NFS
- 详解Uncoide、ASCII、UTF-8
- OpenCV 1.0在VC6下安装与配置(附测试程序)
- [Android]Ams 广播发送原理(三)
- 背景图片铺满整个屏幕
- 从零到一学Maven——宏观把控
- 算法-寻找两个链表的第一个公共结点
- git
- java 分拣存储
- 辐射渐变
- CMake2:版本号配置与头文件生成
- Python中的递归函数及二分查找算法如何使用?