[Android]Ams 广播发送原理(三)

[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> stickies = mStickyBroadcasts.get(
                 UserHandle.USER_ALL);
         if (stickies != null) {
             ArrayList list = stickies.get(intent.getAction());
             if (list != null) {
                 int N = list.size();
                 int i;
                 for (i=0; iif (intent.filterEquals(list.get(i))) {
                         throw new IllegalArgumentException(
                                 "Sticky broadcast " + intent + " for user "
                                 + userId + " conflicts with existing global broadcast");
                     }
                 }
             }
         }
     }
     // 所有的StickyBroadcast都会保存在这个集合里面
     ArrayMap> stickies = mStickyBroadcasts.get(userId);

     // 第一次发sticky广播时,这个userId对象的ArrayMap肯定是空,并且将其保存起来
     if (stickies == null) {
         stickies = new ArrayMap<>();
         mStickyBroadcasts.put(userId, stickies);
     }
     ArrayList 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; // 存储静态注册的receiver
List 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 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; itget(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 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.java
public 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 ↩

你可能感兴趣的:(Android广播)