Broadcast广播机制

一、概述

Broadcast机制的作用主要是为了实现事情一处发生,多处得到通知的效果。广播分为广播发送和广播接收两个过程,其中广播接收(BroadcastReceiver)是Android四大组件之一。
BroadcastReceiver分为两类:

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

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

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

广播在系统中以BroadcastRecord对象来记录。

二、注册广播

2.1 注册广播

广播注册,对于应用开发来说,往往是在Activity/Service中调用registerReceiver()方法,而Activity或Service都间接继承于Context抽象类,真正干活是交给ContextImpl类。另外调用getOuterContext()可获取最外层的调用者Activity或Service。

[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());
}

用户注册receiver时会把一个自定义的receiver对象作为第一个参数传入,用户的receiver都是继承于BroadcastReceiver的。
broadcastPermission拥有广播的权限控制,scheduler用于指定接收到广播时onRecive执行线程,当scheduler=null则默认代表在主线程中执行,这也是最常见的用法

[ContextImpl.java]

private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
        IntentFilter filter, String broadcastPermission,
        Handler scheduler, Context context) {
    IIntentReceiver rd = null;
    if (receiver != null) {
        if (mPackageInfo != null && context != null) {
            if (scheduler == null) {
                scheduler = mMainThread.getHandler();
            }
            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 {
        final Intent intent = ActivityManagerNative.getDefault().registerReceiver(                    mMainThread.getApplicationThread(), mBasePackageName,
                rd, filter, broadcastPermission, userId);
        if (intent != null) {
            intent.setExtrasClassLoader(getClassLoader());
            intent.prepareToEnterProcess();
        }
        return intent;
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

IIntentReceiver的对象rd在此处担当消息传递机制binder的实体,ReceiveDispatcher类用来管理IIntentReceiver对象。
registerReceiver()方法中参数mMainThread.getApplicationThread()返回的是ApplicationThread,这是Binder的Bn端,用于system_server进程与该进程的通信。

2.2 LoadedApk.getReceiverDispatcher

rd的获取调用到LoadedApk的getReceiverDispatcher()方法。

[ 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) {
            map = mReceivers.get(context);
            if (map != null) {
                rd = map.get(r);
            }
        }
        if (rd == null) {
            //当广播分发者为空,则创建ReceiverDispatcher
            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);
            }
        } else {
            //验证广播分发者的context、handler是否一致
            rd.validate(context, handler);
        }
        rd.mForgotten = false;
        return rd.getIIntentReceiver();
    }
}

不妨令BroadcastReceiver(广播接收者)为key,LoadedApk.ReceiverDispatcher(分发者)为value的ArrayMap 记为A。此处mReceivers是一个以Context为key,以A为value的ArrayMap。对于ReceiverDispatcher(广播分发者),当不存在时则创建一个。

2.3 创建ReceiverDispatcher

[LoadedApk.java]

static final class ReceiverDispatcher {
    final IIntentReceiver.Stub mIIntentReceiver;
    final BroadcastReceiver mReceiver;
    final Context mContext;
    final Handler mActivityThread;
    final Instrumentation mInstrumentation;
    final boolean mRegistered;
    final IntentReceiverLeaked mLocation;
    RuntimeException mUnregisterLocation;
    boolean mForgotten;
    ReceiverDispatcher(BroadcastReceiver receiver, Context context,
            Handler activityThread, Instrumentation instrumentation,
            boolean registered) {
        if (activityThread == null) {
            throw new NullPointerException("Handler must not be null");
        }

        mIIntentReceiver = new InnerReceiver(this, !registered);
        mReceiver = receiver;
        mContext = context;
        mActivityThread = activityThread;
        mInstrumentation = instrumentation;
        mRegistered = registered;
        mLocation = new IntentReceiverLeaked(null);
        mLocation.fillInStackTrace();
    }
...
}

构造方法中mActivityThread便是前面传递过来的当前主线程的Handler.

2.3.1 创建InnerReceiver

[LoadedApk.java]

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;
}
...
}

ReceiverDispatcher(广播分发者)有一个内部类InnerReceiver,该类继承于IIntentReceiver.Stub。显然,这是一个Binder服务端,广播分发者通过rd.getIIntentReceiver()可获取该Binder服务端对象InnerReceiver,用于Binder IPC通信。

2.4 AMP.registerReceiver

ContextImpl.java的registerReceiverInternal()方法最终调用到AMP中的registerReceiver()方法
[ ActivityManagerNative.java]

 public Intent registerReceiver(IApplicationThread caller, String packageName,
        IIntentReceiver receiver,
        IntentFilter filter, String perm, int userId) throws RemoteException
{
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IActivityManager.descriptor);
    data.writeStrongBinder(caller != null ? caller.asBinder() : null);
    data.writeString(packageName);
    data.writeStrongBinder(receiver != null ? receiver.asBinder() : null);
    filter.writeToParcel(data, 0);
    data.writeString(perm);
    data.writeInt(userId);
    mRemote.transact(REGISTER_RECEIVER_TRANSACTION, data, reply, 0);
    reply.readException();
    Intent intent = null;
    int haveIntent = reply.readInt();
    if (haveIntent != 0) {
        intent = Intent.CREATOR.createFromParcel(reply);
    }
    reply.recycle();
    data.recycle();
    return intent;
}

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

ReceiverList rl = (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());

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

这里有两个Binder服务端对象caller和receiver,都代表执行注册广播动作所在的进程. AMP通过Binder驱动将这些信息发送给system_server进程中的AMS对象,接下来进入AMS.registerReceiver。

2.5 AMS.registerReceiver

[ActivityManagerService.java]

    public Intent registerReceiver(IApplicationThread caller, String callerPackage,
        IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
    enforceNotIsolatedCaller("registerReceiver");
    ArrayList<Intent> stickyIntents = null;
    ProcessRecord callerApp = null;
    int callingUid;
    int callingPid;
    synchronized(this) {
        if (caller != null) {
            //从mLruProcesses查询调用者的进程信息
            callerApp = getRecordForAppLocked(caller);
            if (callerApp == null) {
                throw new SecurityException(
                        "Unable to find app for caller " + caller
                        + " (pid=" + Binder.getCallingPid()
                        + ") when registering receiver " + receiver);
            }
            if (callerApp.info.uid != Process.SYSTEM_UID &&
                    !callerApp.pkgList.containsKey(callerPackage) &&
                    !"android".equals(callerPackage)) {
                throw new SecurityException("Given caller package " + callerPackage
                        + " is not running in process " + callerApp);
            }
            callingUid = callerApp.info.uid;
            callingPid = callerApp.pid;
        } else {
            callerPackage = null;
            callingUid = Binder.getCallingUid();
            callingPid = Binder.getCallingPid();
        }

        userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
                ALLOW_FULL_ONLY, "registerReceiver", callerPackage);

        //获取IntentFilter中的actions. 这就是平时所需要监听的广播action
        Iterator<String> actions = filter.actionsIterator();
        if (actions == null) {
            ArrayList<String> noAction = new ArrayList<String>(1);
            noAction.add(null);
            actions = noAction.iterator();
        }

        // Collect stickies of users
        int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
        while (actions.hasNext()) {
            String action = actions.next();
            for (int id : userIds) {
                ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);
                if (stickies != null) {
                    ArrayList<Intent> intents = stickies.get(action);
                    if (intents != null) {
                        if (stickyIntents == null) {
                            stickyIntents = new ArrayList<Intent>();
                        }
                        stickyIntents.addAll(intents);
                    }
                }
            }
        }
    }

    ArrayList<Intent> allSticky = null;
    if (stickyIntents != null) {
        final ContentResolver resolver = mContext.getContentResolver();
        // Look for any matching sticky broadcasts...
        for (int i = 0, N = stickyIntents.size(); i < N; i++) {
            Intent intent = stickyIntents.get(i);
            // If intent has scheme "content", it will need to acccess
            // provider that needs to lock mProviderMap in ActivityThread
            // and also it may need to wait application response, so we
            // cannot lock ActivityManagerService here.
            if (filter.match(resolver, intent, true, TAG) >= 0) {
                if (allSticky == null) {
                    allSticky = new ArrayList<Intent>();
                }
                allSticky.add(intent);
            }
        }
    }

    // The first sticky in the list is returned directly back to the client.
    Intent sticky = allSticky != null ? allSticky.get(0) : null;
    if (receiver == null) {
        return sticky;
    }

    synchronized (this) {
        if (callerApp != null && (callerApp.thread == null
                || callerApp.thread.asBinder() != caller.asBinder())) {
            // Original caller already died
            return null;
        }
        ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
        if (rl == null) {
            rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                    userId, receiver);
            if (rl.app != null) {
                rl.app.receivers.add(rl);
            } else {
                try {
                    receiver.asBinder().linkToDeath(rl, 0);
                } catch (RemoteException e) {
                    return sticky;
                }
                rl.linkedToDeath = true;
            }
            mRegisteredReceivers.put(receiver.asBinder(), rl);
        } else if (rl.uid != callingUid) {
            throw new IllegalArgumentException(
                    "Receiver requested to register for uid " + callingUid
                    + " was previously registered for uid " + rl.uid);
        } else if (rl.pid != callingPid) {
            throw new IllegalArgumentException(
                    "Receiver requested to register for pid " + callingPid
                    + " was previously registered for pid " + rl.pid);
        } else if (rl.userId != userId) {
            throw new IllegalArgumentException(
                    "Receiver requested to register for user " + userId
                    + " was previously registered for user " + rl.userId);
        }
        BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                permission, callingUid, userId);
        rl.add(bf);
        mReceiverResolver.addFilter(bf);

        // Enqueue broadcasts for all existing stickies that match
        // this filter.
        if (allSticky != null) {
            ArrayList receivers = new ArrayList();
            receivers.add(bf);

            final int stickyCount = allSticky.size();
            for (int i = 0; i < stickyCount; i++) {
                Intent intent = allSticky.get(i);
                BroadcastQueue queue = broadcastQueueForIntent(intent);
                BroadcastRecord r = new BroadcastRecord(queue, intent, null,
                        null, -1, -1, null, null, AppOpsManager.OP_NONE, null, receivers,
                        null, 0, null, null, false, true, true, -1);
                queue.enqueueParallelBroadcastLocked(r);
                queue.scheduleBroadcastsLocked();
            }
        }

        return sticky;
    }
}

其中mRegisteredReceivers记录着所有已注册的广播,以receiver IBinder为key, ReceiverList为value为HashMap。

在BroadcastQueue中有两个广播队列mParallelBroadcasts,mOrderedBroadcasts,数据类型都为ArrayList:

mParallelBroadcasts:并行广播队列,可以立刻执行,而无需等待另一个广播运行完成,该队列只允许动态已注册的广播,从而避免发生同时拉起大量进程来执行广播,前台的和后台的广播分别位于独立的队列。
mOrderedBroadcasts:有序广播队列,同一时间只允许执行一个广播,该队列顶部的广播便是活动广播,其他广播必须等待该广播结束才能运行,也是独立区别前台的和后台的广播。

2.5.1 AMS.getRecordForAppLocked

[ActivityManagerService.java]

    final ProcessRecord getRecordForAppLocked(
        IApplicationThread thread) {
    if (thread == null) {
        return null;
    }

    int appIndex = getLRURecordIndexForAppLocked(thread);
    return appIndex >= 0 ? mLruProcesses.get(appIndex) : null;
}
private final int getLRURecordIndexForAppLocked(IApplicationThread thread) {
    IBinder threadBinder = thread.asBinder();
    // Find the application record.
    for (int i=mLruProcesses.size()-1; i>=0; i--) {
        ProcessRecord rec = mLruProcesses.get(i);
        if (rec.thread != null && rec.thread.asBinder() == threadBinder) {
            return i;
        }
    }
    return -1;
}

mLruProcesses数据类型为ArrayList<ProcessRecord>,而ProcessRecord对象有一个IApplicationThread字段,根据该字段查找出满足条件的ProcessRecord对象。

2.5.2 IntentFilter.match

[IntentFilter.java]

public final int match(ContentResolver resolver, Intent intent,
        boolean resolve, String logTag) {
    String type = resolve ? intent.resolveType(resolver) : intent.getType();
    return match(intent.getAction(), type, intent.getScheme(),
                 intent.getData(), intent.getCategories(), logTag);
}
public final int match(String action, String type, String scheme,
        Uri data, Set<String> categories, String logTag) {
    if (action != null && !matchAction(action)) {
        if (false) Log.v(
            logTag, "No matching action " + action + " for " + this);
        return NO_MATCH_ACTION;
    }

    int dataMatch = matchData(type, scheme, data);
    if (dataMatch < 0) {
        if (false) {
            if (dataMatch == NO_MATCH_TYPE) {
                Log.v(logTag, "No matching type " + type
                      + " for " + this);
            }
            if (dataMatch == NO_MATCH_DATA) {
                Log.v(logTag, "No matching scheme/path " + data
                      + " for " + this);
            }
        }
        return dataMatch;
    }
    String categoryMismatch = matchCategories(categories);
    if (categoryMismatch != null) {
        if (false) {
            Log.v(logTag, "No matching category " + categoryMismatch + " for " + this);
        }
        return NO_MATCH_CATEGORY;
    }

    // It would be nice to treat container activities as more
    // important than ones that can be embedded, but this is not the way...
    if (false) {
        if (categories != null) {
            dataMatch -= mCategories.size() - categories.size();
        }
    }

    return dataMatch;
}

该方法用于匹配发起的Intent数据是否匹配成功,匹配项共有4项action, type, data, category,任何一项匹配不成功都会失败。

2.5.3 AMS.broadcastQueueForIntent

[ActivityManagerService.java]

BroadcastQueue broadcastQueueForIntent(Intent intent) {
    final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
    if (DEBUG_BROADCAST_BACKGROUND) Slog.i(TAG_BROADCAST,
            "Broadcast intent " + intent + " on "
            + (isFg ? "foreground" : "background") + " queue");
    return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
}

broadcastQueueForIntent(Intent intent)通过判断intent.getFlags()是否包含FLAG_RECEIVER_FOREGROUND 来决定是前台或后台广播,进而返回相应的广播队列mFgBroadcastQueue或者mBgBroadcastQueue。

  • 当Intent的flags包含FLAG_RECEIVER_FOREGROUND,则返回mFgBroadcastQueue;
  • 当Intent的flags不包含FLAG_RECEIVER_FOREGROUND,则返回mBgBroadcastQueue;

2.6 注册广播总结

注册广播:

  1. 传递的参数为广播接收者BroadcastReceiver和Intent过滤条件IntentFilter;
  2. 创建对象LoadedApk.ReceiverDispatcher.InnerReceiver,该对象继承于IIntentReceiver.Stub;
  3. 通过AMS把当前进程的ApplicationThread和InnerReceiver对象的代理类,注册登记到system_server进程;
  4. 当广播receiver没有注册过,则创建广播接收者队列ReceiverList,该对象继承于ArrayList, 并添加到AMS.mRegisteredReceivers(已注册广播队列);
  5. 创建BroadcastFilter,并添加到AMS.mReceiverResolver;
  6. 将BroadcastFilter添加到该广播接收者的ReceiverList

另外,当注册的是Sticky广播:

  • 创建BroadcastRecord,并添加到BroadcastQueue的mParallelBroadcasts(并行广播队列),注册后调用AMS来尽快处理该广播。
  • 根据注册广播的Intent是否包含FLAG_RECEIVER_FOREGROUND,则mFgBroadcastQueue

广播注册完, 另一个操作便是在广播发送过程.

三、发送广播

发送广播是在Activity或Service中调用sendBroadcast()方法,而Activity或Service都间接继承于Context抽象类,真正干活是交给ContextImpl类。

3.1 sendBroadcast

[ContextImpl.java]

@Override
public void sendBroadcast(Intent intent) {
    warnIfCallingFromSystemProcess();
    String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
    try {
        intent.prepareToLeaveProcess(this);
        ActivityManagerNative.getDefault().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();
    }
}

3.2 AMP.broadcastIntent

[ActivityManagerNative.java]

public int broadcastIntent(IApplicationThread caller,
        Intent intent, String resolvedType, IIntentReceiver resultTo,
        int resultCode, String resultData, Bundle map,
        String[] requiredPermissions, int appOp, Bundle options, boolean serialized,
        boolean sticky, int userId) throws RemoteException
{
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IActivityManager.descriptor);
    data.writeStrongBinder(caller != null ? caller.asBinder() : null);
    intent.writeToParcel(data, 0);
    data.writeString(resolvedType);
    data.writeStrongBinder(resultTo != null ? resultTo.asBinder() : null);
    data.writeInt(resultCode);
    data.writeString(resultData);
    data.writeBundle(map);
    data.writeStringArray(requiredPermissions);
    data.writeInt(appOp);
    data.writeBundle(options);
    data.writeInt(serialized ? 1 : 0);
    data.writeInt(sticky ? 1 : 0);
    data.writeInt(userId);
    mRemote.transact(BROADCAST_INTENT_TRANSACTION, data, reply, 0);
    reply.readException();
    int res = reply.readInt();
    reply.recycle();
    data.recycle();
    return res;
}

3.3 AMS.broadcastIntent

[ActivityManagerService.java]

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();
        int res = broadcastIntentLocked(callerApp,
                callerApp != null ? callerApp.info.packageName : null,
                intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
                requiredPermissions, appOp, bOptions, serialized, sticky,
                callingPid, callingUid, userId);
        Binder.restoreCallingIdentity(origId);
        return res;
    }
}

broadcastIntent()方法有两个布尔参数serialized和sticky来共同决定是普通广播,有序广播,还是Sticky广播,参数如下:

类型 serialized sticky
sendBroadcast false false
sendOrderedBroadcast true false
sendStickyBroadcast false true

3.4 AMS.broadcastIntentLocked

[ActivityManagerService.java]

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 userId) {
//step1: 设置flag
//step2: 广播权限验证
//step3: 处理系统相关广播
//step4: 增加sticky广播
//step5: 查询receivers和registeredReceivers
//step6: 处理并行广播
//step7: 合并registeredReceivers到receivers
//step8: 处理串行广播
    return ActivityManager.BROADCAST_SUCCESS;
}

broadcastIntentLocked方法比较长,这里划分为8个部分来分别说明。

3.4.1 设置广播flag

intent = new Intent(intent);
//增加该flag,默认得,广播不会发送给已停止的APP
intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);

//当没有启动完成时,不允许启动新进程
if (!mProcessesReady && (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
}
//如果应用启动管理器正在被使用,则跳过广播
if (AppBootManager.IN_USING) {
boolean skip = AppBootManager.getInstance(null).
skipBroadcast(intent, callerPackage, callingPid, callingUid, callerApp);
if (skip) {
return ActivityManager.BROADCAST_SUCCESS;
}
}
userId = handleIncomingUser(callingPid, callingUid, userId,
true, ALLOW_NON_FULL, "broadcast", callerPackage);

//检查发送广播时用户状态,确认广播接收者不为所有应用且该应用不在运行,则跳过该广播并给出异常提示
if (userId != UserHandle.USER_ALL && !isUserRunningLocked(userId, false)) {
if ((callingUid != Process.SYSTEM_UID
|| (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0)
&& !Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {
return ActivityManager.BROADCAST_FAILED_USER_STOPPED;
}
}

这个过程最重要的工作是:

  • 添加flag=FLAG_EXCLUDE_STOPPED_PACKAGES,保证已停止app不会收到该广播;
  • 当系统还没有启动完成,则不允许启动新进程,即只有动态注册receiver才能接受广播
  • 当非USER_ALL广播且当前用户并没有处于Running的情况下,除非是系统升级广播或者关机广播,否则直接返回。

BroadcastReceiver还有其他flag,位于Intent.java常量:

FLAG_RECEIVER_REGISTERED_ONLY //只允许已注册receiver接收广播
FLAG_RECEIVER_REPLACE_PENDING //新广播会替代相同广播
FLAG_RECEIVER_FOREGROUND //只允许前台receiver接收广播
FLAG_RECEIVER_NO_ABORT //对于有序广播,先接收到的receiver无权抛弃广播
FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT //Boot完成之前,只允许已注册receiver接收广播
FLAG_RECEIVER_BOOT_UPGRADE //升级模式下,允许系统准备就绪前可以发送广播

3.4.2 广播权限验证

    BroadcastOptions brOptions = null;
    if (bOptions != null) {
        brOptions = new BroadcastOptions(bOptions);
        if (brOptions.getTemporaryAppWhitelistDuration() > 0) {
            //查看广播调用者是否被允许这样做,检查实际的真正调用者(并不是任何一个提供操作的调用者都是真正的PendingIntent),因为那才是真正的实际提供参数者
            if (checkComponentPermission(
                    android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
                    Binder.getCallingPid(), Binder.getCallingUid(), -1, true)
                    != PackageManager.PERMISSION_GRANTED) {
                throw new SecurityException(msg);
            }
        }
    }
    //验证受保护的广播仅是由系统code发送的,并且该系统code只能发送受保护的广播
    final String action = intent.getAction();
    final boolean isProtectedBroadcast;
    try {
        isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);
    } catch (RemoteException e) {
        return ActivityManager.BROADCAST_SUCCESS;
    }
    if (ParallelAppUtils.isParallelUser(userId)
            && Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action)) {
        userId = UserHandle.USER_ALL;
    }

    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;
    }
    //第一条安全检查是——停止非系统应用发送受保护的广播
    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.";
                Slog.w(TAG, msg);
                throw new SecurityException(msg);
            } else if (intent.getComponent() != null) {
               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);
            }
        }
    }

主要功能:

  • 对于callingAppId为SYSTEM_UID,PHONE_UID,SHELL_UID,BLUETOOTH_UID,NFC_UID之一或者callingUid == 0时都畅通无阻;
  • 否则当调用者进程为空 或者非persistent进程的情况下:
    • 当发送的是受保护广播mProtectedBroadcasts(只允许系统使用),则抛出异常;
    • 当action为ACTION_APPWIDGET_CONFIGURE时,虽然不希望该应用发送这种广播,处于兼容性考虑,限制该广播只允许发送给自己,否则抛出异常。

3.4.3 处理系统相关广播

final String action = intent.getAction();
if (action != null) {
switch (action) {
    case Intent.ACTION_UID_REMOVED: //uid移除
    case Intent.ACTION_PACKAGE_REMOVED: //package移除
    case Intent.ACTION_PACKAGE_ADDED: //增加package
    case Intent.ACTION_PACKAGE_CHANGED: //package改变

    case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE: //外部设备不可用
    case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE: //外部设备可用

    case Intent.ACTION_TIMEZONE_CHANGED: //时区改变,通知所有运行中的进程
    case Intent.ACTION_TIME_CHANGED: //时间改变,通知所有运行中的进程

    case Intent.ACTION_CLEAR_DNS_CACHE: //DNS缓存清空
    case Proxy.PROXY_CHANGE_ACTION: //网络代理改变
}
}

3.4.4 增加sticky广播

if (sticky) {
if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,
        callingPid, callingUid)
        != PackageManager.PERMISSION_GRANTED) {
    throw new SecurityException("");
}
if (requiredPermissions != null && requiredPermissions.length > 0) {
    return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION;
}

if (intent.getComponent() != null) {
   //当sticky广播发送给指定组件,则throw Exception
}
if (userId != UserHandle.USER_ALL) {
   //当该广播为非USER_ALL广播,且非跟USER_ALL广播出现冲突,则throw Exception
}

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))) {
        //替换已存在的sticky intent
        list.set(i, new Intent(intent));
        break;
    }
}
//新的intent追加到list
if (i >= stickiesCount) {
    list.add(new Intent(intent));
}
}

这个过程主要是将sticky广播增加到list,并放入mStickyBroadcasts里面。

3.4.5 查询receivers和registeredReceivers

    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};
    }
//查询谁会接收此广播
List receivers = null;
List<BroadcastFilter> registeredReceivers = null;
//当允许静态接收者处理该广播,则通过PKMS根据Intent查询相应的静态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 == 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 {
            registeredReceivers = mReceiverResolver.queryIntent(intent,
                    resolvedType, false, userId);
        }
    }
  • receivers:记录着匹配当前intent的所有静态注册广播接收者;
  • registeredReceivers:记录着匹配当前的所有动态注册的广播接收者。
  • 根据userId来决定广播是发送给全部的接收者,还是指定的userId;
  • mReceiverResolver是AMS的成员变量,记录着已注册的广播接收者的resolver。

AMS.collectReceiverComponents:

private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
        int callingUid, int[] users) {
    int pmFlags = STOCK_PM_FLAGS | MATCH_DEBUG_TRIAGED_MISSING;

    List<ResolveInfo> receivers = null;
    try {
        HashSet<ComponentName> singleUserReceivers = null;
        boolean scannedFirstReceivers = false;
        for (int user : users) {
            // Skip users that have Shell restrictions, with exception of always permitted
            // Shell broadcasts
            if (callingUid == Process.SHELL_UID
                    && mUserController.hasUserRestriction(
                            UserManager.DISALLOW_DEBUGGING_FEATURES, user)
                    && !isPermittedShellBroadcast(intent)) {
                continue;
            }
            List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
                    .queryIntentReceivers(intent, resolvedType, pmFlags, user).getList();
            if (user != UserHandle.USER_SYSTEM && newReceivers != null) {
                // If this is not the system user, we need to check for
                // any receivers that should be filtered out.
                for (int i=0; i<newReceivers.size(); i++) {
                    ResolveInfo ri = newReceivers.get(i);
                    if ((ri.activityInfo.flags&ActivityInfo.FLAG_SYSTEM_USER_ONLY) != 0) {
                        newReceivers.remove(i);
                        i--;
                    }
                }
            }
            if (newReceivers != null && newReceivers.size() == 0) {
                newReceivers = null;
            }
            if (receivers == null) {
                receivers = newReceivers;
            } else if (newReceivers != null) {
                // We need to concatenate the additional receivers
                // found with what we have do far.  This would be easy,
                // but we also need to de-dup any receivers that are
                // singleUser.
                if (!scannedFirstReceivers) {
                    // Collect any single user receivers we had already retrieved.
                    scannedFirstReceivers = true;
                    for (int i=0; i<receivers.size(); i++) {
                        ResolveInfo ri = receivers.get(i);
                        if ((ri.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
                            ComponentName cn = new ComponentName(
                                    ri.activityInfo.packageName, ri.activityInfo.name);
                            if (singleUserReceivers == null) {
                                singleUserReceivers = new HashSet<ComponentName>();
                            }
                            singleUserReceivers.add(cn);
                        }
                    }
                }
                // Add the new results to the existing results, tracking
                // and de-dupping single user receivers.
                for (int i=0; i<newReceivers.size(); i++) {
                    ResolveInfo ri = newReceivers.get(i);
                    if ((ri.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) {
                        ComponentName cn = new ComponentName(
                                ri.activityInfo.packageName, ri.activityInfo.name);
                        if (singleUserReceivers == null) {
                            singleUserReceivers = new HashSet<ComponentName>();
                        }
                        if (!singleUserReceivers.contains(cn)) {
                            singleUserReceivers.add(cn);
                            receivers.add(ri);
                        }
                    } else {
                        receivers.add(ri);
                    }
                }
            }
        }
    } catch (RemoteException ex) {
        // pm is in same process, this will never happen.
    }
    return receivers;
}

3.4.6 处理并行广播

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

    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);
        final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
        if (!replaced) {
            queue.enqueueParallelBroadcastLocked(r);
            queue.scheduleBroadcastsLocked();
        }
        registeredReceivers = null;
        NR = 0;
    }

3.4.7 合并registeredReceivers到receivers

    // 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);
        }
        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;
        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++;
    }

动态注册的registeredReceivers,全部合并都receivers,再统一按串行方式处理。

3.4.8 处理串行广播

    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);
        }
    }

3.5 小结

发送广播过程:

  1. 默认不发送给已停止(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES)的应用包;
  2. 处理各种PACKAGE,TIMEZONE等相关的系统广播;
  3. 当为粘性广播,则将sticky广播增加到list,并放入mStickyBroadcasts里面;
  4. 当广播的Intent没有设置FLAG_RECEIVER_REGISTERED_ONLY,则允许静态广播接收者来处理该广播; 创建BroadcastRecord对象,并将该对象加入到相应的广播队列, 然后调用BroadcastQueue的scheduleBroadcastsLocked ()方法来完成的不同广播处理:

处理方式:

  1. Sticky广播: 广播注册过程处理AMS.registerReceiver,开始处理粘性广播,见小节[2.5];

    • 创建BroadcastRecord对象;
    • 并添加到mParallelBroadcasts队列;
    • 然后执行queue.scheduleBroadcastsLocked;
  2. 并行广播: 广播发送过程处理,见小节[3.4.6]

    • 只有动态注册的mRegisteredReceivers才会并行处理;
    • 会创建BroadcastRecord对象;
    • 并添加到mParallelBroadcasts队列;
    • 然后执行queue.scheduleBroadcastsLocked;
  3. 串行广播: 广播发送广播处理,见小节[3.4.8]

    • 所有静态注册的receivers以及动态注册
    • mRegisteredReceivers合并到一张表处理;
    • 创建BroadcastRecord对象;
    • 并添加到mOrderedBroadcasts队列;
    • 然后执行queue.scheduleBroadcastsLocked;

可见不管哪种广播方式,接下来都会执行scheduleBroadcastsLocked方法来处理广播;

四、 处理广播

4.1 处理广播

在发送广播过程中会执行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;
}

在BroadcastQueue对象创建时,mHandler=new BroadcastHandler(handler.getLooper());那么此处交由mHandler的handleMessage来处理。

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

BroadcastHandler采用的是”ActivityManager”线程的Looper。

4.2 handleMessage

[ BroadcastQueue.java]

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case BROADCAST_INTENT_MSG: {
                if (DEBUG_BROADCAST) Slog.v(
                        TAG_BROADCAST, "Received BROADCAST_INTENT_MSG");
                processNextBroadcast(true);
            } break;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容