安卓rom源码分析

Broadcast机制源码详解-注册

2018-04-20  本文已影响3人  刘佳阔

1.功能详解

1.广播分类

1).Normal Broadcast:普通广播
此处将普通广播界定为:开发者自己定义的intent,以context.sendBroadcast_"AsUser"(intent, ...)形式。具体可以使用的方法有:
sendBroadcast(intent)/sendBroadcast(intent, receiverPermission)/sendBroadcastAsUser
(intent, userHandler)/sendBroadcastAsUser(intent, userHandler,receiverPermission)。
普通广播会被注册了的相应的感兴趣(intent-filter匹配)接收,且顺序是无序的。如果发送广播时有相应的权限要求,
BroadCastReceiver如果想要接收此广播,也需要有相应的权限。

2).System Broadcast: 系统广播
Android系统中内置了多个系统广播,只要涉及到手机的基本操作,基本上都会发出相应的系统广播。如:开启启动,网络状态改变,拍照,
屏幕关闭与开启,点亮不足等等。每个系统广播都具有特定的intent-filter,其中主要包括具体的action,系统广播发出后,
将被相应的BroadcastReceiver接收。系统广播在系统内部当特定事件发生时,有系统自动发出。

3)Ordered broadcast:有序广播
有序广播的有序广播中的“有序”是针对广播接收者而言的,指的是发送出去的广播被BroadcastReceiver按照先后循序接收。
同一时刻只会有一个广播接收器能够接收到这条广播
有序广播的定义过程与普通广播无异,只是其的主要发送方式变为:sendOrderedBroadcast(intent, receiverPermission, ...)。

对于有序广播,其主要特点总结如下:
1>多个具当前已经注册且有效的BroadcastReceiver接收有序广播时,是按照先后顺序接收的,先后顺序判定标准遵循为:
当前系统中所有有效的动态注册和静态注册的BroadcastReceiver按照priority属性值从大到小排序,对于具有相同的priority的动态广播
和静态广播,动态广播会排在前面。
2>先接收的BroadcastReceiver可以对此有序广播进行截断,使后面的BroadcastReceiver不再接收到此广播,也可以对广播进行修改,
使后面的BroadcastReceiver接收到广播后解析得到错误的参数值。当然,一般情况下,不建议对有序广播进行此类操作,
尤其是针对系统中的有序广播。
4)Sticky Broadcast:粘性广播(在 android 5.0/api 21中deprecated,不再推荐使用,相应的还有粘性有序广播,同样已经deprecated)。
既然已经deprecated,此处不再多做总结。

5)Local Broadcast:App应用内广播(此处的App应用以App应用进程为界)
由前文阐述可知,Android中的广播可以跨进程甚至跨App直接通信,且注册是exported对于有intent-filter的情况下默认值是true,
由此将可能出现安全隐患如下:
1.其他App可能会针对性的发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收到广播并处理;
2.其他App可以注册与当前App一致的intent-filter用于接收广播,获取广播具体信息。
无论哪种情形,这些安全隐患都确实是存在的。由此,最常见的增加安全性的方案是:
1.对于同一App内部发送和接收广播,将exported属性人为设置成false,使得非本App内部发出的此广播不被接收;
2.在广播发送和接收时,都增加上相应的permission,用于权限验证;
3.发送广播时,指定特定广播接收器所在的包名,具体是通过intent.setPackage(packageName)指定在,这样此广播将只会发送到此包中的
App内与之相匹配的有效广播接收器中。

App应用内广播可以理解成一种局部广播的形式,广播的发送者和接收者都同属于一个App。实际的业务需求中,App应用内广播可能需要用到。
同时,之所以使用应用内广播时,而不是使用全局广播的形式,更多的考虑到的是Android广播机制中的安全性问题。
系统提供LocalBroadcastManager 来发送应用内广播.
 对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册的ContextReceiver才有可能接收到
(静态注册或其他方式动态注册的ContextReceiver是接收不到的)。用法如下

//注册应用内广播接收器
  localBroadcastManager = LocalBroadcastManager.getInstance(this);
  localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);
//取消注册应用内广播接收器
  localBroadcastManager.unregisterReceiver(mBroadcastReceiver);
//发送应用内广播
  localBroadcastManager.sendBroadcast(intent);

2.广播的变化

1).Android5.0/API level 21开始粘滞广播和有序粘滞广播过期,以后不再建议使用;
2).”静态注册的广播接收器即使app已经退出,主要有相应的广播发出,依然可以接收到,但此种描述自Android 3.1开始有可能不再成立“
Android 3.1开始系统在Intent与广播相关的flag增加了参数,分别是FLAG_INCLUDE_STOPPED_PACKAGES
和FLAG_EXCLUDE_STOPPED_PACKAGES。

FLAG_INCLUDE_STOPPED_PACKAGES:包含已经停止的包(停止:即包所在的进程已经退出)
FLAG_EXCLUDE_STOPPED_PACKAGES:不包含已经停止的包

主要原因如下:
自Android3.1开始,系统本身则增加了对所有app当前是否处于运行状态的跟踪。在发送广播时,不管是什么广播类型,系统默认直接增加了值
为FLAG_EXCLUDE_STOPPED_PACKAGES的flag,导致即使是静态注册的广播接收器,对于其所在进程已经退出的app,同样无法接收到广播。

由此,对于系统广播,由于是系统内部直接发出,无法更改此intent flag值,因此,3.1开始对于静态注册的接收系统广播的BroadcastReceiver,
如果App进程已经退出,将不能接收到广播。

但是对于自定义的广播,可以通过复写此flag为FLAG_INCLUDE_STOPPED_PACKAGES,使得静态注册的BroadcastReceiver,
即使所在App进程已经退出,也能能接收到广播,并会启动应用进程,但此时的BroadcastReceiver是重新新建的

2.注册源代码解读

1.ContextImpl.registerReceiverInternal
首先registerReceiver会执行到ContextWrapper的registerReceiver方法.转而又执行到ContextImpl的registerReceiverInternal方法里.我们从这里开始看起

 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) {
             //为空表示默认为主线程
     //AMS并不是直接给广播接收者发送广播的,当广播到达应用程序进程的时候,
      //会被封装成一个Message,然后push到主线程消息队列中,然后才会给接
      //收者处理,你也可以指定一个处理的Handler,将onReceive()调度在非主线程执行。
                if (scheduler == null) {
//1/mMainThread就是ActivityThread,是我们主线程的处理类.这个scheduler就是主线程的Handler, mH对象.
                    scheduler = mMainThread.getHandler();
                }
//2.这里的IIntentReceiver  rd是对BroadcastReceiver 的一个转化.这个IIntentReceiver 继承Binder,
可以夸进程传输到ActivityManagerService进程.
                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 {
//3.这里就调用到了ActivityManagerService的registerReceiver方法.这是IPC过程.ActivityManagerNative.getDefault()
就是ActivityManagerService的本地代理
            return ActivityManagerNative.getDefault().registerReceiver(
                    mMainThread.getApplicationThread(), mBasePackageName,
                    rd, filter, broadcastPermission, userId);
        } catch (RemoteException e) {
            return null;
        }
    }

2.LoadedApk.getReceiverDispatcher
这是接着1.2的方法看,,LoadedApk这个类包含了当前加载的apk的主要的信息,其中成员变量mReceivers表就记录了所有动态注册的receiver。
ReceiverDispatcher把BroadcastReceiver和InnerReceiver联系起来.他们是一一对应的.
InnerReceiver的作用就是类似BroadcastReceiver在ams端的代理.InnerReceiver会把远程Ams的处理方式转发回来.通过消息队列交给BroadcastReceiver处理.而ReceiverDispatcher是把这两个类联系起来的.

private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
      = new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();
 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);//1. 这是上边定义的map集合.以每个Context为键.        
                if (map != null) {
                    rd = map.get(r);
                }
            }
            if (rd == null) {
//2.ReceiverDispatcher为空就新建一个,他里边包含InnerReceiver内部类,
                rd = new ReceiverDispatcher(r, context, handler,
                        instrumentation, registered);
                if (registered) {
                    if (map == null) {
//3.这里看出.BroadcastReceiver(广播接收者)和ReceiverDispatcher(广播分发者)以及InnerReceiver是一一对应的.
InnerReceiver传输到Ams进程,每个广播接收者对应一个广播分发者, 当AMS向app发送广播时会调用到app进程的广播分发者,
然后再将广播以message形式post到app的主线程,来执行onReceive()方法。
                        map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
                        mReceivers.put(context, map);
                    }
                    map.put(r, rd);
                }
            } else {
                rd.validate(context, handler);
            }
            rd.mForgotten = false;
            return rd.getIIntentReceiver();
        }
    }

3.ActivityManagerService.registerReceiver
这里就是远程的处理过程,主要是对调用者进程的检查.粘性广播的保存.再把普通广播存储起来,把广播对应的IntentFilter 转化为BroadcastFilter 存储起来.以便发送广播后进行查找
这里有几个重要的变量讲解一下.
final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<IBinder, ReceiverList>();

这个mRegisteredReceivers 里的key IBinder 就是之前的InnerReceiver,他继承自IIntentReceiver.Stub.也就是本地BroadcastReceiver在Ams的代理.用InnerReceiver.asBinder()作为key.value则是这个广播接收者对应的BroadcastFilter集合.而BroadcastFilter则是对IntentFilter的封装.

 public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
        enforceNotIsolatedCaller("registerReceiver");
        int callingUid; // Uid是用户ID Android中每个程序都有一个Uid
        int callingPid; //pid是进程id,每一个不同的程序都能有一个UId,但是一个应用里面可以有多个PId
        synchronized(this) {
            ProcessRecord callerApp = null;
            if (caller != null) { //1.由caller获取当前进程对象
                callerApp = getRecordForAppLocked(caller);
//2. 进程还没创建,直接抛出异常
                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 = this.handleIncomingUser(callingPid, callingUid, userId,
                    true, ALLOW_FULL_ONLY, "registerReceiver", callerPackage);

            List allSticky = null;

           //3.从actions中,先把粘性广播帅选出来,放进stickyIntents中
            Iterator actions = filter.actionsIterator();
            if (actions != null) {
                while (actions.hasNext()) {
                    String action = (String)actions.next();
                    allSticky = getStickiesLocked(action, filter, allSticky,
                            UserHandle.USER_ALL);
                    allSticky = getStickiesLocked(action, filter, allSticky,
                            UserHandle.getUserId(callingUid));
                }
            } else {
                allSticky = getStickiesLocked(null, filter, allSticky,
                        UserHandle.USER_ALL);
                allSticky = getStickiesLocked(null, filter, allSticky,
                        UserHandle.getUserId(callingUid));
            }

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

 
            if (receiver == null) {
                return sticky;
            }
//4.mRegisteredReceivers表存了所有动态注册的广播接收者对应的IntentFilter的集合,
     //由receiver作为key,获取到ReceiverList,为什么是ReceiverList,
    //而不是一个Receiver呢,因为一个BroadcastReceiver可能用来接收多个广播,会有多个
IntentFilter
            ReceiverList rl
                = (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());
            if (rl == null) {
                rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                        userId, receiver);
                if (rl.app != null) {
//5.rl.app就是上文创建的 ProcessRecord callerApp,这里的操作就是把ReceiverList保存到进程的receivers中
                    rl.app.receivers.add(rl);
                } else {
                    try {
                        receiver.asBinder().linkToDeath(rl, 0);
                    } catch (RemoteException e) {
                        return sticky;
                    }
                    rl.linkedToDeath = true;
                }
//这里把receiver和他对应的intentFilter建立了联系
                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);
            }  
//6.把IntentFilter封装成BroadcastFilter,并添加到接收者队列,这只适用于动态注册的广播.
            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                    permission, callingUid, userId);
            rl.add(bf);
            if (!bf.debugCheck()) {
                Slog.w(TAG, "==> For Dynamic broadast");
            }
//把BroadCast添加给解析器.
            mReceiverResolver.addFilter(bf);

 //如果是粘性广播,创建BroadcastRecord,并添加到
   //BroadcastQueue的并行广播队列(mParallelBroadcasts),这里不解读了.粘性广播已经废弃了.
            if (allSticky != null) {
                ArrayList receivers = new ArrayList();
                receivers.add(bf);
                int N = allSticky.size();
                for (int i=0; i<N; i++) {
                    Intent intent = (Intent)allSticky.get(i);
                    BroadcastQueue queue = broadcastQueueForIntent(intent);
                    BroadcastRecord r = new BroadcastRecord(queue, intent, null,
                            null, -1, -1, null, null, AppOpsManager.OP_NONE, receivers, null, 0,
                            null, null, false, true, true, -1);
                    queue.enqueueParallelBroadcastLocked(r);
                    queue.scheduleBroadcastsLocked();
                }
            }
            return sticky;
        }
    }
上一篇下一篇

猜你喜欢

热点阅读