赞
踩
Android四大组件中的BroadcastReceiver,在我们实际工作中被频繁的使用,我们可以利用系统的开机广播,网络状态改变的广播等等实现我们的业务逻辑,也可以通过广播实现跨进程间的通信(实际上是利用binder机制)。那么广播的原理呢?是不是值得我们去深入学习一下呢。广播,简单来说分为动态注册和静态注册两种,本文将着重讲解动态注册,静态注册将与packageManagerService一起分析。注册,字面意思就是,对与我需要收到的广播报备一下,告诉系统我想要接收这个广播。这么看来,它的报备过程肯定是把相关信息保存起来嘛,有了广播后进行匹配筛选,谁要发给谁嘛。好,解析结束,下课。哈哈,开个玩笑咱们下面一步一步的开始记录。
首先,我们写一个demo,对于动态注册一个广播。
@Override protected 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 { @Override public void onReceive(Context context, Intent intent) { } } @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(gaomhReceiver); }
注意,为了防止引起内存泄露问题,在onDestroy()中需要解除注册。动态注册的广播当进程被杀死后,又不能帮你重新拉起进程,你要是不取消注册,除了坏处,一点好处没有。
这样我们,进注册了一个广播,接下来我们看一下,它究竟注册到了什么地方。
frameworks/base/core/java/android/content/ContextWrapper.java
@Override
public Intent registerReceiver(
BroadcastReceiver receiver, IntentFilter filter) {
return mBase.registerReceiver(receiver, filter);
}
frameworks/base/core/java/android/app/ContextImpl.java
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
return registerReceiver(receiver, filter, null, null);
}
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler) {
return registerReceiverInternal(receiver, getUserId(),
filter, broadcastPermission, scheduler, getOuterContext(), 0);
}
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中一探究竟。
这也太长了吧,我其实一直在想,之前到底是谁和我说的一个方法最好不要超过多少多少行,出来一下,谢谢。
嫌太长的兄弟再往下翻翻,我会把步骤提取出来。
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 users int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) }; while (actions.hasNext()) { String action = actions.next(); for (int id : userIds) { ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id); if (stickies != null) { ArrayList<Intent> intents = stickies.get(action); if (intents != null) { if (stickyIntents == null) { stickyIntents = new ArrayList<Intent>(); } stickyIntents.addAll(intents); } } } } } ArrayList<Intent> allSticky = null; if (stickyIntents != null) { final ContentResolver resolver = mContext.getContentResolver(); // Look for any matching sticky broadcasts... for (int i = 0, N = stickyIntents.size(); i < N; i++) { Intent intent = stickyIntents.get(i); // If intent has scheme "content", it will need to acccess // provider that needs to lock mProviderMap in ActivityThread // and also it may need to wait application response, so we // cannot lock ActivityManagerService here. if (filter.match(resolver, intent, true, TAG) >= 0) { if (allSticky == null) { allSticky = new ArrayList<Intent>(); } allSticky.add(intent); } } } // The first sticky in the list is returned directly back to the client. Intent sticky = allSticky != null ? allSticky.get(0) : null; if (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 died return null; } ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder()); if (rl == null) { rl = new ReceiverList(this, callerApp, callingPid, callingUid, userId, receiver); if (rl.app != null) { rl.app.receivers.add(rl); } else { try { receiver.asBinder().linkToDeath(rl, 0); } catch (RemoteException e) { return sticky; } rl.linkedToDeath = true; } mRegisteredReceivers.put(receiver.asBinder(), rl); } else if (rl.uid != callingUid) { throw new IllegalArgumentException( "Receiver requested to register for uid " + callingUid + " was previously registered for uid " + rl.uid); } else if (rl.pid != callingPid) { throw new IllegalArgumentException( "Receiver requested to register for pid " + callingPid + " was previously registered for pid " + rl.pid); } else if (rl.userId != userId) { throw new IllegalArgumentException( "Receiver requested to register for user " + userId + " was previously registered for user " + rl.userId); } BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission, callingUid, userId); rl.add(bf); 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 users int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) }; while (actions.hasNext()) { String action = actions.next(); for (int id : userIds) { ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id); if (stickies != null) { ArrayList<Intent> intents = stickies.get(action); if (intents != null) { if (stickyIntents == null) { stickyIntents = new ArrayList<Intent>(); } stickyIntents.addAll(intents); } } } } ArrayList<Intent> allSticky = null; if (stickyIntents != null) { final ContentResolver resolver = mContext.getContentResolver(); // Look for any matching sticky broadcasts... for (int i = 0, N = stickyIntents.size(); i < N; i++) { Intent intent = stickyIntents.get(i); // If intent has scheme "content", it will need to acccess // provider that needs to lock mProviderMap in ActivityThread // and also it may need to wait application response, so we // cannot lock ActivityManagerService here. if (filter.match(resolver, intent, true, TAG) >= 0) { if (allSticky == null) { allSticky = new ArrayList<Intent>(); } allSticky.add(intent); } } }
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 descriptors if (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 receivers if ((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_APPS if (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 package removeUriPermissionsForPackageLocked(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 users for (int i = 0; i < users.length; i++) { if (mUserController.hasUserRestriction( UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) { continue; } List<BroadcastFilter> registeredReceiversForUser = mReceiverResolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, users[i]); if (registeredReceivers == null) { registeredReceivers = registeredReceiversForUser; } else if (registeredReceiversForUser != null) { registeredReceivers.addAll(registeredReceiversForUser); } } } else { registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId); } } final boolean replacePending = (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0; if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing broadcast: " + intent.getAction() + " replacePending=" + replacePending); int NR = registeredReceivers != null ? registeredReceivers.size() : 0; if (!ordered && NR > 0) { // If 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; }
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
intent = new Intent(intent);
final boolean callerInstantApp = isInstantApp(callerApp, callerPackage, callingUid);
// Instant Apps cannot use FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS
if (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的安全性。
// 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的时候,静态广播也可能存在不准确的情况,那好我干脆就不去处理了。
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); } } }
这个就很明显对于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 package removeUriPermissionsForPackageLocked(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(); } } } }
判断粘性广播,并且更新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)); } }
根据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 users for (int i = 0; i < users.length; i++) { if (mUserController.hasUserRestriction( UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) { continue; } List<BroadcastFilter> registeredReceiversForUser = mReceiverResolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, users[i]); if (registeredReceivers == null) { registeredReceivers = registeredReceiversForUser; } else if (registeredReceiversForUser != null) { registeredReceivers.addAll(registeredReceiversForUser); } } } else { registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false /*defaultOnly*/, userId); } }
final boolean replacePending = (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0; if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing broadcast: " + intent.getAction() + " replacePending=" + replacePending); int NR = registeredReceivers != null ? registeredReceivers.size() : 0; if (!ordered && NR > 0) { // If 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; }
将最终的结果发给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); } }
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到对应的数据结构当中。
调用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;
}
之后,我们最终会调用到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, mService.compatibilityInfoForPackageLocked(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,
mService.compatibilityInfoForPackageLocked(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——广播的注册、发送和接收过程
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。