美文网首页
Broadcast机制源码详解-处理

Broadcast机制源码详解-处理

作者: 刘佳阔 | 来源:发表于2018-04-20 17:29 被阅读0次

    1.代码讲解.

    1.ContextImpl.sendBroadcast
    同之前一样.ContextWrapper把方法转发给ComtextImpl,

        public void sendBroadcast(Intent intent) {
            warnIfCallingFromSystemProcess();
    //1. 返回 intent的 MIME data type  
            String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
            try {
                intent.prepareToLeaveProcess();
    //2.直接调用ams的方法.
                ActivityManagerNative.getDefault().broadcastIntent(
                    mMainThread.getApplicationThread(), intent, resolvedType, null,
                    Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, false,
                    getUserId());
            } catch (RemoteException e) {
            }
        }
    

    2.ActivityManagerService.broadcastIntent

     public final int broadcastIntent(IApplicationThread caller,
                Intent intent, String resolvedType, IIntentReceiver resultTo,
                int resultCode, String resultData, Bundle map,
                String requiredPermission, int appOp, boolean serialized, boolean sticky, int userId) {
            enforceNotIsolatedCaller("broadcastIntent");//1.验证不是单独的进程
            synchronized(this) {
    //2.对intent的一些flag的验证
                intent = verifyBroadcastLocked(intent);
    //3.拿到进程的对象ProcessRecord 
                final ProcessRecord callerApp = getRecordForAppLocked(caller);
                final int callingPid = Binder.getCallingPid();
                final int callingUid = Binder.getCallingUid();
                final long origId = Binder.clearCallingIdentity();
    //4.调用自己的方法.
                int res = broadcastIntentLocked(callerApp,
                        callerApp != null ? callerApp.info.packageName : null,
                        intent, resolvedType, resultTo,
                        resultCode, resultData, map, requiredPermission, appOp, serialized, sticky,
                        callingPid, callingUid, userId);
                Binder.restoreCallingIdentity(origId);
                return res;
            }
        }
    

    3.Ams.broadcastIntentLocked
    主要功能
    //设置Flag
    //检查BroadcastOptions
    //当前是否有权力发出广播
    //处理系统相关广播
    //处理粘性广播
    //registeredReceivers和receivers查询
    // 处理并行广播
    //整理两个receiver列表
    // 处理串行广播

    private final int broadcastIntentLocked(ProcessRecord callerApp,
                String callerPackage, Intent intent, String resolvedType,
                IIntentReceiver resultTo, int resultCode, String resultData,
                Bundle map, String requiredPermission, int appOp,
                boolean ordered, boolean sticky, int callingPid, int callingUid,
                int userId) {
            intent = new Intent(intent);
    //1.这句很重要,默认的广播,不发给进程关闭的app,以前可以监听广播来唤醒自己.安卓改版后加了这标记就不能实现了.
    如果加的是FLAG_INCLUDE_STOPPED_PACKAGES 就可以启动已死亡app
            // By default broadcasts do not go to stopped apps.
            intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
    
            userId = handleIncomingUser(callingPid, callingUid, userId,
                    true, ALLOW_NON_FULL, "broadcast", callerPackage);
     
    //2.如果进程没启动,除非是系统升级广播或者关机广播,否则直接返回跳过这个进程,不给他发送广播.
            if (userId != UserHandle.USER_ALL && mStartedUsers.get(userId) == null) {
                if (callingUid != Process.SYSTEM_UID || (intent.getFlags()
                        & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {
                    return ActivityManager.BROADCAST_SUCCESS;
                }
            }
    
     //3.阻止非系统进程发送受保护的广播
            int callingAppId = UserHandle.getAppId(callingUid);
            if (callingAppId == Process.SYSTEM_UID || callingAppId == Process.PHONE_UID
                || callingAppId == Process.SHELL_UID || callingAppId == Process.BLUETOOTH_UID
                || callingAppId == Process.NFC_UID || callingUid == 0) {
                // Always okay.
            } else if (callerApp == null || !callerApp.persistent) {
                try {
                    if (AppGlobals.getPackageManager().isProtectedBroadcast(
                            intent.getAction())) {
                        String msg = "Permission Denial: not allowed to send broadcast "
                                + intent.getAction() + " from pid="
                                + callingPid + ", uid=" + callingUid;
                        Slog.w(TAG, msg);
                        throw new SecurityException(msg);
                    } else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(intent.getAction())) {
                        // Special case for compatibility: we don't want apps to send this,
                        // but historically it has not been protected and apps may be using it
                        // to poke their own app widget.  So, instead of making it protected,
                        // just limit it to the caller.
                        if (callerApp == null) {
                            String msg = "Permission Denial: not allowed to send broadcast "
                                    + intent.getAction() + " from unknown caller.";
                            Slog.w(TAG, msg);
                            throw new SecurityException(msg);
                        } else if (intent.getComponent() != null) {
                            // They are good enough to send to an explicit component...  verify
                            // it is being sent to the calling app.
                            if (!intent.getComponent().getPackageName().equals(
                                    callerApp.info.packageName)) {
                                String msg = "Permission Denial: not allowed to send broadcast "
                                        + intent.getAction() + " to "
                                        + intent.getComponent().getPackageName() + " from "
                                        + callerApp.info.packageName;
                                Slog.w(TAG, msg);
                                throw new SecurityException(msg);
                            }
                        } else {
                            // Limit broadcast to their own package.
                            intent.setPackage(callerApp.info.packageName);
                        }
                    }
                } catch (RemoteException e) {
                    Slog.w(TAG, "Remote exception", e);
                    return ActivityManager.BROADCAST_SUCCESS;
                }
            }
    
    //4.处理特殊广播.如果是PackageManager发出的删除应用的广播,要把被删除应用的所有Activity移除出历史栈.
            final boolean uidRemoved = Intent.ACTION_UID_REMOVED.equals(
                    intent.getAction());
            if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())
                    || Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())
                    || Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())
                    || Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(intent.getAction())
                    || uidRemoved) {
                if (checkComponentPermission(
                        android.Manifest.permission.BROADCAST_PACKAGE_REMOVED,
                        callingPid, callingUid, -1, true)
                        == PackageManager.PERMISSION_GRANTED) {
                    if (uidRemoved) {
                        final Bundle intentExtras = intent.getExtras();
                        final int uid = intentExtras != null
                                ? intentExtras.getInt(Intent.EXTRA_UID) : -1;
                        if (uid >= 0) {
                            BatteryStatsImpl bs = mBatteryStatsService.getActiveStatistics();
                            synchronized (bs) {
                                bs.removeUidStatsLocked(uid);
                            }
                            mAppOpsService.uidRemoved(uid);
                        }
                    } else {
                       //5. 如果资源不可用,也要强制移除包信息.并刷新属性缓存
                        if 
    (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())) {
                            String list[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                            if (list != null && (list.length > 0)) {
                                for (String pkg : list) {
                                    forceStopPackageLocked(pkg, -1, false, true, true, false, false, userId,
                                            "storage unmount");
                                }
                                cleanupRecentTasksLocked(UserHandle.USER_ALL);
                                sendPackageBroadcastLocked(
                                        IApplicationThread.EXTERNAL_STORAGE_UNAVAILABLE, list, userId);
                            }
                        } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(
                                intent.getAction())) {
                            cleanupRecentTasksLocked(UserHandle.USER_ALL);
                        } else {
                            Uri data = intent.getData();
                            String ssp;
                            if (data != null && (ssp=data.getSchemeSpecificPart()) != null) {
                                boolean removed = Intent.ACTION_PACKAGE_REMOVED.equals(
                                        intent.getAction());
                                boolean fullUninstall = removed &&
                                        !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
                                if (!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false)) {
                                    forceStopPackageLocked(ssp, UserHandle.getAppId(
                                            intent.getIntExtra(Intent.EXTRA_UID, -1)), false, true, true,
                                            false, fullUninstall, userId,
                                            removed ? "pkg removed" : "pkg changed");
                                }
                                if (removed) {
                                    sendPackageBroadcastLocked(IApplicationThread.PACKAGE_REMOVED,
                                            new String[] {ssp}, userId);
                                    if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
                                        mAppOpsService.packageRemoved(
                                                intent.getIntExtra(Intent.EXTRA_UID, -1), ssp);
    
                                        // Remove all permissions granted from/to this package
                                        removeUriPermissionsForPackageLocked(ssp, userId, true);
                                    }
                                }
                            }
                        }
                    }
                } else {
                    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);
                }
    
            // Special case for adding a package: by default turn on compatibility
            // mode.
            } else if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) {
                Uri data = intent.getData();
                String ssp;
                if (data != null && (ssp=data.getSchemeSpecificPart()) != null) {
                    mCompatModePackages.handlePackageAddedLocked(ssp,
                            intent.getBooleanExtra(Intent.EXTRA_REPLACING, false));
                }
            }
    
     //6.如果是时区变化的广播.发送一个重设时区的message给所有正在运行的进程
        这往下几个都是对系统广播的处理.都是发送message来处理的.越过不看了
            if (Intent.ACTION_TIMEZONE_CHANGED.equals(intent.getAction())) {
                mHandler.sendEmptyMessage(UPDATE_TIME_ZONE);
            }
            if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
                final int is24Hour = intent.getBooleanExtra(
                        Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT, false) ? 1 : 0;
                mHandler.sendMessage(mHandler.obtainMessage(UPDATE_TIME, is24Hour, 0));
                BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
                synchronized (stats) {
                    stats.noteCurrentTimeChangedLocked();
                }
            }
    
            if (Intent.ACTION_CLEAR_DNS_CACHE.equals(intent.getAction())) {
                mHandler.sendEmptyMessage(CLEAR_DNS_CACHE_MSG);
            }
    
            if (Proxy.PROXY_CHANGE_ACTION.equals(intent.getAction())) {
                ProxyInfo proxy = intent.getParcelableExtra(Proxy.EXTRA_PROXY_INFO);
                mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG, proxy));
            }
    
    //7.处理粘性广播.
            if (sticky) {
    //8.需要android.Manifest.permission.BROADCAST_STICKY才能发送粘性广播
                if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,
                        callingPid, callingUid)
                        != PackageManager.PERMISSION_GRANTED) {
                    throw new SecurityException(msg);
                }
       //9.发送粘性广播不能强制添加别的权限
                if (requiredPermission != null) {
                    Slog.w(TAG, "Can't broadcast sticky intent " + intent
                            + " and enforce permission " + requiredPermission);
                    return ActivityManager.BROADCAST_STICKY_CANT_HAVE_PERMISSION;
                }
      //10.粘性广播也不能指定特定的组件名称
                if (intent.getComponent() != null) {
                    throw new SecurityException(
                            "Sticky broadcasts can't target a specific component");
                }
                // We use userId directly here, since the "all" target is maintained
                // as a separate set of sticky broadcasts.
                if (userId != UserHandle.USER_ALL) {
                    // But first, if this is not a broadcast to all users, then
                    // make sure it doesn't conflict with an existing broadcast to
                    // all users.
                    ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(
                            UserHandle.USER_ALL);
                    if (stickies != null) {
                        ArrayList<Intent> list = stickies.get(intent.getAction());
                        if (list != null) {
                            int N = list.size();
                            int i;
                            for (i=0; i<N; i++) {
                                if (intent.filterEquals(list.get(i))) {
                                    throw new IllegalArgumentException(
                                            "Sticky broadcast " + intent + " for user "
                                            + userId + " conflicts with existing global broadcast");
                                }
                            }
                        }
                    }
                }
                ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
                if (stickies == null) {
                    stickies = new ArrayMap<String, ArrayList<Intent>>();
                    mStickyBroadcasts.put(userId, stickies);
                }
    //11.广播是使用Intent描述的,用action取出广播列表
                ArrayList<Intent> list = stickies.get(intent.getAction());
                if (list == null) {
                    list = new ArrayList<Intent>();
                    stickies.put(intent.getAction(), list);
                }
                int N = list.size();
                int i;
      //12.粘性广播发送后是会保存下来的,故如果已经存在则不需要重新发送  ,
    // filterEquals函数会比较两个intent的action、data、type、class以及categories等信息,
                for (i=0; i<N; i++) {
                    if (intent.filterEquals(list.get(i))) {
                        // This sticky already exists, replace it.
                        list.set(i, new Intent(intent));
                        break;
                    }
                }
                if (i >= N) {
                    list.add(new Intent(intent));
                }
            }
    
            int[] users;//13.看是想发个所有用户还是发给特定用户
            if (userId == UserHandle.USER_ALL) {
                // Caller wants broadcast to go to all started users.
                users = mStartedUserArray;
            } else {
                // Caller wants broadcast to go to one specific user.
                users = new int[] {userId};
            }
    
            // Figure out who all will receive this broadcast.
            List receivers = null;
            List<BroadcastFilter> registeredReceivers = null;
     //14 为了处理“串行广播”和“并行广播”,broadcastIntentLocked()方法中搞了两个list,一个是receivers,另一个是registeredReceivers,
    registeredReceivers是动态广播接收器列表 ,receivers是静态广播接收器列表 。
    collectReceiverComponents内部调用包管理器的queryIntentReceivers()接口,查询出和intent匹配的所有静态receivers,
    此时所返回的查询结果本身已经排好序了,因此,该返回值被直接赋值给了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
                    UserManagerService ums = getUserManagerLocked();
                    for (int i = 0; i < users.length; i++) {
                        if (ums.hasUserRestriction(
                                UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {
                            continue;
                        }
    //15.得到动态注册的接收器列表,这个列表是没有排序的.
                        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);
                }
            }
    
            final boolean replacePending =
                    (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
            
       //16.处理并行广播(无序广播)和串行广播(有序广播)
            int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
    //17.参数ordered标记当前发送的广播是否是有序广播,可以看到如果发送的是无序广播,进入的是并行广播队列
            if (!ordered && NR > 0) {
     //18.构建广播队列,封装广播实体类BroadcastRecord 
                final BroadcastQueue queue = broadcastQueueForIntent(intent);
                BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                        callerPackage, callingPid, callingUid, resolvedType, requiredPermission,
                        appOp, registeredReceivers, resultTo, resultCode, resultData, map,
                        ordered, sticky, false, userId);
            
                final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
                if (!replaced) {
    //19.把广播入栈并处理,这里的处理也是通过发送mgessage来实现的
                    queue.enqueueParallelBroadcastLocked(r);
                    queue.scheduleBroadcastsLocked();
                }
                registeredReceivers = null;
                NR = 0;
            }
    
            // Merge into one list.
            int ir = 0;
            if (receivers != null) { //这里是对静态广播的处理
    //20.组织监听新app安装这个广播来组织app自启动.
                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) { //  21.静态注册的广播是ResolveInfo类型 
                        curt = (ResolveInfo)receivers.get(it);
                    }
                    if (curr == null) {  //22 动态注册的广播是BroadcastFilter类型
                        curr = registeredReceivers.get(ir);
                    }
    //23. 如果动态注册广播接收者优先级高于等于静态广播接收者,则把动态注册的广播接收者插入到当前位置,  
     静态注册的广播接收者后移,所以同优先级动态注册的先于静态注册的接收到广播  
                    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;
                    }
                }
            }
    //24.把优先级低于所有静态注册广播接收者的动态广播接收者都追加到receivers列表中的末尾 
            while (ir < NR) {
                if (receivers == null) {
                    receivers = new ArrayList();
                }
                receivers.add(registeredReceivers.get(ir));
                ir++;
            }
    
            if ((receivers != null && receivers.size() > 0)
                    || resultTo != null) {
    //25.这里的操作和上文的无序广播类似了.得到广播队列,封装广播对象
                BroadcastQueue queue = broadcastQueueForIntent(intent);
                BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                        callerPackage, callingPid, callingUid, resolvedType,
                        requiredPermission, appOp, receivers, resultTo, resultCode,
                        resultData, map, ordered, sticky, false, userId);
                if (DEBUG_BROADCAST) Slog.v(
                        TAG, "Enqueueing ordered broadcast " + r
                        + ": prev had " + queue.mOrderedBroadcasts.size());
                if (DEBUG_BROADCAST) {
                    int seq = r.intent.getIntExtra("seq", -1);
                    Slog.i(TAG, "Enqueueing broadcast " + r.intent.getAction() + " seq=" + seq);
                }
                boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r); 
                if (!replaced) {
    //26.加入有序广播队列,同感handler发送有序广播.
                    queue.enqueueOrderedBroadcastLocked(r);
                    queue.scheduleBroadcastsLocked();
                }
            }
    
            return ActivityManager.BROADCAST_SUCCESS;
        }
    

    最后总结一下
    对于粘性广播是在注册过程处理的,创建BroadcastRecord对象;并添加到mParallelBroadcasts队列;
    然后执行queue.scheduleBroadcastsLocked进行处理

    对于无序广播(并行): 动态注册的广播会进入mRegisteredReceivers表,会创建BroadcastRecord对象,并添加到mParallelBroadcasts队列;然后执行queue.scheduleBroadcastsLocked;

    对于所有静态注册的广播和动态注册的有序广播会进入receivers表中(串行),会创建BroadcastRecord对象,并添加到mOrderedBroadcasts队列;然后执行queue.scheduleBroadcastsLocked

    相关文章

      网友评论

          本文标题:Broadcast机制源码详解-处理

          本文链接:https://www.haomeiwen.com/subject/rgnakftx.html