Android四大组件系列7 Broadcast广播机制(上)

一 概述

广播 (Broadcast) 机制用于进程或线程间通信,广播分为广播发送和广播接收两个过程,其中广播接收者 BroadcastReceiver 是 Android 四大组件之一。BroadcastReceiver 分为两类:

  • 静态广播接收者:通过 AndroidManifest.xml 的标签来声明的 BroadcastReceiver
  • 动态广播接收者:通过 AMS.registerReceiver() 方式注册的 BroadcastReceiver,动态注册更为灵活,可在不需要时动态取消注册

PS:动态 BroadcastReceiver 比较简单,静态的就麻烦一些了,因为在广播发送之时,静态 receiver 所从属的进程可能还没有启动呢,这就需要先启动新的进程,费时费力。另一方面,有些时候用户希望广播能够按照一定顺序发送,为此,Android 又搞出了 ordered broadcast 的概念。

从广播发送方式可分为三类:

  • 普通广播:通过 Context.sendBroadcast() 发送,并行处理
  • 有序广播:通过 Context.sendOrderedBroadcast() 发送,串行处理
  • Sticky 广播:通过 Context.sendStickyBroadcast() 发送

广播在系统中以 BroadcastRecord 对象来记录,该对象有几个时间相关的成员变量需要注意。

final class BroadcastRecord extends Binder {
    final ProcessRecord callerApp; // 广播发送者所在进程
    final String callerPackage; // 广播发送者所在包名
    // 包括动态注册的 BroadcastFilter 和静态注册的 ResolveInfo
    final List receivers;
    final int callingPid;   // 广播发送者 pid
    int nextReceiver;  // 下一个被执行的接收者
    IBinder receiver; // 当前正在处理的接收者
    int anrCount;   // 广播 ANR 次数

    long enqueueClockTime;  // 入队列时间,伴随着 scheduleBroadcastsLocked
    long dispatchTime;      // 分发时间
    long dispatchClockTime;// 分发时间,伴随着 deliverToRegisteredReceiverLocked
    long receiverTime;      // 接收时间(首次等于 dispatchClockTime)
    long finishTime;   // 广播完成时间,位于 addBroadcastToHistoryLocked 方法内
}

我们这一节主要分析广播的注册流程,相关代码路径如下:

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java
frameworks/base/services/core/java/com/android/server/am/BroadcastRecord.java

frameworks/base/core/java/android/content/BroadcastReceiver.java
frameworks/base/core/java/android/app/ContextImpl.java
frameworks/base/core/java/android/app/LoadedApk.java
frameworks/base/core/java/android/app/ActivityThread.java

二 动态BroadcastReceiver注册

2.1 ContextImpl.registerReceiver

frameworks/base/core/java/android/app/ContextImpl.java

@Override
public Intent registerReceiver(BroadcastReceiver receiver,
        IntentFilter filter) {
    return registerReceiver(receiver, filter, null, null);
}
 
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
        String broadcastPermission, Handler scheduler) {
    return registerReceiverInternal(receiver, getUserId(), filter,
            broadcastPermission, scheduler, getOuterContext(), 0);
}

注册之时,用户会把一个自定义的 receiver 对象作为第一个参数传入。当然,用户的 receiver 都是继承于 BroadcastReceiver 的。

其中 broadcastPermission 拥有广播的权限控制,scheduler 用于指定接收到广播时 onRecive 执行线程,当 scheduler=null 则默认代表在主线程中执行,这也是最常见的用法。

使用过广播机制的同学,对这个 BroadcastReceiver 应该都不陌生,这里就不多说了。我们需要关心的是,这个 registerReceiverInternal() 内部还包含了什么重要的细节。

2.2 ContextImpl.registerReceiverInternal

private Intent registerReceiverInternal(BroadcastReceiver receiver,
    int userId, IntentFilter filter, String broadcastPermission,
    Handler scheduler, Context context, int flags) {
    // 获取 InnerReceiver, 基本思路是从 mReceivers 映射表中查找和
    // BroadcastReceiver 一一对应的 ReceiverDispatcher
    // 进而获取 InnerReceiver. 如果没有则新建.
    IIntentReceiver rd = null;
    if (receiver != null) {
        if (mPackageInfo != null && context != null) {
            if (scheduler == null) {
                scheduler = mMainThread.getHandler();
            }
            // 查找和 context 对应的“子哈希表”里的 ReceiverDispatcher
            // 如果找不到,就重新 new 一个
            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 {
        // AMS.registerReceiver 注册广播.
        final Intent intent = ActivityManager.getService().registerReceiver(
                mMainThread.getApplicationThread(), mBasePackageName, rd, 
                filter, broadcastPermission, userId, flags);
        if (intent != null) {
            intent.setExtrasClassLoader(getClassLoader());
            intent.prepareToEnterProcess();
        }
        return intent;
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

ContextImpl.registerReceiver 过程:

1.根据 BroadcastReceiver 获取对应的 ReceiverDispatcher

  • 先根据 LoadedApk.getReceiverDispatcher,从 ArrayMap<BroadcastReceiver, ReceiverDispatcher> 映射表中得到和 BroadcastReceiver 对应的 ReceiverDispatcher
  • 如果 LoadedApk 为空,则新建 LoadedApk.ReceiverDispatcher

2.执行 ReceiverDispatcher.getIIntentReceiver() 得到 InnerReceiver (即 IIntentReceiver.Stub )
3.调用 AMS.registerReceiver 注册广播

请大家注意那个 rd 对象(IIntentReceiver)。我们知道,在 Android 架构中,广播动作最终其实都是由 AMS 递送出来的。AMS 利用 binder 机制,将语义传递给各个应用进程,应用进程再辗转调用到 receiver 的 onReceive(),完成这次广播。而此处的 rd 对象正是承担“语义传递工作“的 binder 实体。

为了管理这个重要的 binder 实体,Android 搞出了一个叫做 ReceiveDispatcher 的类。该类的定义截选如下:

2.3 LoadedApk.ReceiveDispatcher

LoadedApk.java

static final class ReceiverDispatcher {

        final static class InnerReceiver extends IIntentReceiver.Stub {
            final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;
            final LoadedApk.ReceiverDispatcher mStrongRef;

            InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {
                mDispatcher = 
                    new WeakReference<LoadedApk.ReceiverDispatcher>(rd);
                mStrongRef = strong ? rd : null;
            }

            // 核心回调函数
            @Override
            public void performReceive(Intent intent, int resultCode, 
                String data, Bundle extras, boolean ordered,
                boolean sticky, int sendingUser) {
                
                final LoadedApk.ReceiverDispatcher rd;
                if (intent == null) {
                    rd = null;
                } else {
                    rd = mDispatcher.get();
                }
                ......
                if (rd != null) {
                // 调用 LoadedApk.ReceiverDispatcher 的 performReceive 方法
                    rd.performReceive(intent, resultCode, data, extras,
                            ordered, sticky, sendingUser);
                } else {
                    ......
                }
            }
        }
     // 请注意这个域!它就是传到外面的rd
        final IIntentReceiver.Stub mIIntentReceiver;
        @UnsupportedAppUsage
        // 每一个 ReceiverDispatcher 对应一个 BroadcastReceiver
        final BroadcastReceiver mReceiver;
        final Context mContext;
        final Handler mActivityThread;
        final Instrumentation mInstrumentation;
        final boolean mRegistered;
        final IntentReceiverLeaked mLocation;
        RuntimeException mUnregisterLocation;
        boolean mForgotten;

        final class Args extends BroadcastReceiver.PendingResult {
            private Intent mCurIntent;
            private final boolean mOrdered;
            private boolean mDispatched;
            private boolean mRunCalled;

            public Args(Intent intent, int resultCode, ......) {
                super(resultCode, resultData, resultExtras, ......);
                mCurIntent = intent;
                mOrdered = ordered;
            }
// 这个 getRunnable 返回的 Runnable,会被发送到主线程的消息队列里执行
            public final Runnable getRunnable() {
                return () -> {
                    final BroadcastReceiver receiver = mReceiver;
                    final boolean ordered = mOrdered;
                    ......                    
                    try {
                        ClassLoader cl = mReceiver.getClass().getClassLoader();
                        intent.setExtrasClassLoader(cl);
                        intent.prepareToEnterProcess();
                        setExtrasClassLoader(cl);
                        receiver.setPendingResult(this);
                        // 关键调用:回调 receiver 的 onReceive 方法
                        receiver.onReceive(mContext, intent);
                    } catch (Exception e) {
                        ......
                    }

                    if (receiver.getPendingResult() != null) {
                        finish();
                    }
                };
            }
        }
        // ReceiverDispatcher 构造函数
        ReceiverDispatcher(BroadcastReceiver receiver, Context context,
                Handler activityThread, Instrumentation instrumentation,
                boolean registered) {
            if (activityThread == null) {
                throw new NullPointerException("Handler must not be null");
            }
        // 生成一个 InnerReceiver
            mIIntentReceiver = new InnerReceiver(this, !registered);
            mReceiver = receiver;
            mContext = context;
            mActivityThread = activityThread;
            mInstrumentation = instrumentation;
            mRegistered = registered;
            mLocation = new IntentReceiverLeaked(null);
            mLocation.fillInStackTrace();
        }
        ......
        // 返回这个 mIIntentReceiver
        IIntentReceiver getIIntentReceiver() {
            return mIIntentReceiver;
        }
        ......
        public void performReceive(Intent intent, int resultCode,
            String data, Bundle extras, boolean ordered, 
            boolean sticky, int sendingUser) {
            // 把参数封装到 Args 中
            final Args args = new Args(intent, resultCode, data, extras,
                ordered, sticky, sendingUser);
            ......
     // 通过 args 获取一个 Runnable,并把这个 Runnable post 到 主线程的消息队列
     // 其中的 mActivityThread 是一个 Handler,默认属于主线程
        if (intent == null || !mActivityThread.post(args.getRunnable())) {
            ......
        }
    }
}

这样看来,“动态注册的 BroadcastReceiver ” 和 “ ReceiverDispatcher 节点” 具有一一对应的关系。示意图如下:

在这里插入图片描述
一个应用里可能会注册多个动态 receiver,所以这种一一对应关系最好整理成表,这个表就位于 LoadedApk 中。前文 mPackageInfo.getReceiverDispatcher() 一句中的 mPackageInfo 就是 LoadedApk 对象。

在 Android 的架构里,应用进程里是用 LoadedApk 来对应一个 apk 的,进程里加载了多少个 apk,就会有多少 LoadedApk。每个 LoadedApk 里会有一张 “关于本 apk 动态注册的所有 receiver ” 的哈希表(mReceivers)。当然,在 LoadedApk 初创之时,这张表只是个空表。

mReceivers 表的定义如下:

private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> 
    mReceivers = new ArrayMap<>();

该表的 key 项是我们比较熟悉的 Context,也就是说可以是 Activity、Service 或 Application。而 value 项则是另一张“子哈希表”。这是个 “表中表” 的形式。

言下之意就是,每个 Context(比如一个 activity),是可以注册多个 receiver 的,这个很好理解。mReceivers 里的“子哈希表”的 key 值为 BroadcastReceiver,value 项为 ReceiverDispatcher,示意图如下:
在这里插入图片描述
接下来我们继续看 LoadedApk.getReceiverDispatcher

2.3.1 LoadedApk.getReceiverDispatcher

frameworks/base/core/java/android/app/LoadedApk.java

public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
        Context context, Handler handler,
        Instrumentation instrumentation, boolean registered) {
    synchronized (mReceivers) {
        LoadedApk.ReceiverDispatcher rd = null;
        ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
        if (registered) {
        // 这个 mReceivers 就是我们上节说的哈希表
            map = mReceivers.get(context);
            if (map != null) {
                rd = map.get(r);
            }
        }
        // 如果没有对应的 ReceiverDispatcher,那么就创建一个
        if (rd == null) {
            rd = new ReceiverDispatcher(r, context, handler,
                    instrumentation, registered);
            if (registered) {
                if (map == null) {
        map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
                    mReceivers.put(context, map);
                }
                map.put(r, rd);
            }
        }
        rd.mForgotten = false;
        return rd.getIIntentReceiver();
    }
}

ReceiverDispatcher 作用小结:

  • 它是负责将广播 Broadcast 派发给 BroadcastReceiver 执行的广播派发器
  • BroadcastReceiver 和 ReceiverDispatcher 是一一对应的,一个广播接收器对应一个广播派发器
  • 它主要是通过内部类 InnerReceiver 和 Args 执行广播派发过程

2.4 AMS.ReceiverList

我们继续看 registerReceiverInternal() 的后半部分,最终调用到了 AMS 的 registerReceiver。

try {
    final Intent intent = ActivityManager.getService().registerReceiver(
            mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
            broadcastPermission, userId, flags);
    if (intent != null) {
        intent.setExtrasClassLoader(getClassLoader());
        intent.prepareToEnterProcess();
    }
    return intent;
} catch (RemoteException e) {
    throw e.rethrowFromSystemServer();
}

registerReceiver() 函数的 filter 参数指明了 receiver 对哪些 intent 感兴趣。

对同一个 BroadcastReceiver 对象来说,可以注册多个感兴趣的 filter,就好像声明静态 receiver 时,也可以为一个 receiver 编写多个 <intent-filter> 一样。这些 IntentFilter 信息会汇总到 AMS 的 mRegisteredReceivers 表中。在 AMS 端,我们可以这样访问相应的汇总表:

ActivityManagerService.java

final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();

// 这里的 receiver 就是应用进程中传递过来的 IIntentReceiver
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());

其中的 receiver 参数为 IIntentReceiver 类型,正对应着 ReceiverDispatcher 中那个 binder 实体。也就是说,每个客户端的 ReceiverDispatcher,会对应 AMS 端的一个 ReceiverList。

ReceiverList 的定义截选如下:

ReceiverList.java

final class ReceiverList extends ArrayList<BroadcastFilter>
        implements IBinder.DeathRecipient {
    final ActivityManagerService owner;
    public final IIntentReceiver receiver;
    public final ProcessRecord app;
    public final int pid;
    public final int uid;
    public final int userId;
    BroadcastRecord curBroadcast = null;
    boolean linkedToDeath = false;

    String stringName;

    ReceiverList(ActivityManagerService _owner, ProcessRecord _app,
            int _pid, int _uid, int _userId, IIntentReceiver _receiver) {
        owner = _owner;
        receiver = _receiver;
        app = _app;
        pid = _pid;
        uid = _uid;
        userId = _userId;
    }
    ......
    public void binderDied() {
        linkedToDeath = false;
        owner.unregisterReceiver(receiver);
    }

    public boolean containsFilter(IntentFilter filter) {
        final int N = size();
        for (int i = 0; i < N; i++) {
            final BroadcastFilter f = get(i);
            if (IntentResolver.filterEquals(f, filter)) {
                return true;
            }
        }
        return false;
    }
    ......  
}

ReceiverList 继承于 ArrayList<BroadcastFilter>,而 BroadcastFilter 又继承于 IntentFilter,所以 ReceiverList 可以被理解为一个 IntentFilter 数组列表。

final class BroadcastFilter extends IntentFilter {
    // Back-pointer to the list this filter is in.
    final ReceiverList receiverList;
    final String packageName;
    final String requiredPermission;
    final int owningUid;
    final int owningUserId;
    final boolean instantApp;
    final boolean visibleToInstantApp;

    BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList,
            ......) {
        super(_filter);
        receiverList = _receiverList;
        packageName = _packageName;
        requiredPermission = _requiredPermission;
        ......
    }
    ......
}

现在,我们可以绘制一张完整一点儿的图:
在这里插入图片描述
这张图只画了一个用户进程,在实际的系统里当然会有很多用户进程了,不过其关系是大致统一的,所以我们不再重复绘制。

关于动态 receiver 的注册,我们就先说这么多。至于激发广播时,又会做什么动作,我们会在后文阐述,现在我们先接着说明和动态 receiver 相对的静态 receiver。

三 静态BroadcastReceiver

静态 receiver 是指那些在 AndroidManifest.xml 文件中声明的 receiver,它们的信息会在系统启动时,由 Package Manager Service(PKMS)解析并记录下来。以后,当 AMS 调用 PKMS 的接口来查询 “和 intent 匹配的组件” 时,PKMS 内部就会去查询当初记录下来的数据,并把结果返回 AMS。

有的同学认为静态 receiver 是常驻内存的,这种说法并不准确。因为常驻内存的只是静态 receiver 的描述性信息,并不是 receiver 实体本身。

在 PKMS 内部,会有一个针对 receiver 而设置的 Resolver(决策器),其示意图如下:
在这里插入图片描述
需要说明的是以上的示意图是之前的 android 版本实现的,在 android 10.0 中,PKMS 中的 mActivities,mReceivers,mServices 等相关组件已经被封装到了 ComponentResolver 中了。

ComponentResolver.java

private final ActivityIntentResolver mActivities = new ActivityIntentResolver();

private final ProviderIntentResolver mProviders = new ProviderIntentResolver();

private final ActivityIntentResolver mReceivers = new ActivityIntentResolver();

private final ServiceIntentResolver mServices = new ServiceIntentResolver();

3.1 ActivityIntentResolver

ComponentResolver.java # ActivityIntentResolver

private static final class ActivityIntentResolver
    extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> {
        @Override
     public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
          boolean defaultOnly, int userId) {
          ......
    }

   List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags,
             int userId) {
         ......
   }

    List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
        int flags, List<PackageParser.Activity> packageActivities, int userId) {
           ......
    }

   private void addActivity(PackageParser.Activity a, String type,
       List<PackageParser.ActivityIntentInfo> newIntents) {
            ......
   }

   private void removeActivity(PackageParser.Activity a, String type) {
            mActivities.remove(a.getComponentName());
            ......
   }

  ......

  // Keys are String (activity class name), values are Activity.
  private final ArrayMap<ComponentName, PackageParser.Activity> mActivities =
        new ArrayMap<>();
  private int mFlags;
}

3.2 IntentResolver

IntentResolver.java

public abstract class IntentResolver<F extends IntentFilter, R extends Object> {
    final private static String TAG = "IntentResolver";
    final private static boolean DEBUG = false;
    final private static boolean localLOGV = DEBUG || false;
    final private static boolean localVerificationLOGV = DEBUG || false;

    public void addFilter(F f) {
        ......
    }
    ......
    public ArrayList<F> findFilters(IntentFilter matching) {
        ......
    }

    public void removeFilter(F f) {
        removeFilterInternal(f);
        mFilters.remove(f);
    }
    ......
    private final ArrayMap<String, F[]> mActionToFilter = 
            new ArrayMap<String, F[]>();
    private final ArrayMap<String, F[]> mSchemeToFilter =
            new ArrayMap<String, F[]>();
}

IntentResolver 是对 IntentFilter 操作的封装。

3.3 PackageParser.ActivityIntentInfo

PackageParser.java # ActivityIntentInfo

public final static class ActivityIntentInfo extends IntentInfo {
        @UnsupportedAppUsage
        public Activity activity;

        public ActivityIntentInfo(Activity _activity) {
            activity = _activity;
        }
        ......
        public ActivityIntentInfo(Parcel in) {
            super(in);
        }
}

而 IntentInfo 又继承自 IntentFilter,代码如下:

PackageParser.java # IntentInfo

public static abstract class IntentInfo extends IntentFilter {
        ......
        @UnsupportedAppUsage
        protected IntentInfo() {
        }

        protected IntentInfo(Parcel dest) {
            super(dest);
            hasDefault = (dest.readInt() == 1);
            labelRes = dest.readInt();
            nonLocalizedLabel = dest.readCharSequence();
            icon = dest.readInt();
            logo = dest.readInt();
            banner = dest.readInt();
            preferred = dest.readInt();
        }
        ......
}

3.4 ResolveInfo

ResolveInfo.java

public class ResolveInfo implements Parcelable {
    private static final String TAG = "ResolveInfo";
    public ActivityInfo activityInfo;
    public ServiceInfo serviceInfo;
    public ProviderInfo providerInfo;
    public IntentFilter filter;
    public String resolvePackageName;
    
    ......
    public ResolveInfo(ResolveInfo orig) {
        activityInfo = orig.activityInfo;
        serviceInfo = orig.serviceInfo;
        providerInfo = orig.providerInfo;
        filter = orig.filter;
        priority = orig.priority;
        preferredOrder = orig.preferredOrder;
        match = orig.match;
        specificIndex = orig.specificIndex;
        labelRes = orig.labelRes;
        nonLocalizedLabel = orig.nonLocalizedLabel;
        icon = orig.icon;
        resolvePackageName = orig.resolvePackageName;
        noResourceId = orig.noResourceId;
        iconResourceId = orig.iconResourceId;
        system = orig.system;
        targetUserId = orig.targetUserId;
        handleAllWebDataURI = orig.handleAllWebDataURI;
        isInstantAppAvailable = orig.isInstantAppAvailable;
        instantAppAvailable = isInstantAppAvailable;
    }
    
    private ResolveInfo(Parcel source) {
        activityInfo = null;
        serviceInfo = null;
        providerInfo = null;
        switch (source.readInt()) {
            case 1:
                activityInfo = ActivityInfo.CREATOR.createFromParcel(source);
                break;
            case 2:
                serviceInfo = ServiceInfo.CREATOR.createFromParcel(source);
                break;
            case 3:
                providerInfo = ProviderInfo.CREATOR.createFromParcel(source);
                break;
            default:
                Slog.w(TAG, "Missing ComponentInfo!");
                break;
        }
        if (source.readInt() != 0) {
            filter = IntentFilter.CREATOR.createFromParcel(source);
        }
        priority = source.readInt();
        preferredOrder = source.readInt();
        match = source.readInt();
        specificIndex = source.readInt();
        labelRes = source.readInt();
        nonLocalizedLabel
                = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
        icon = source.readInt();
        resolvePackageName = source.readString();
        targetUserId = source.readInt();
        system = source.readInt() != 0;
        noResourceId = source.readInt() != 0;
        iconResourceId = source.readInt();
        handleAllWebDataURI = source.readInt() != 0;
        instantAppAvailable = isInstantAppAvailable = source.readInt() != 0;
    }
}

以上是涉及到到的各个核心数据结构,关于 PKMS 的查询动作的细节,可参考 PKMS 的相关文档。

目前我们只需知道,PKMS 向外界提供了 queryIntentReceivers(Intent intent, String resolvedType,…) 函数,该函数可以返回一个 ParceledListSlice<ResolveInfo> 列表。这个列表的所包含的信息为:有多少 receiver 对相关 Intent 的 Action 感兴趣。

总之就是,当系统发出一个广播时,PKMS 必须能够决策出,有多少静态 receiver 对这个广播感兴趣,而且这些 receiver 的信息分别又是什么。

关于 receiver 的注册动作,我们就先说这么多。下面我们来看看激发广播时的动作。

四 广播发送

大家常见的激发广播的函数有哪些呢?从 ContextImpl.java 文件中,我们可以看到一系列发送广播的接口,列举如下:

  • public void sendBroadcast(Intent intent)
  • public void sendBroadcast(Intent intent, String receiverPermission)
  • public void sendBroadcast(Intent intent, String receiverPermission, Bundle options)
  • public void sendOrderedBroadcast(Intent intent, String receiverPermission)
  • public void sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)
  • public void sendStickyBroadcast(Intent intent)
  • public void sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)

其中 sendBroadcast() 是最简单的发送广播的动作。而 sendOrderedBroadcast(),则是用来向系统发出有序广播 (Ordered broadcast) 的。这种有序广播对应的所有接收器只能按照一定的优先级顺序,依次接收 intent。这些优先级一般记录在 AndroidManifest.xml 文件中,具体位置在 <intent-filter> 元素的 android:priority 属性中,其数值越大表示优先级越高,取值范围为 -1000 到 1000。另外,有时候我们也可以调用 IntentFilter 对象的 setPriority() 方法来设置优先级。

对于有序广播而言,前面的接收者可以对接收到的广播 intent 进行处理,并将处理结果放置到广播 intent 中,然后传递给下一个接收者。需要注意的是,前面的接收者有权终止广播的进一步传播。也就是说,如果广播被前面的接收者终止了,那么后面的接收器就再也无法接收到广播了。

还有一个怪东西,叫做 sticky 广播,它又是什么呢?简单地说,sticky 广播可以保证 “在广播递送时尚未注册的 receiver”,一旦日后注册进系统,就能够马上接到 “错过” 的 sticky 广播。有关它的细节,我们在后文再说。

在发送方,我们熟悉的调用 sendBroadcast() 的代码片段如下:

4.1 ContextImpl.sendBroadcast

ContextImpl.java

public void sendBroadcast(Intent intent) {
        warnIfCallingFromSystemProcess();
        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
        try {
            intent.prepareToLeaveProcess(this);
            ActivityManager.getService().broadcastIntent(
                    mMainThread.getApplicationThread(), intent, resolvedType,
                    null, Activity.RESULT_OK, null, null, null, 
                    AppOpsManager.OP_NONE, null, false, false,
                    getUserId());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
}

通过 Binder 机制,跨进程调用 AMS 的 broadcastIntent()。

4.2 AMS.broadcastIntent

public final int broadcastIntent(IApplicationThread caller,
        Intent intent, String resolvedType, IIntentReceiver resultTo,
        int resultCode, String resultData, Bundle resultExtras,
        String[] requiredPermissions, int appOp, Bundle bOptions,
        boolean serialized, boolean sticky, int userId) {
    enforceNotIsolatedCaller("broadcastIntent");
    synchronized(this) {
        intent = verifyBroadcastLocked(intent);

        final ProcessRecord callerApp = getRecordForAppLocked(caller);
        final int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();

        final long origId = Binder.clearCallingIdentity();
        try {
            return broadcastIntentLocked(callerApp,
                    callerApp != null ? callerApp.info.packageName : null,
                    intent, resolvedType, resultTo, resultCode, resultData,
                    resultExtras,
                    requiredPermissions, appOp, bOptions, serialized, sticky,
                    callingPid, callingUid, callingUid, callingPid, userId);
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }
}

进而调用 broadcastIntentLocked。

4.3 AMS.broadcastIntentLocked

final int broadcastIntentLocked(ProcessRecord callerApp,
            String callerPackage, Intent intent, String resolvedType,
            IIntentReceiver resultTo, int resultCode, ......) {
        return broadcastIntentLocked(callerApp, callerPackage, intent, 
            resolvedType, resultTo, resultCode, resultData, resultExtras,
            requiredPermissions, appOp, bOptions, ordered, sticky, 
            callingPid, callingUid, realCallingUid, realCallingPid, userId,
            false /* allowBackgroundActivityStarts */);
}

继续调用 broadcastIntentLocked

final int broadcastIntentLocked(ProcessRecord callerApp,
            String callerPackage, Intent intent, String resolvedType,
            IIntentReceiver resultTo, int resultCode, String resultData,
            Bundle resultExtras, String[] requiredPermissions, int appOp,
            Bundle bOptions, boolean ordered, boolean sticky, int callingPid,
            int callingUid, int realCallingUid, int realCallingPid,
            int userId, boolean allowBackgroundActivityStarts) {
            
        intent = new Intent(intent);
        ......
       // 为 intent 添加 FLAG_EXCLUDE_STOPPED_PACKAGES 标记
        intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
        ......

        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 ROOT_UID:
            case SYSTEM_UID:
            case PHONE_UID:
            case BLUETOOTH_UID:
            case NFC_UID:
            case SE_UID:
            case NETWORK_STACK_UID:
                isCallerSystem = true;
                break;
            default:
                isCallerSystem = (callerApp != null) &&
                    callerApp.isPersistent();
                break;
        }

        if (!isCallerSystem) {
            if (isProtectedBroadcast) {
             String msg = "Permission Denial: not allowed to send broadcast "
                        + action + " from pid="
                        + callingPid + ", uid=" + callingUid;
                Slog.w(TAG, msg);
                throw new SecurityException(msg);

            } else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
                    || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
                if (callerPackage == null) {
                    ......
                } else if (intent.getComponent() != null) {
                    ......
                } else {
                    // Limit broadcast to their own package.
                    intent.setPackage(callerPackage);
                }
            }
        }

        ......

        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:                    
                    if (checkComponentPermission(
                            android.Manifest.permission.BROADCAST_PACKAGE_REMOVED,
                            callingPid, callingUid, -1, true)
                            != PackageManager.PERMISSION_GRANTED) {
                        String msg = "Permission Denial: " + intent.getAction()
                                + " broadcast from " + callerPackage + " (pid=" + callingPid
                                + ", uid=" + callingUid + ")"
                                + " requires "
                                + android.Manifest.permission.BROADCAST_PACKAGE_REMOVED;
                        Slog.w(TAG, msg);
                        throw new SecurityException(msg);
                    }
                    switch (action) {
                        case Intent.ACTION_UID_REMOVED:
                            ......
                        case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:
                            ......
                            break;
                        case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:
                            mAtmInternal.cleanupRecentTasksForUser(UserHandle.USER_ALL);
                            break;
                        case Intent.ACTION_PACKAGE_REMOVED:
                        case Intent.ACTION_PACKAGE_CHANGED:
                            ......
                            break;
                        case Intent.ACTION_PACKAGES_SUSPENDED:
                        case Intent.ACTION_PACKAGES_UNSUSPENDED:
                            ......
                            break;
                    }
                    break;
                case Intent.ACTION_PACKAGE_REPLACED:
                {
                    ......
                    break;
                }
                case Intent.ACTION_PACKAGE_ADDED:
                {
                    ......
                    break;
                }
                case Intent.ACTION_PACKAGE_DATA_CLEARED:
                {
                   ......
                    break;
                }
                case Intent.ACTION_TIMEZONE_CHANGED:
                    mHandler.sendEmptyMessage(UPDATE_TIME_ZONE);
                    break;
                case Intent.ACTION_TIME_CHANGED:......
                    break;
                case Intent.ACTION_CLEAR_DNS_CACHE:
                    mHandler.sendEmptyMessage(CLEAR_DNS_CACHE_MSG);
                    break;
                case Proxy.PROXY_CHANGE_ACTION:
                    mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG));
                    break;
                case android.hardware.Camera.ACTION_NEW_PICTURE:
                case android.hardware.Camera.ACTION_NEW_VIDEO:                    
                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                    break;
                case android.security.KeyChain.ACTION_TRUST_STORE_CHANGED:
                    mHandler.sendEmptyMessage(HANDLE_TRUST_STORAGE_UPDATE_MSG);
                    break;
                case "com.android.launcher.action.INSTALL_SHORTCUT":
                    Log.w(TAG, "Broadcast " + action
                            + " no longer supported. It will not be delivered.");
                    return ActivityManager.BROADCAST_SUCCESS;
                case Intent.ACTION_PRE_BOOT_COMPLETED:
                    timeoutExempt = true;
                    break;
            }
            ......
        }

        // 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;
            }
            if (intent.getComponent() != null) {
                throw new SecurityException(
                        "Sticky broadcasts can't target a specific component");
            }          
            if (userId != UserHandle.USER_ALL) {
                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");
                            }
                        }
                    }
                }
            }
            ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
            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));
            }
        }
        ......
        // 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) {
            receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
        }
        if (intent.getComponent() == null) {
            if (userId == UserHandle.USER_ALL && callingUid == 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 /*defaultOnly*/, users[i]);
                    if (registeredReceivers == null) {
                        registeredReceivers = registeredReceiversForUser;
                    } else if (registeredReceiversForUser != null) {
                        registeredReceivers.addAll(registeredReceiversForUser);
                    }
                }
            } else {
                registeredReceivers = mReceiverResolver.queryIntent(intent,
                        resolvedType, false /*defaultOnly*/, userId);
            }
        }

        final boolean replacePending =
                (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;

        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing broadcast: " + intent.getAction()
                + " replacePending=" + replacePending);

        int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
        if (!ordered && NR > 0) {
            if (isCallerSystem) {
                checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
                        isProtectedBroadcast, registeredReceivers);
            }
            final BroadcastQueue queue = broadcastQueueForIntent(intent);
            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                    callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
                    requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,
                    resultCode, resultData, resultExtras, ordered, sticky, false, userId,
                    allowBackgroundActivityStarts, timeoutExempt);
            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
            final boolean replaced = replacePending
                    && (queue.replaceParallelBroadcastLocked(r) != null);
            // Note: We assume resultTo is null for non-ordered broadcasts.
            if (!replaced) {
                queue.enqueueParallelBroadcastLocked(r);
                queue.scheduleBroadcastsLocked();
            }
            registeredReceivers = null;
            NR = 0;
        }

        // Merge into one list.
        int ir = 0;
        if (receivers != null) {
            ......
            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 (isCallerSystem) {
            checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
                    isProtectedBroadcast, receivers);
        }

        if ((receivers != null && receivers.size() > 0)
                || resultTo != null) {
            BroadcastQueue queue = broadcastQueueForIntent(intent);
            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                    callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
                    requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
                    resultData, resultExtras, ordered, sticky, false, userId,
                    allowBackgroundActivityStarts, timeoutExempt);

            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r);

            final BroadcastRecord oldRecord =
                    replacePending ? queue.replaceOrderedBroadcastLocked(r) : null;
            if (oldRecord != null) {
                // Replaced, fire the result-to receiver.
                if (oldRecord.resultTo != null) {
                    final BroadcastQueue oldQueue = broadcastQueueForIntent(oldRecord.intent);
                    try {
                        oldQueue.performReceiveLocked(oldRecord.callerApp, oldRecord.resultTo,
                                oldRecord.intent,
                                Activity.RESULT_CANCELED, null, null,
                                false, false, oldRecord.userId);
                    } catch (RemoteException e) {
                        Slog.w(TAG, "Failure ["
                                + queue.mQueueName + "] sending broadcast result of "
                                + intent, e);

                    }
                }
            } else {
                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;
    }

broadcastIntentLocked() 是广播发送的核心函数,内容很多,函数很长,其中主要考虑的是以下方面的技术细节:

首先,有些广播 intent 只能由具有特定权限的进程发送,而有些广播 intent 在发送之前需要做一些其他动作。当然,如果发送方进程是系统进程、phone 进程、shell 进程等具有 root 权限的进程,那么必然有权发出广播。

另外,有时候用户希望发送 sticky 广播,以便日后注册的 receiver 可以收到 “错过” 的 sticky 广播。要达到这个目的,系统必须在内部维护一张 sticky 广播表,在具体的实现中,AMS 会把 sticky 类型的广播 intent 加入到 mStickyBroadcasts 映射表中。

mStickyBroadcasts 是一张哈希映射表,其 key 值为 intent 的 action 字符串,value 值为 “与这个 action 对应的 intent 数组列表” 的引用。当我们发送 sticky 广播时,新的广播 intent 要么替换掉 intent 数组列表中的某项,要么作为一个新项被添加进数组列表,以备日后使用。

发送广播时,还需要考虑所发送的广播是否需要有序(ordered)递送。而且,receiver 本身又分为动态注册和静态声明的,这让我们面对的情况更加复杂。从目前的代码来看,静态 receiver 一直是按照有序方式递送的,而动态 receiver 则需要根据 ordered 参数的值,做不同的处理。当我们需要有序递送时,AMS 会把动态 receivers 和静态 receivers 合并到一张表中,这样才能依照 receiver 的优先级,做出正确的处理,此时动态 receivers 和静态 receivers 可能呈现一种交错顺序。

另一方面,有些广播是需要发给特定目标组件的,这个也要加以考虑。

现在我们来分析 broadcastIntentLocked() 函数。我们可以将其逻辑大致整理成以下几步:

  • 为 intent 添加 FLAG_EXCLUDE_STOPPED_PACKAGES 标记
  • 判断进程是否有权限发出广播
  • 处理和package相关的广播
  • 处理其他一些系统广播
  • 更新系统中的sticky列表
  • 查询和 intent 匹配的静态 receivers
  • 查询和 intent 匹配的动态 receivers
  • 向并行 receivers 递送广播
  • 整合(剩下的)并行 receivers,以及静态 receivers,形成一个串行 receivers 表
  • 逐个向串行 receivers 递送广播

4.3.1 为intent添加FLAG_EXCLUDE_STOPPED_PACKAGES标记

intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);

为什么 intent 要添加 FLAG_EXCLUDE_STOPPED_PACKAGES 标记呢?

原因是这样的,在 Android 3.1 之后,PKMS 加强了对 “处于停止状态的” 应用的管理。

  • 如果一个应用在安装后从来没有启动过
  • 或者这个应用已经被用户强制停止了

符合以上两个条件的应用,我们称这个应用处于停止状态(stopped state)。

为了达到精细调整的目的,Android 增加了2个 flag:FLAG_INCLUDE_STOPPED_PACKAGES 和 FLAG_EXCLUDE_STOPPED_PACKAGES,以此来表示 intent 是否要激活 “处于停止状态的” 应用。

从上面的 broadcastIntentLocked() 函数可以看到,在默认情况下,AMS 是不会把 intent 广播发给 “处于停止状态的” 应用的。

据说 Google 这样做是为了防止一些流氓软件或病毒干坏事。当然,如果广播的发起者认为自己的确需要广播到 “处于停止状态的” 应用的话,它可以让 intent 携带 INCLUDE_STOPPED_PACKAGES 标记,从这个标记的注释可以了解到,如果这两个标记同时设置的话,那么 INCLUDE 标记会 “取胜”,它会覆盖掉 framework 自动添加的 FLAG_EXCLUDE_STOPPED_PACKAGES 标记。

4.3.2 判断进程是否有权限发出广播

接着,broadcastIntentLocked() 会判断发送广播的进程是否有权限发出广播,代码截选如下:

 // Verify that protected broadcasts are only being sent by system code,
 // and that system code is only sending protected broadcasts.
        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 ROOT_UID:
            case SYSTEM_UID:
            case PHONE_UID:
            case BLUETOOTH_UID:
            case NFC_UID:
            case SE_UID:
            case NETWORK_STACK_UID:
                isCallerSystem = true;
                break;
            default:
                isCallerSystem = (callerApp != null) &&
                    callerApp.isPersistent();
                break;
        }

        if (!isCallerSystem) {
            if (isProtectedBroadcast) {
             String msg = "Permission Denial: not allowed to send broadcast "
                        + action + " from pid="
                        + callingPid + ", uid=" + callingUid;
                Slog.w(TAG, msg);
                throw new SecurityException(msg);

            } else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
                    || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
                if (callerPackage == null) {
                    String msg = "Permission Denial: not allowed to send broadcast "
                            + action + " from unknown caller.";
                    ......
                } else if (intent.getComponent() != null) {
                    ......
                } else {
                    // Limit broadcast to their own package.
                    intent.setPackage(callerPackage);
                }
            }
        }

如果发起方的 UID 为 SYSTEM_UID、PHONE_UID 或 BLUETOOTH_UID 等,或者发起方具有 root 权限,那么它一定有权限发送广播。

另外,还有一个 “保护性广播” 的概念,也要考虑进来。有些同学会询问 AndroidManifest.xml 中的一级标记 <protected-broadcast> 是什么意思。简单地说,Google 认为有一些广播是只能由系统发送,如果某个系统级 AndroidManifest.xml 中写了这个标记,那么在 PKMS 解析该文件时,就会把 “保护性广播” 标记中的名字(一般是 Action 字符串)记录下来。在系统运作起来之后,如果某个不具有系统权限的应用试图发送系统中的 “保护性广播”,那么到 AMS 的 broadcastIntentLocked() 处就会被拦住,AMS 会抛出异常,提示 “Permission Denial: not allowed to send broadcast”。

以下是 framework/base/core/res/AndroidManifest.xml 文件中部分保护性广播示例:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="android" coreApp="true" android:sharedUserId="android.uid.system"
    android:sharedUserLabel="@string/android_system_label">

    <!-- ================================================ -->
    <!-- Special broadcasts that only the system can send -->
    <!-- ================================================ -->
    <eat-comment />

    <protected-broadcast android:name="android.intent.action.SCREEN_OFF" />
    <protected-broadcast android:name="android.intent.action.SCREEN_ON" />
    <protected-broadcast android:name="android.intent.action.USER_PRESENT" />
    <protected-broadcast android:name="android.intent.action.TIME_SET" />
    <protected-broadcast android:name="android.intent.action.TIME_TICK" />
    <protected-broadcast android:name="android.intent.action.TIMEZONE_CHANGED" />
    ......
</manifest>

4.3.3 处理和package相关的广播

接下来需要处理一些系统级的 “Package 广播”,这些主要从 PKMS 处发来。

比如,当 PKMS 处理 APK 的添加、删除或改动时,一般会发出类似下面的广播:

  • ACTION_PACKAGE_ADDED
  • ACTION_PACKAGE_REMOVED
  • ACTION_PACKAGE_CHANGED
  • ACTION_PACKAGE_REPLACED 等广播。

AMS 必须确保发送 “包广播” 的发起方具有 BROADCAST_PACKAGE_REMOVED 权限,如果没有,那么 AMS 会抛出异常(SecurityException)。

接着,AMS 判断如果是某个用户 id 被删除了的话(Intent.ACTION_UID_REMOVED),那么必须把这件事通知给 “电池状态服务”。

另外,如果是 SD 卡等外部设备上的应用不可用了,这常常是因为卡被 unmount 了,此时 PKMS 会发出 Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE,而 AMS 则需要把 SD 卡上的所有包都强制停止,并立即发出另一个 “Package 广播”:EXTERNAL_STORAGE_UNAVAILABLE。

如果是某个外部包被删除或改动了(ACTION_PACKAGE_REMOVED / REPLACED),则要进一步判断 intent 里是否携带了 EXTRA_DONT_KILL_APP 额外数据,如果没有携带,说明需要立即强制结束 package,否则,不强制结束 package。看来有些应用即使在删除或改动了包后,还会在系统(内存)中保留下来并继续运行。另外,如果是删除包的话,此时要发出 PACKAGE_REMOVED 广播。

等等,以上只是简单列举了一些典型的包处理广播,还有很多针对其它的包广播的处理,再次不再赘述,同学们可以自行查看代码研究。

4.3.4 处理其他一些系统广播

broadcastIntentLocked() 不但要对 “Package 广播” 进行处理,还要关心其他一些系统广播。比如:

  • ACTION_TIMEZONE_CHANGED
  • ACTION_TIME_CHANGED
  • ACTION_CLEAR_DNS_CACHE
  • PROXY_CHANGE_ACTION

等等,感兴趣的同学可以自行研究这些广播的意义。

4.3.5 更新系统中的sticky列表

接着,broadcastIntentLocked() 中会判断当前发出的是否是 sticky 广播,如果是的话,必须把广播 intent 记录下来。

一开始会判断一下发起方是否具有发出 sticky 广播的权限,比如说要拥有 BROADCAST_STICKY 权限等等。判断合格后,broadcastIntentLocked() 会更新 AMS 里的一张表:mStickyBroadcasts,其处理代码如下:

// 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) {
       // sticky 广播不能 enforece permissions
       Slog.w(TAG, "Can't broadcast sticky intent " + intent
       + " and enforce permissions " + Arrays.toString(requiredPermissions));
        return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION;
     }
     // sticky 广播不能指定特定的目标组件
     if (intent.getComponent() != null) {
             throw new SecurityException(
          "Sticky broadcasts can't target a specific component");
     }
       
     // 如果用户 id 不是全局的, 也就是给特定用户发送的 sticky 广播
     // 检查这个广播是否和全局的 mStickyBroadcasts 列表中的广播有冲突    
     if (userId != UserHandle.USER_ALL) {
          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");
                    }
                }
             }
         }
      }
      // 真正开始更新 mStickyBroadcasts 列表信息
      ArrayMap<String, ArrayList<Intent>> stickies = 
          mStickyBroadcasts.get(userId);
          
      if (stickies == null) { // 空表的话就创建一个新的
            stickies = new ArrayMap<>();
            mStickyBroadcasts.put(userId, stickies);
      }
      
      ArrayList<Intent> list = stickies.get(intent.getAction());
      if (list == null) { // 表中没有对应的 action,则创建并添加
           list = new ArrayList<>();
           stickies.put(intent.getAction(), list);
      }
      final int stickiesCount = list.size();
      int i;
      // 遍历 action 对应 intent 列表,如果不存在则添加,如果存在则更新
      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));
       }
}

mStickyBroadcasts 的定义是这样的:

ActivityManagerService.java

final SparseArray<ArrayMap<String, ArrayList<Intent>>> mStickyBroadcasts =
            new SparseArray<ArrayMap<String, ArrayList<Intent>>>();

上面代码的 filterEquals() 函数会比较两个 intent 的 action、data、type、identifier、package、component 以及 categories 等信息,但不会比较 extra 数据。

如果两个 intent 的 action 是一样的,但其他信息不同,那么它们在 ArrayList<Intent> 中会被记成两个不同的 intent。而如果发现新发送的 intent 在 ArrayList 中已经有个 “相等的” 旧 intent 时,则会用新的替掉旧的。

以后,每当 AMS 收到注册新的动态 receiver 时,注册动作中都会遍历一下 mStickyBroadcast 表,看哪些 intent 可以和新 receiver 的 filter 匹配,只有匹配的 intent 才会递送给新 receiver,示意图如下:
在这里插入图片描述
图中通过动态注册的新 receiver 的 filter 只对 a1 和 a3 这两个 action 感兴趣,所以遍历时就不会考虑 mStickyBroadcast 表中的 a2 表项对应的子表,而 a1、a3 子表所对应的若干 intent 中又只有一部分可以和 filter 匹配,比如 a1 的 intent1 以及 a3 的 intent2,所以图中只选择了这两个 intent 递送给新 receiver。

除了更新 mStickyBoradcast 表的动作以外,sticky 广播和普通广播在 broadcastIntentLocked() 中的代码是一致的,并没有其他什么不同了。

4.3.6 向并行receivers递送广播

然后 broadcastIntentLocked() 会尝试向并行 receivers 递送广播。

此时会调用到 queue.scheduleBroadcastsLocked()。相关代码截选如下:

// Figure out who all will receive this broadcast.
// 先找出所有接收此广播的 receives 列表
   List receivers = null;
   List<BroadcastFilter> registeredReceivers = null;
   // Need to resolve the intent to interested receivers...
   if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
             == 0) {
        receivers = collectReceiverComponents(intent, 
            resolvedType, callingUid, users);
   }
    if (intent.getComponent() == null) {
         ......
         registeredReceivers = mReceiverResolver.queryIntent(intent,
                resolvedType, false /*defaultOnly*/, userId);
        ......
    }
    ......
    int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
    if (!ordered && NR > 0) { // 不是有序的广播
        
        if (isCallerSystem) {
            checkBroadcastFromSystem(intent, callerApp, callerPackage,
            callingUid, isProtectedBroadcast, registeredReceivers);
        }
     // 获取广播队列 BroadcastQueue,可能是前台广播队列,也可能是后台广播队列
        final BroadcastQueue queue = broadcastQueueForIntent(intent);
        // 构建广播的数据结构 BroadcastRecord
        BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
           callerPackage, callingPid, callingUid, ......);
           
       final boolean replaced = replacePending
             && (queue.replaceParallelBroadcastLocked(r) != null);
       // Note: We assume resultTo is null for non-ordered broadcasts.
       if (!replaced) {
           queue.enqueueParallelBroadcastLocked(r);
           // 关键部分:执行具体的广播的发送操作
           queue.scheduleBroadcastsLocked();
       }
       registeredReceivers = null;
       NR = 0;
    }

简单地说,就是 new 一个 BroadcastRecord 节点,并插入 BroadcastQueue 内的并行处理队列,最后发起实际的广播调度:scheduleBroadcastsLocked()。关于上面代码中的 registeredReceivers 列表,我们会在后文说明,这里先跳过。

其实不光并行处理部分需要一个 BroadcastRecord 节点,串行处理部分也需要 BroadcastRecord 节点。也就是说,要发送一次广播,AMS 必须构造一个或两个 BroadcastRecord 节点,并将之插入合适的广播队列(mFgBroadcastQueue 或 mBgBroadcastQueue)。插入成功后,再执行队列的 scheduleBroadcastsLocked() 动作,进行实际的派发调度。示意图如下:
在这里插入图片描述
请注意图中 BroadcastRecord 节点所携带的节点链。

  • 在 mParallelBroadcasts 表中,每个 BroadcastRecord 只可能携带 BroadcastFilter,因为平行处理的节点只会对应动态 receiver,而所有静态 receiver 只能是串行处理的
  • 另一方面,在 mOrderedBroadcasts 表中,BroadcastRecord 中则既可能携带 BroadcastFilter,也可能携带 ResolveInfo。这个其实很容易理解,首先,ResolveInfo 对应静态 receiver,放到这里自不待言,其次,如果用户在发送广播时明确指定要按 ordered 方式发送的话,那么即使目标方的 receiver 是动态注册的,它对应的 BroadcastFilter 也会被强制放到这里

现在让我们再整合一下思路,做下小结:

BroadcastRecord 节点内部的 receivers 列表,记录着和这个广播动作相关的目标 receiver 信息,该列表内部的子节点可能是 ResolveInfo 类型的,也可能是 BroadcastFilter 类型的。

ResolveInfo 是从 PKMS 处查到的静态 receiver 的描述信息,它的源头是 PKMS 分析的那些 AndroidManifest.xml 文件。

而 BroadcastFilter,来自于一开始阐述动态 receiver 时,提到的 AMS 端的 mRegisteredReceivers 哈希映射表。现在,我们再画一张示意图:
在这里插入图片描述
因为 BroadcastRecord 里的 BroadcastFilter,和 AMS 的 mRegisteredReceivers 表中(间接)所指的对应 BroadcastFilter 是同一个对象,所以我是用虚线将它们连起来的。

Ok,我们接着看 scheduleBroadcastsLocked() 动作。scheduleBroadcastsLocked() 的代码如下:

BroadcastQueue.java

public void scheduleBroadcastsLocked() {
        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
                + mQueueName + "]: current="
                + mBroadcastsScheduled);

        if (mBroadcastsScheduled) {
            return;
        }
        mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
        mBroadcastsScheduled = true;
    }

发出 BROADCAST_INTENT_MSG 消息。我们接下来看对这个消息的处理:

BroadcastQueue.java

private final class BroadcastHandler extends Handler {
        public BroadcastHandler(Looper looper) {
            super(looper, null, true);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case BROADCAST_INTENT_MSG: {
                    if (DEBUG_BROADCAST) Slog.v(
                            TAG_BROADCAST, "Received BROADCAST_INTENT_MSG ["
                            + mQueueName + "]");
                    processNextBroadcast(true);
                } break;
                case BROADCAST_TIMEOUT_MSG: {
                    synchronized (mService) {
                        broadcastTimeoutLocked(true);
                    }
                } break;
            }
        }
 }

也就是说,AMS 端会在 BroadcastQueue.java 中的 processNextBroadcast() 具体处理广播。我们在下一篇文章中重点分析这个函数。

4.3.7 整理两个receiver列表

我们前文已经说过,有些广播是需要有序递送的。为了合理处理 “有序递送” 和 “平行递送”,broadcastIntentLocked() 函数内部搞出了两个 list:

List receivers = null;
List<BroadcastFilter> registeredReceivers = null;

其中,receivers 主要用于记录 “有序递送” 的 receiver,而 registeredReceivers 则用于记录与 intent 相匹配的动态注册的 receiver。

关于这两个 list 的大致运作是这样的:我们先利用包管理器的 queryIntentReceivers() 接口,查询出和 intent 匹配的所有静态 receivers,此时所返回的查询结果本身已经排好序了,因此,该返回值被直接赋值给了 receivers 变量,代码如下:

    receivers = AppGlobals.getPackageManager()
      .queryIntentReceivers(intent, resolvedType, pmFlags, user).getList();

而对于动态注册的 receiver 信息,就不是从包管理器获取了,这些信息本来就记录在 AMS 之中,此时只需调用:

      registeredReceivers = mReceiverResolver.queryIntent(intent,
            resolvedType, false /*defaultOnly*/, userId);

就可以了。注意,此时返回的 registeredReceivers 中的子项是没有经过排序的。而关于 PKMS 的 queryIntentReceivers(),我们可以参考 PKMS 的专题文档,此处不再赘述。

  • 如果是 “并行递送” 广播, registeredReceivers 中的各个 receiver 会在 scheduleBroadcastsLocked() 函数中被并行处理掉
  • 大家回头看看向并行 receivers 发送广播的代码,会发现在调用完 scheduleBroadcastsLocked() 后,registeredReceivers 会被强制赋值成 null 值
  • 如果是 “串行递送” 广播,那么必须考虑把 registeredReceivers 表合并到 receivers 表中去
  • 我们知道,receivers 列表中只记录了一些静态 receiver,这些 receiver 将会被 “有序递送”。现在我们只需再遍历一下 registeredReceivers 列表,并将其中的每个子项插入到 receivers 列表的合适地方,就可以合并出一条顺序列表了。当然,如果 registeredReceivers 已经被设为 null 了,就无所谓合并了

为什么静态声明的 receiver 只会 “有序递送” 呢?我想也许和这种 receiver 的复杂性有关系,因为在需要递送广播时,receiver 所属的进程可能还没有启动呢,所以也许会涉及到启动进程的流程,这些都是比较复杂的流程。

当然,上面所说的是没有明确指定目标组件的情况,如果 intent 里含有明确的目标信息,那么就不需要调用包管理器的 queryIntentReceivers() 了,只需 new 一个 ArrayList,并赋值给 receivers,然后把目标组件对应的 ResolveInfo 信息添加进 receivers 数组列表即可。

4.3.8 逐个向串行receivers递送广播

当 receivers 列表整理完毕之后,现在要开始尝试逐个向串行 receivers 递送广播了。正如前文所说,这里要重新 new 一个新的 BroadcastRecord 节点:

if ((receivers != null && receivers.size() > 0)
       || resultTo != null) {
    BroadcastQueue queue = broadcastQueueForIntent(intent);
    BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
        callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
        requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
        resultData, resultExtras, ordered, sticky, false, userId,
        allowBackgroundActivityStarts, timeoutExempt);

      if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
       "Enqueueing ordered broadcast " + r);

      final BroadcastRecord oldRecord =
             replacePending ? queue.replaceOrderedBroadcastLocked(r) : null;
      if (oldRecord != null) {
          // Replaced, fire the result-to receiver.
          if (oldRecord.resultTo != null) {
               final BroadcastQueue oldQueue =
                    broadcastQueueForIntent(oldRecord.intent);
               try {
                  oldQueue.performReceiveLocked(oldRecord.callerApp,
                      oldRecord.resultTo, oldRecord.intent,
                      Activity.RESULT_CANCELED, null, null,
                      false, false, oldRecord.userId);
                } catch (RemoteException e) {
                    Slog.w(TAG, "Failure ["
                     + queue.mQueueName + "] sending broadcast result of "
                     + intent, e);
                }
          }
      } else {
         queue.enqueueOrderedBroadcastLocked(r);
         queue.scheduleBroadcastsLocked();
     }
}

而 scheduleBroadcastsLocked() 最终会间接导致走到 BroadcastQueue.java 中的 processNextBroadcast()。这一点和前文所说的 “向并行 receivers 递送广播” 的动作基本一致。我们在下一篇文章继续详细分析 processNextBroadcast() 函数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值