[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
    • 总结

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广播数据结构

image

// 判断该广播是发给指定用户还是所有用户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

image

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

现在,我们再画一张示意图:

image

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

  1. 参考品茗论道说广播(Broadcast内部机制讲解) tttps://my.oschina.net/youranhongcha/blog/226274 ↩
  2. 参考品茗论道说广播(Broadcast内部机制讲解) tttps://my.oschina.net/youranhongcha/blog/226274 ↩
  3. 参考品茗论道说广播(Broadcast内部机制讲解) tttps://my.oschina.net/youranhongcha/blog/226274 ↩