100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > Android 广播实现原理解析

Android 广播实现原理解析

时间:2022-01-17 11:37:25

相关推荐

Android 广播实现原理解析

Android 广播实现原理解析

前言

Android四大组件中的BroadcastReceiver,在我们实际工作中被频繁的使用,我们可以利用系统的开机广播,网络状态改变的广播等等实现我们的业务逻辑,也可以通过广播实现跨进程间的通信(实际上是利用binder机制)。那么广播的原理呢?是不是值得我们去深入学习一下呢。广播,简单来说分为动态注册和静态注册两种,本文将着重讲解动态注册,静态注册将与packageManagerService一起分析。注册,字面意思就是,对与我需要收到的广播报备一下,告诉系统我想要接收这个广播。这么看来,它的报备过程肯定是把相关信息保存起来嘛,有了广播后进行匹配筛选,谁要发给谁嘛。好,解析结束,下课。哈哈,开个玩笑咱们下面一步一步的开始记录。

客户端的注册

首先,我们写一个demo,对于动态注册一个广播。

@Overrideprotected void onStart() {super.onStart();IntentFilter filter = new IntentFilter();filter.addAction("come.letv.gaomh");gaomhReceiver = new GaomhReceiver();this.registerReceiver(gaomhReceiver,filter);}public class GaomhReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {}}@Overrideprotected void onDestroy() {super.onDestroy();unregisterReceiver(gaomhReceiver);}

注意,为了防止引起内存泄露问题,在onDestroy()中需要解除注册。动态注册的广播当进程被杀死后,又不能帮你重新拉起进程,你要是不取消注册,除了坏处,一点好处没有。

这样我们,进注册了一个广播,接下来我们看一下,它究竟注册到了什么地方。

frameworks/base/core/java/android/content/ContextWrapper.java

@Overridepublic Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {return mBase.registerReceiver(receiver, filter);}

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

@Overridepublic Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {return registerReceiver(receiver, filter, null, null);}

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

private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,IntentFilter filter, String broadcastPermission,Handler scheduler, Context context, int flags) {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 = 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();}}

反正是一顿调用最后来到了registerReceiverInternal()这个方法。我们很清楚的看到啊activityManager.getService().registerReceiver(),这个方法马上就要调到ActivityManagerService(之后一律写成AMS)了,那不就注册成功了?等等,好像有点不对,过去倒是过去了,咋回来啊,肉包子打狗,一去不回了。

我们发现在跑到AMS之前,还有点东西IIntentReceiver rd ,我合理猜测,这个rd会不会就是广播回来的桥梁呢?

IIntentReceiver是什么?

这里的ReceiverDispatcher又是什么呢?

它是LoadedApk中的一个内部类,保存了这个receiver的信息,用于在广播派发到本进程的时候执行,上面这方法最重要的是getIIntentReceiver,这个就非常重要了,它是一个Binder对象,说在广播注册的时候将这个binder对象传递到了AMS,然后当广播派发到本进程的时候,通过这个binder对象再会调回来,它在ReceiverDispatcher创建的时候创建。(参考资料里的文章copy的,感觉总结的很到位。)

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) {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 {rd.validate(context, handler);}rd.mForgotten = false;return rd.getIIntentReceiver();}}

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

ReceiverDispatcher 中的

public void performReceive(Intent intent, int resultCode, String data,Bundle extras, boolean ordered, boolean sticky, int sendingUser) {if (ActivityThread.DEBUG_BROADCAST) {int seq = intent.getIntExtra("seq", -1);Slog.i(ActivityThread.TAG, "Enqueueing broadcast " + intent.getAction() + " seq=" + seq+ " to " + mReceiver);}Args args = new Args(intent, resultCode, data, extras, ordered,sticky, sendingUser);if (!mActivityThread.post(args)) {if (mRegistered && ordered) {IActivityManager mgr = ActivityManagerNative.getDefault();if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,"Finishing sync broadcast to " + mReceiver);args.sendFinished(mgr);}}}

这里就是广播真正派发到本进程的时候从systemserver binder call过来执行的。

到这里我们就把注册的去处以及回来的路都铺垫好了,下面我们一起走到AMS中一探究竟。

AMS对于动态注册广播的处理

这也太长了吧,我其实一直在想,之前到底是谁和我说的一个方法最好不要超过多少多少行,出来一下,谢谢。

嫌太长的兄弟再往下翻翻,我会把步骤提取出来。

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) {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 = handleIncomingUser(callingPid, callingUid, userId,true, ALLOW_FULL_ONLY, "registerReceiver", callerPackage);Iterator<String> actions = filter.actionsIterator();if (actions == null) {ArrayList<String> noAction = new ArrayList<String>(1);noAction.add(null);actions = noAction.iterator();}// Collect stickies of usersint[] 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 (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Register receiver " + filter + ": " + sticky);if (receiver == null) {return sticky;}synchronized (this) {if (callerApp != null && (callerApp.thread == null|| callerApp.thread.asBinder() != caller.asBinder())) {// Original caller already diedreturn 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);if (!bf.debugCheck()) {Slog.w(TAG, "==> For Dynamic broadcast");}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;}}

下面这些代码一句话解释:判断注册的广播,粘性广播的话加入到allSticky

userId = handleIncomingUser(callingPid, callingUid, userId,true, ALLOW_FULL_ONLY, "registerReceiver", callerPackage);Iterator<String> actions = filter.actionsIterator();if (actions == null) {ArrayList<String> noAction = new ArrayList<String>(1);noAction.add(null);actions = noAction.iterator();}// Collect stickies of usersint[] 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);}}}

1.rl用来储存广播接受者

2.bf用来描述注册的广播接受者

3.rl.add(bf)

4. mReceiverResolver.addFilter(bf)

到这个时候,我们就完成了注册,其实就是把相关的一系列参数进行存储,我们之后就可以根据这些参数进行匹配,然后通过binder发送广播到这些已经注册的广播接收者。

synchronized (this) {...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);} ...}...BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,permission, callingUid, userId);rl.add(bf);if (!bf.debugCheck()) {Slog.w(TAG, "==> For Dynamic broadcast");}mReceiverResolver.addFilter(bf);...return sticky;}

上面主要做了几件事情:

1.对caller的判断

2.遍历action,查询是否有匹配的sticky广播

3.将本次注册的广播放到mRegisteredReceivers中记录

4.如果是sticky广播,开始派发

广播的发送

终于注册完了,现在我们只需要安安静静的等待广播的来到。

其实广播的发送又有很多内容,有序广播,无序广播,粘性广播,静态注册的广播怎么发送,动态注册的广播怎么发送。

提一个问题:广播接收者,是动态注册的接收的快,还是静态注册的接收的快?(这个问题是我来北京的第一家公司面试我的一道面试题,我当时贼痛快的蒙了一个,结果答错了,不过最后居然还是面过了,哈哈)。

广播的发送流程,其实还是从系统去调到AMS中的broadcastIntent()方法,可以参考下上面的注册调到AMS,也可以写个demo自己跑跑断点哈,偷个懒直接从AMS开始写。

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");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, map, requiredPermission, appOp, serialized, sticky,callingPid, callingUid, userId);Binder.restoreCallingIdentity(origId);return res;}}

此方法是对广播合法性进行检查,看方法名字就能看出来这不是verify吗,继续往下看。

final Intent verifyBroadcastLocked(Intent intent) {// Refuse possible leaked file descriptorsif (intent != null && intent.hasFileDescriptors() == true) {throw new IllegalArgumentException("File descriptors passed in Intent");}int flags = intent.getFlags();if (!mProcessesReady) {// if the caller really truly claims to know what they're doing, go// ahead and allow the broadcast without launching any receiversif ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT) != 0) {intent = new Intent(intent);intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);} else if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {Slog.e(TAG, "Attempt to launch receivers of broadcast intent " + intent+ " before boot completion");throw new IllegalStateException("Cannot broadcast before boot completed");}}if ((flags&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {throw new IllegalArgumentException("Can't use FLAG_RECEIVER_BOOT_UPGRADE here");}return intent;}

再疯狂@一下当初和我说方法不能超过多少行的哥们,老规矩,拆分步骤往下看。有兴趣的自己慢慢看看。

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) {intent = new Intent(intent);final boolean callerInstantApp = isInstantApp(callerApp, callerPackage, callingUid);// Instant Apps cannot use FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPSif (callerInstantApp) {intent.setFlags(intent.getFlags() & ~Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);}// By default broadcasts do not go to stopped apps.intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);// If we have not finished booting, don't allow this to launch new processes.if (!mProcessesReady && (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);}if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,(sticky ? "Broadcast sticky: ": "Broadcast: ") + intent+ " ordered=" + ordered + " userid=" + userId);if ((resultTo != null) && !ordered) {Slog.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!");}userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,ALLOW_NON_FULL, "broadcast", callerPackage);// Make sure that the user who is receiving this broadcast is running.// If not, we will just skip it. Make an exception for shutdown broadcasts// and upgrade steps.if (userId != UserHandle.USER_ALL && !mUserController.isUserRunningLocked(userId, 0)) {if ((callingUid != SYSTEM_UID|| (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0)&& !Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {Slog.w(TAG, "Skipping broadcast of " + intent+ ": user " + userId + " is stopped");return ActivityManager.BROADCAST_FAILED_USER_STOPPED;}}BroadcastOptions brOptions = null;if (bOptions != null) {brOptions = new BroadcastOptions(bOptions);if (brOptions.getTemporaryAppWhitelistDuration() > 0) {// See if the caller is allowed to do this. Note we are checking against// the actual real caller (not whoever provided the operation as say a// PendingIntent), because that who is actually supplied the arguments.if (checkComponentPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,Binder.getCallingPid(), Binder.getCallingUid(), -1, true)!= PackageManager.PERMISSION_GRANTED) {String msg = "Permission Denial: " + intent.getAction()+ " broadcast from " + callerPackage + " (pid=" + callingPid+ ", uid=" + callingUid + ")"+ " requires "+ android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST;Slog.w(TAG, msg);throw new SecurityException(msg);}}}// Verify that protected broadcasts are only being sent by system code,// and that system code is only sending protected broadcasts.final String action = intent.getAction();final boolean isProtectedBroadcast;try {isProtectedBroadcast = AppGlobals.getPackageManager().isProtectedBroadcast(action);} catch (RemoteException e) {Slog.w(TAG, "Remote exception", e);return ActivityManager.BROADCAST_SUCCESS;}final boolean isCallerSystem;switch (UserHandle.getAppId(callingUid)) {case ROOT_UID:case SYSTEM_UID:case PHONE_UID:case BLUETOOTH_UID:case NFC_UID:isCallerSystem = true;break;default:isCallerSystem = (callerApp != null) && callerApp.persistent;break;}// First line security check before anything else: stop non-system apps from// sending protected broadcasts.if (!isCallerSystem) {if (isProtectedBroadcast) {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)) {// Special case for compatibility: we don't want apps to send this,// but historically it has not been protected and apps may be using it// to poke their own app widget. So, instead of making it protected,// just limit it to the caller.if (callerPackage == null) {String msg = "Permission Denial: not allowed to send broadcast "+ action + " from unknown caller.";Slog.w(TAG, msg);throw new SecurityException(msg);} else if (intent.getComponent() != null) {// They are good enough to send to an explicit component... verify// it is being sent to the calling app.if (!intent.getComponent().getPackageName().equals(callerPackage)) {String msg = "Permission Denial: not allowed to send broadcast "+ action + " to "+ intent.getComponent().getPackageName() + " from "+ callerPackage;Slog.w(TAG, msg);throw new SecurityException(msg);}} else {// Limit broadcast to their own package.intent.setPackage(callerPackage);}}}if (action != null) {if (getBackgroundLaunchBroadcasts().contains(action)) {if (DEBUG_BACKGROUND_CHECK) {Slog.i(TAG, "Broadcast action " + action + " forcing include-background");}intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);}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:// Handle special intents: if this broadcast is from the package// manager about a package being removed, we need to remove all of// its activities from the history stack.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:final int uid = getUidFromIntent(intent);if (uid >= 0) {mBatteryStatsService.removeUid(uid);mAppOpsService.uidRemoved(uid);}break;case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:// If resources are unavailable just force stop all those packages// and flush the attribute cache as well.String list[] =intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);if (list != null && list.length > 0) {for (int i = 0; i < list.length; i++) {forceStopPackageLocked(list[i], -1, false, true, true,false, false, userId, "storage unmount");}mRecentTasks.cleanupLocked(UserHandle.USER_ALL);sendPackageBroadcastLocked(ApplicationThreadConstants.EXTERNAL_STORAGE_UNAVAILABLE,list, userId);}break;case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:mRecentTasks.cleanupLocked(UserHandle.USER_ALL);break;case Intent.ACTION_PACKAGE_REMOVED:case Intent.ACTION_PACKAGE_CHANGED:Uri data = intent.getData();String ssp;if (data != null && (ssp=data.getSchemeSpecificPart()) != null) {boolean removed = Intent.ACTION_PACKAGE_REMOVED.equals(action);final boolean replacing =intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);final boolean killProcess =!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false);final boolean fullUninstall = removed && !replacing;if (removed) {if (killProcess) {forceStopPackageLocked(ssp, UserHandle.getAppId(intent.getIntExtra(Intent.EXTRA_UID, -1)),false, true, true, false, fullUninstall, userId,removed ? "pkg removed" : "pkg changed");}final int cmd = killProcess? ApplicationThreadConstants.PACKAGE_REMOVED: ApplicationThreadConstants.PACKAGE_REMOVED_DONT_KILL;sendPackageBroadcastLocked(cmd,new String[] {ssp}, userId);if (fullUninstall) {mAppOpsService.packageRemoved(intent.getIntExtra(Intent.EXTRA_UID, -1), ssp);// Remove all permissions granted from/to this packageremoveUriPermissionsForPackageLocked(ssp, userId, true);removeTasksByPackageNameLocked(ssp, userId);mServices.forceStopPackageLocked(ssp, userId);// Hide the "unsupported display" dialog if necessary.if (mUnsupportedDisplaySizeDialog != null && ssp.equals(mUnsupportedDisplaySizeDialog.getPackageName())) {mUnsupportedDisplaySizeDialog.dismiss();mUnsupportedDisplaySizeDialog = null;}mCompatModePackages.handlePackageUninstalledLocked(ssp);mBatteryStatsService.notePackageUninstalled(ssp);}} else {if (killProcess) {killPackageProcessesLocked(ssp, UserHandle.getAppId(intent.getIntExtra(Intent.EXTRA_UID, -1)),userId, ProcessList.INVALID_ADJ,false, true, true, false, "change " + ssp);}cleanupDisabledPackageComponentsLocked(ssp, userId, killProcess,intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST));}}break;case Intent.ACTION_PACKAGES_SUSPENDED:case Intent.ACTION_PACKAGES_UNSUSPENDED:final boolean suspended = Intent.ACTION_PACKAGES_SUSPENDED.equals(intent.getAction());final String[] packageNames = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);synchronized(ActivityManagerService.this) {mRecentTasks.onPackagesSuspendedChanged(packageNames, suspended, userHandle);}break;}break;case Intent.ACTION_PACKAGE_REPLACED:{final Uri data = intent.getData();final String ssp;if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {ApplicationInfo aInfo = null;try {aInfo = AppGlobals.getPackageManager().getApplicationInfo(ssp, 0 /*flags*/, userId);} catch (RemoteException ignore) {}if (aInfo == null) {Slog.w(TAG, "Dropping ACTION_PACKAGE_REPLACED for non-existent pkg:"+ " ssp=" + ssp + " data=" + data);return ActivityManager.BROADCAST_SUCCESS;}mStackSupervisor.updateActivityApplicationInfoLocked(aInfo);sendPackageBroadcastLocked(ApplicationThreadConstants.PACKAGE_REPLACED,new String[] {ssp}, userId);}break;}case Intent.ACTION_PACKAGE_ADDED:{// Special case for adding a package: by default turn on compatibility mode.Uri data = intent.getData();String ssp;if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {final boolean replacing =intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);mCompatModePackages.handlePackageAddedLocked(ssp, replacing);try {ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(ssp, 0, 0);mBatteryStatsService.notePackageInstalled(ssp,ai != null ? ai.versionCode : 0);} catch (RemoteException e) {}}break;}case Intent.ACTION_PACKAGE_DATA_CLEARED:{Uri data = intent.getData();String ssp;if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {// Hide the "unsupported display" dialog if necessary.if (mUnsupportedDisplaySizeDialog != null && ssp.equals(mUnsupportedDisplaySizeDialog.getPackageName())) {mUnsupportedDisplaySizeDialog.dismiss();mUnsupportedDisplaySizeDialog = null;}mCompatModePackages.handlePackageDataClearedLocked(ssp);}break;}case Intent.ACTION_TIMEZONE_CHANGED:// If this is the time zone changed action, queue up a message that will reset// the timezone of all currently running processes. This message will get// queued up before the broadcast happens.mHandler.sendEmptyMessage(UPDATE_TIME_ZONE);break;case Intent.ACTION_TIME_CHANGED:// EXTRA_TIME_PREF_24_HOUR_FORMAT is optional so we must distinguish between// the tri-state value it may contain and "unknown".// For convenience we re-use the Intent extra values.final int NO_EXTRA_VALUE_FOUND = -1;final int timeFormatPreferenceMsgValue = intent.getIntExtra(Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT,NO_EXTRA_VALUE_FOUND /* defaultValue */);// Only send a message if the time preference is available.if (timeFormatPreferenceMsgValue != NO_EXTRA_VALUE_FOUND) {Message updateTimePreferenceMsg =mHandler.obtainMessage(UPDATE_TIME_PREFERENCE_MSG,timeFormatPreferenceMsgValue, 0);mHandler.sendMessage(updateTimePreferenceMsg);}BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();synchronized (stats) {stats.noteCurrentTimeChangedLocked();}break;case Intent.ACTION_CLEAR_DNS_CACHE:mHandler.sendEmptyMessage(CLEAR_DNS_CACHE_MSG);break;case Proxy.PROXY_CHANGE_ACTION:ProxyInfo proxy = intent.getParcelableExtra(Proxy.EXTRA_PROXY_INFO);mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG, proxy));break;case android.hardware.Camera.ACTION_NEW_PICTURE:case android.hardware.Camera.ACTION_NEW_VIDEO:// In N we just turned these off; in O we are turing them back on partly,// only for registered receivers. This will still address the main problem// (a spam of apps waking up when a picture is taken putting significant// memory pressure on the system at a bad point), while still allowing apps// that are already actively running to know about this happening.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":// As of O, we no longer support this broadcasts, even for pre-O apps.// Apps should now be using ShortcutManager.pinRequestShortcut().Log.w(TAG, "Broadcast " + action+ " no longer supported. It will not be delivered.");return ActivityManager.BROADCAST_SUCCESS;}if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||Intent.ACTION_PACKAGE_REMOVED.equals(action) ||Intent.ACTION_PACKAGE_REPLACED.equals(action)) {final int uid = getUidFromIntent(intent);if (uid != -1) {final UidRecord uidRec = mActiveUids.get(uid);if (uidRec != null) {uidRec.updateHasInternetPermission();}}}}// 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");}// 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<>();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));}}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};}// 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 usersfor (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 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, callerInstantApp, resolvedType,requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,resultCode, resultData, resultExtras, ordered, sticky, false, userId);if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);final boolean replaced = replacePending&& (queue.replaceParallelBroadcastLocked(r) != 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) {// 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++;}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);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());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 函数主要功能

1 移除Stopped Package来接收此广播

2 判断广播是否是PRE_Boot接收

3 Protected broadcast

4 特殊ACTION处理

5 StickBroadcast

6 Sticky广播数据结构

7 根据Intent Figure out 对应的Receiver

8 优先处理并行广播

9 处理静态注册Receiver和Order动态广播

10 整理mReceiver和registeredReceivers

11 无Receiver接收此Intent仅记录

12 开始进入广播队列BroadcastQeueue

13 scheduleBroadcastsLocked

1.移除Stopped Package来接收此广播

intent = new Intent(intent);final boolean callerInstantApp = isInstantApp(callerApp, callerPackage, callingUid);// Instant Apps cannot use FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPSif (callerInstantApp) {intent.setFlags(intent.getFlags() & ~Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);}// By default broadcasts do not go to stopped apps.intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);

FLAG_INCLUDE_STOPPED_PACKAGES

FLAG_EXCLUDE_STOPPED_PACKAGES

广播目前存在两个flag,广播的发送是否包含已经停止的进程,这也是为了提高Android的安全性。

2. 判断广播是否是PRE_Boot接收

// If we have not finished booting, don't allow this to launch new processes.if (!mProcessesReady && (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0) {intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);}

是否启动完成,没有完成的话添加 Intent.FLAG_RECEIVER_REGISTERED_ONLY,为什么会有这样的操作呢,做个之后博客的铺垫,Android的静态注册广播其实是PackageManagerService对于xml文件的解析,如果说系统还没有ready,完全有可能存在xml还没有解析完成。这个时候加上之前的那个flag,我们就只对于动态注册的广播进行处理。反正没有ready的时候,静态广播也可能存在不准确的情况,那好我干脆就不去处理了。

3. Protected broadcast

protected-broadcast用来指定一个广播,该广播只能被系统发送,如果普通APP在自己的xml里声明了这些广播,发送时也会被提示错误。“Permission Denial: not allowed to send broadcast”。

ProtectedBroadcast,主要定义在系统级应用的Manfiest.xml中,可见frameworks/base/core/res/AndroidManifest.xml

// First line security check before anything else: stop non-system apps from// sending protected broadcasts.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)) {// Special case for compatibility: we don't want apps to send this,// but historically it has not been protected and apps may be using it// to poke their own app widget. So, instead of making it protected,// just limit it to the caller.if (callerPackage == null) {String msg = "Permission Denial: not allowed to send broadcast "+ action + " from unknown caller.";Slog.w(TAG, msg);throw new SecurityException(msg);} else if (intent.getComponent() != null) {// They are good enough to send to an explicit component... verify// it is being sent to the calling app.if (!intent.getComponent().getPackageName().equals(callerPackage)) {String msg = "Permission Denial: not allowed to send broadcast "+ action + " to "+ intent.getComponent().getPackageName() + " from "+ callerPackage;Slog.w(TAG, msg);throw new SecurityException(msg);}} else {// Limit broadcast to their own package.intent.setPackage(callerPackage);}}}

4 特殊ACTION处理

这个就很明显对于action的处理,有兴趣或者用到了自己看看哈。

if (action != null) {if (getBackgroundLaunchBroadcasts().contains(action)) {if (DEBUG_BACKGROUND_CHECK) {Slog.i(TAG, "Broadcast action " + action + " forcing include-background");}intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);}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:// Handle special intents: if this broadcast is from the package// manager about a package being removed, we need to remove all of// its activities from the history stack.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:final int uid = getUidFromIntent(intent);if (uid >= 0) {mBatteryStatsService.removeUid(uid);mAppOpsService.uidRemoved(uid);}break;case Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE:// If resources are unavailable just force stop all those packages// and flush the attribute cache as well.String list[] =intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);if (list != null && list.length > 0) {for (int i = 0; i < list.length; i++) {forceStopPackageLocked(list[i], -1, false, true, true,false, false, userId, "storage unmount");}mRecentTasks.cleanupLocked(UserHandle.USER_ALL);sendPackageBroadcastLocked(ApplicationThreadConstants.EXTERNAL_STORAGE_UNAVAILABLE,list, userId);}break;case Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE:mRecentTasks.cleanupLocked(UserHandle.USER_ALL);break;case Intent.ACTION_PACKAGE_REMOVED:case Intent.ACTION_PACKAGE_CHANGED:Uri data = intent.getData();String ssp;if (data != null && (ssp=data.getSchemeSpecificPart()) != null) {boolean removed = Intent.ACTION_PACKAGE_REMOVED.equals(action);final boolean replacing =intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);final boolean killProcess =!intent.getBooleanExtra(Intent.EXTRA_DONT_KILL_APP, false);final boolean fullUninstall = removed && !replacing;if (removed) {if (killProcess) {forceStopPackageLocked(ssp, UserHandle.getAppId(intent.getIntExtra(Intent.EXTRA_UID, -1)),false, true, true, false, fullUninstall, userId,removed ? "pkg removed" : "pkg changed");}final int cmd = killProcess? ApplicationThreadConstants.PACKAGE_REMOVED: ApplicationThreadConstants.PACKAGE_REMOVED_DONT_KILL;sendPackageBroadcastLocked(cmd,new String[] {ssp}, userId);if (fullUninstall) {mAppOpsService.packageRemoved(intent.getIntExtra(Intent.EXTRA_UID, -1), ssp);// Remove all permissions granted from/to this packageremoveUriPermissionsForPackageLocked(ssp, userId, true);removeTasksByPackageNameLocked(ssp, userId);mServices.forceStopPackageLocked(ssp, userId);// Hide the "unsupported display" dialog if necessary.if (mUnsupportedDisplaySizeDialog != null && ssp.equals(mUnsupportedDisplaySizeDialog.getPackageName())) {mUnsupportedDisplaySizeDialog.dismiss();mUnsupportedDisplaySizeDialog = null;}mCompatModePackages.handlePackageUninstalledLocked(ssp);mBatteryStatsService.notePackageUninstalled(ssp);}} else {if (killProcess) {killPackageProcessesLocked(ssp, UserHandle.getAppId(intent.getIntExtra(Intent.EXTRA_UID, -1)),userId, ProcessList.INVALID_ADJ,false, true, true, false, "change " + ssp);}cleanupDisabledPackageComponentsLocked(ssp, userId, killProcess,intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST));}}break;case Intent.ACTION_PACKAGES_SUSPENDED:case Intent.ACTION_PACKAGES_UNSUSPENDED:final boolean suspended = Intent.ACTION_PACKAGES_SUSPENDED.equals(intent.getAction());final String[] packageNames = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);synchronized(ActivityManagerService.this) {mRecentTasks.onPackagesSuspendedChanged(packageNames, suspended, userHandle);}break;}break;case Intent.ACTION_PACKAGE_REPLACED:{final Uri data = intent.getData();final String ssp;if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {ApplicationInfo aInfo = null;try {aInfo = AppGlobals.getPackageManager().getApplicationInfo(ssp, 0 /*flags*/, userId);} catch (RemoteException ignore) {}if (aInfo == null) {Slog.w(TAG, "Dropping ACTION_PACKAGE_REPLACED for non-existent pkg:"+ " ssp=" + ssp + " data=" + data);return ActivityManager.BROADCAST_SUCCESS;}mStackSupervisor.updateActivityApplicationInfoLocked(aInfo);sendPackageBroadcastLocked(ApplicationThreadConstants.PACKAGE_REPLACED,new String[] {ssp}, userId);}break;}case Intent.ACTION_PACKAGE_ADDED:{// Special case for adding a package: by default turn on compatibility mode.Uri data = intent.getData();String ssp;if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {final boolean replacing =intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);mCompatModePackages.handlePackageAddedLocked(ssp, replacing);try {ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(ssp, 0, 0);mBatteryStatsService.notePackageInstalled(ssp,ai != null ? ai.versionCode : 0);} catch (RemoteException e) {}}break;}case Intent.ACTION_PACKAGE_DATA_CLEARED:{Uri data = intent.getData();String ssp;if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {// Hide the "unsupported display" dialog if necessary.if (mUnsupportedDisplaySizeDialog != null && ssp.equals(mUnsupportedDisplaySizeDialog.getPackageName())) {mUnsupportedDisplaySizeDialog.dismiss();mUnsupportedDisplaySizeDialog = null;}mCompatModePackages.handlePackageDataClearedLocked(ssp);}break;}case Intent.ACTION_TIMEZONE_CHANGED:// If this is the time zone changed action, queue up a message that will reset// the timezone of all currently running processes. This message will get// queued up before the broadcast happens.mHandler.sendEmptyMessage(UPDATE_TIME_ZONE);break;case Intent.ACTION_TIME_CHANGED:// EXTRA_TIME_PREF_24_HOUR_FORMAT is optional so we must distinguish between// the tri-state value it may contain and "unknown".// For convenience we re-use the Intent extra values.final int NO_EXTRA_VALUE_FOUND = -1;final int timeFormatPreferenceMsgValue = intent.getIntExtra(Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT,NO_EXTRA_VALUE_FOUND /* defaultValue */);// Only send a message if the time preference is available.if (timeFormatPreferenceMsgValue != NO_EXTRA_VALUE_FOUND) {Message updateTimePreferenceMsg =mHandler.obtainMessage(UPDATE_TIME_PREFERENCE_MSG,timeFormatPreferenceMsgValue, 0);mHandler.sendMessage(updateTimePreferenceMsg);}BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();synchronized (stats) {stats.noteCurrentTimeChangedLocked();}break;case Intent.ACTION_CLEAR_DNS_CACHE:mHandler.sendEmptyMessage(CLEAR_DNS_CACHE_MSG);break;case Proxy.PROXY_CHANGE_ACTION:ProxyInfo proxy = intent.getParcelableExtra(Proxy.EXTRA_PROXY_INFO);mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG, proxy));break;case android.hardware.Camera.ACTION_NEW_PICTURE:case android.hardware.Camera.ACTION_NEW_VIDEO:// In N we just turned these off; in O we are turing them back on partly,// only for registered receivers. This will still address the main problem// (a spam of apps waking up when a picture is taken putting significant// memory pressure on the system at a bad point), while still allowing apps// that are already actively running to know about this happening.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":// As of O, we no longer support this broadcasts, even for pre-O apps.// Apps should now be using ShortcutManager.pinRequestShortcut().Log.w(TAG, "Broadcast " + action+ " no longer supported. It will not be delivered.");return ActivityManager.BROADCAST_SUCCESS;}if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||Intent.ACTION_PACKAGE_REMOVED.equals(action) ||Intent.ACTION_PACKAGE_REPLACED.equals(action)) {final int uid = getUidFromIntent(intent);if (uid != -1) {final UidRecord uidRec = mActiveUids.get(uid);if (uidRec != null) {uidRec.updateHasInternetPermission();}}}}

5 StickBroadcast

判断粘性广播,并且更新stickies。

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

6 根据Intent Figure out 对应的Receiver

根据intent匹配出Receiver,其实就是查询,储存,为下一步发送做准备,你总得知道我们需要发给谁,对吧。

receivers静态注册的接收者

registeredReceivers所有动态注册的接收者

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

8 优先处理并行广播

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 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, callerInstantApp, resolvedType,requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,resultCode, resultData, resultExtras, ordered, sticky, false, userId);if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);final boolean replaced = replacePending&& (queue.replaceParallelBroadcastLocked(r) != null);// Note: We assume resultTo is null for non-ordered broadcasts.if (!replaced) {queue.enqueueParallelBroadcastLocked(r);queue.scheduleBroadcastsLocked();}registeredReceivers = null;NR = 0;}

9 处理静态注册Receiver和Order动态广播

将最终的结果发给BroadCastQueue

if ((receivers != null && receivers.size() > 0)|| resultTo != null) {BroadcastQueue queue = broadcastQueueForIntent(intent);BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,callerPackage, callingPid, callingUid, resolvedType,requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,resultData, resultExtras, ordered, sticky, false, userId);if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r+ ": prev had " + queue.mOrderedBroadcasts.size());if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST,"Enqueueing broadcast " + r.intent.getAction());boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);if (!replaced) {queue.enqueueOrderedBroadcastLocked(r);queue.scheduleBroadcastsLocked();}} else {// There was nobody interested in the broadcast, but we still want to record// that it happened.if (intent.getComponent() == null && intent.getPackage() == null&& (intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {// This was an implicit broadcast... let's record it for posterity.addBroadcastStatLocked(intent.getAction(), callerPackage, 0, 0, 0);}}

10 整理mReceiver和registeredReceivers

mParallelBroadcasts表中,每个BroadcastRecord只可能携带BroadcastFilter,因为平行处理的节点只会对应动态receiver,

而所有静态receiver只能是串行处理的,另一方面,在mOrderedBroadcasts表中BroadcastRecord中则既可能携带BroadcastFilter,也可能携带ResolveInfo。这个其实很容易理解,

mReceiver解析

首先,ResolveInfo对应静态receiver,放到这里自不待言,

其次,如果用户在发送广播时明确指定要按ordered方式发送的话,那么即使目标方的receiver是动态注册的,它对应的BroadcastFilter也会被强制放到这里。

public void enqueueParallelBroadcastLocked(BroadcastRecord r) {mParallelBroadcasts.add(r);}public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {mOrderedBroadcasts.add(r);}

AMS其实是调用这两个方法在解析判断后add到对应的数据结构当中。

11 开始进入广播队列BroadcastQeueue

调用scheduleBroadcastsLocked(),进入到BroadcastQeueue。

public void scheduleBroadcastsLocked() {if (DEBUG_BROADCAST) Slog.v(TAG, "Schedule broadcasts ["+ mQueueName + "]: current="+ mBroadcastsScheduled);if (mBroadcastsScheduled) {return;}mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));mBroadcastsScheduled = true;}

12 scheduleBroadcastsLocked

之后,我们最终会调用到BroadcastQeueue的processNextBroadcast()方法。

来来来,划重点,前面我曾经说过一个问题,静态注册的广播先接收到还是动态注册的广播先接收到呢。先给出答案:动态广播 > 静态注册的广播。

我们应该知道的是,我们可以通过静态广播,当进程死掉的时候,发送这个广播将他拉起来但是动态却不能实现,他比静态广播可少步骤啊,那好吧,google的思想就是先把简单的处理了再处理复杂的,至少有个快的,对不对。所以,他们选择优先处理并行广播。

首先我们会处理平行广播。

// First, deliver any non-serialized broadcasts right away.while (mParallelBroadcasts.size() > 0) {r = mParallelBroadcasts.remove(0);r.dispatchTime = SystemClock.uptimeMillis();r.dispatchClockTime = System.currentTimeMillis();final int N = r.receivers.size();if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Processing parallel broadcast ["+ mQueueName + "] " + r);for (int i=0; i<N; i++) {Object target = r.receivers.get(i);if (DEBUG_BROADCAST) Slog.v(TAG,"Delivering non-ordered on [" + mQueueName + "] to registered "+ target + ": " + r);deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);}addBroadcastToHistoryLocked(r);if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Done with parallel broadcast ["+ mQueueName + "] " + r);}

deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);看这个方法,看名字就像是发送广播的方法,这个时候就体现命名规范的作用了,我记得我最开始写代码的时候,那名字起的,过段时间自己都不知道自己写的啥。看了看这个方法,果然啊,一会再分析他,继续往下看。

deliverToRegisteredReceiverLocked(),又会调用performReceiveLocked().

private static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,Intent intent, int resultCode, String data, Bundle extras,boolean ordered, boolean sticky, int sendingUser) throws RemoteException {// Send the intent to the receiver asynchronously using one-way binder calls.if (app != null && app.thread != null) {// If we have an app thread, do the call through that so it is// correctly ordered with other one-way calls.app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,data, extras, ordered, sticky, sendingUser, app.repProcState);} else {receiver.performReceive(intent, resultCode, data, extras, ordered,sticky, sendingUser);}}

之后会开始处理有序动态注册广播

if (nextReceiver instanceof BroadcastFilter) {// Simple case: this is a registered receiver who gets// a direct call.BroadcastFilter filter = (BroadcastFilter)nextReceiver;if (DEBUG_BROADCAST) Slog.v(TAG,"Delivering ordered ["+ mQueueName + "] to registered "+ filter + ": " + r);deliverToRegisteredReceiverLocked(r, filter, r.ordered);if (r.receiver == null || !r.ordered) {// The receiver has already finished, so schedule to// process the next one.if (DEBUG_BROADCAST) Slog.v(TAG, "Quick finishing ["+ mQueueName + "]: ordered="+ r.ordered + " receiver=" + r.receiver);r.state = BroadcastRecord.IDLE;scheduleBroadcastsLocked();}return;}

然后会处理静态注册的广播

ProcessRecord app = mService.getProcessRecordLocked(targetProcess,info.activityInfo.applicationInfo.uid, false);if (app != null && app.thread != null) {try {app.addPackage(info.activityInfo.packageName, mService.mProcessStats);processCurBroadcastLocked(r, app);return;} catch (RemoteException e) {Slog.w(TAG, "Exception when sending broadcast to "+ r.curComponent, e);} catch (RuntimeException e) {Log.wtf(TAG, "Failed sending broadcast to "+ r.curComponent + " with " + r.intent, e);// If some unexpected exception happened, just skip// this broadcast. At this point we are not in the call// from a client, so throwing an exception out from here// will crash the entire system instead of just whoever// sent the broadcast.logBroadcastReceiverDiscardLocked(r);finishReceiverLocked(r, r.resultCode, r.resultData,r.resultExtras, r.resultAbort, false);scheduleBroadcastsLocked();// We need to reset the state if we failed to start the receiver.r.state = BroadcastRecord.IDLE;return;}// If a dead object exception was thrown -- fall through to// restart the application.}

private final void processCurBroadcastLocked(BroadcastRecord r,ProcessRecord app) throws RemoteException {if (DEBUG_BROADCAST) Slog.v(TAG,"Process cur broadcast " + r + " for app " + app);if (app.thread == null) {throw new RemoteException();}r.receiver = app.thread.asBinder();r.curApp = app;app.curReceiver = r;app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);mService.updateLruProcessLocked(app, false, null);mService.updateOomAdjLocked();// Tell the application to launch this receiver.r.intent.setComponent(r.curComponent);boolean started = false;try {if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG,"Delivering to component " + r.curComponent+ ": " + r);mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,patibilityInfoForPackageLocked(r.curReceiver.applicationInfo),r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,app.repProcState);if (DEBUG_BROADCAST) Slog.v(TAG,"Process cur broadcast " + r + " DELIVERED for app " + app);started = true;} finally {if (!started) {if (DEBUG_BROADCAST) Slog.v(TAG,"Process cur broadcast " + r + ": NOT STARTED!");r.receiver = null;r.curApp = null;app.curReceiver = null;}}}

如果说当前进程不存在的话需要重新拉起进程,这个也就是静态注册的广播能够被死掉的进程所接收的原因。

if (DEBUG_BROADCAST) Slog.v(TAG,"Need to start app ["+ mQueueName + "] " + targetProcess + " for broadcast " + r);if ((r.curApp=mService.startProcessLocked(targetProcess,info.activityInfo.applicationInfo, true,r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,"broadcast", r.curComponent,(r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))== null) {// Ah, this recipient is unavailable. Finish it if necessary,// and mark the broadcast record as ready for the next.Slog.w(TAG, "Unable to launch app "+ info.activityInfo.applicationInfo.packageName + "/"+ info.activityInfo.applicationInfo.uid + " for broadcast "+ r.intent + ": process is bad");logBroadcastReceiverDiscardLocked(r);finishReceiverLocked(r, r.resultCode, r.resultData,r.resultExtras, r.resultAbort, false);scheduleBroadcastsLocked();r.state = BroadcastRecord.IDLE;return;}

兜兜转转的,终于来到了最后一步,我太难了。。。。。。

//静态的app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,patibilityInfoForPackageLocked(r.curReceiver.applicationInfo),r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,app.repProcState);//动态的app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,data, extras, ordered, sticky, sendingUser, app.repProcState);

总结

兜兜转转的整整写了两天,广播其实就是注册,匹配,发送的过程。动态注册就是将相关的信息保存到AMS中,静态注册其实是在安装时,PKMS解析xml文件进行保存的。匹配其实是AMS做的主要工作,静态注册的广播需要通过PKMS进行查询匹配,动态注册的广播就保存在他自己这里呀。发送,额,也就是发送了,通过binder机制。

参考文献

Android Broadcast原理分析之registerReceiver(一)

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

Android8.0.0-r4——广播的注册、发送和接收过程

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。