導讀:本文說明系統發送廣播的部分流程,如何利用Intent尋找到對應接收器。我們依然只關注接收器的排序問題
這篇文章主要是針對我前兩篇文章
android安全問題(四) 搶先開機啟動 - 結果篇
android安全問題(五) 搶先攔截簡訊 - 結果篇
現在給出第二步分的分析
下面就來看看發送廣播的流程
Context中的sendBroadCast函數的實現是在ContextImpl中,和發送廣播相關的有如下六個函數
void android.app.ContextImpl.sendBroadcast(Intent intent)
void android.app.ContextImpl.sendBroadcast(Intent intent, String receiverPermission)
void android.app.ContextImpl.sendOrderedBroadcast(Intent intent, String receiverPermission)
void android.app.ContextImpl.sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)
void android.app.ContextImpl.sendStickyBroadcast(Intent intent)
void android.app.ContextImpl.sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)
可以分為3組:1普通廣播;2Ordered廣播;3Sticky廣播
不論哪種,最後都會由ActivityManagerService處理
private final int broadcastIntentLocked(ProcessRecord callerApp, String callerPackage, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle map, String requiredPermission, boolean ordered, boolean sticky, int callingPid, int callingUid)
以第一種情況為例,流程圖大概是這個樣子的
ordered和sticky用來區分上面3組廣播
下面我們仔細看看這個方法都幹了些什麼
刪減了一些代碼
private final int broadcastIntentLocked(ProcessRecord callerApp, String callerPackage, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle map, String requiredPermission, boolean ordered, boolean sticky, int callingPid, int callingUid) { ...//處理特殊intent // Add to the sticky list if requested. ...//處理sticky廣播 // Figure out who all will receive this broadcast. List receivers = null; List<BroadcastFilter> registeredReceivers = null; try { if (intent.getComponent() != null) { // Broadcast is going to one specific receiver class... ActivityInfo ai = AppGlobals.getPackageManager(). getReceiverInfo(intent.getComponent(), STOCK_PM_FLAGS); if (ai != null) { receivers = new ArrayList(); ResolveInfo ri = new ResolveInfo(); ri.activityInfo = ai; receivers.add(ri); } } else { // Need to resolve the intent to interested receivers... if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) { receivers = AppGlobals.getPackageManager().queryIntentReceivers( intent, resolvedType, STOCK_PM_FLAGS); } registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false); } } catch (RemoteException ex) { // pm is in same process, this will never happen. } final boolean replacePending = (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0; ... int NR = registeredReceivers != null ? registeredReceivers.size() : 0; ...//如果廣播是非ordered,先處理動態註冊的接收器 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. BroadcastRecord r = new BroadcastRecord(intent, callerApp, callerPackage, callingPid, callingUid, requiredPermission, registeredReceivers, resultTo, resultCode, resultData, map, ordered, sticky, false); ...//mParallelBroadcasts只含有動態註冊的receiver boolean replaced = false; if (replacePending) { for (int i=mParallelBroadcasts.size()-1; i>=0; i--) { if (intent.filterEquals(mParallelBroadcasts.get(i).intent)) { if (DEBUG_BROADCAST) Slog.v(TAG, "***** DROPPING PARALLEL: " + intent); mParallelBroadcasts.set(i, r); replaced = true; break; } } } if (!replaced) { mParallelBroadcasts.add(r); scheduleBroadcastsLocked(); } registeredReceivers = null; NR = 0; } // Merge into one list. //如果廣播是ordered,合并靜態、動態接收器 //否則之前處理過動態接收器,這裡registeredReceivers為null int ir = 0; if (receivers != null) { ... //合并的過程,注意順序 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); } //如果動態接收器優先順序高,那麼就插到前面 //否則進入else,然後進行下一輪比較,拿下一個靜態接收器與之前的動態接收器比較,直到找到自己的位置才插入進列表中 //在這裡,調整好接收器的順序,同等優先順序的,顯然動態要在靜態前面 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 ((receivers != null && receivers.size() > 0) || resultTo != null) { BroadcastRecord r = new BroadcastRecord(intent, callerApp, callerPackage, callingPid, callingUid, requiredPermission, receivers, resultTo, resultCode, resultData, map, ordered, sticky, false); ... if (!replaced) { mOrderedBroadcasts.add(r); scheduleBroadcastsLocked(); } } return BROADCAST_SUCCESS;}
注意上面函數中提到的其中兩個成員變數
/** * List of all active broadcasts that are to be executed immediately * (without waiting for another broadcast to finish). Currently this only * contains broadcasts to registered receivers, to avoid spinning up * a bunch of processes to execute IntentReceiver components. */final ArrayList<BroadcastRecord> mParallelBroadcasts = new ArrayList<BroadcastRecord>();/** * List of all active broadcasts that are to be executed one at a time. * The object at the top of the list is the currently activity broadcasts; * those after it are waiting for the top to finish.. */final ArrayList<BroadcastRecord> mOrderedBroadcasts = new ArrayList<BroadcastRecord>();
如果是非ordered廣播,那麼mParallelBroadcasts將儲存所有動態接收器,然後合并的時候,mParallelBroadcasts設定為null,所以不會合并到receivers中
如果是ordered廣播,那麼mParallelBroadcasts將合并到receivers中
然後,不管是哪種廣播,最後都是調用scheduleBroadcastsLocked繼續處理
最終到processNextBroadcast函數上
在看processNextBroadcast函數之前,還有個問題需要解決
receivers = AppGlobals.getPackageManager().queryIntentReceivers(intent, resolvedType, STOCK_PM_FLAGS);registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false);
那就是查詢出來的List是什麼順序,之後的處理過程上面都已經看到了,但是這裡面是什麼順序我們還沒有看到
先看看動態註冊的Receiver情況
public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly) { String scheme = intent.getScheme(); ArrayList<R> finalList = new ArrayList<R>(); ... ArrayList<F> firstTypeCut = null; ArrayList<F> secondTypeCut = null; ArrayList<F> thirdTypeCut = null; ArrayList<F> schemeCut = null; //下面是一些匹配的細節,我們先不關注 // If the intent includes a MIME type, then we want to collect all of // the filters that match that MIME type. //這裡不涉及MIME,所以這裡resolvedType=null if (resolvedType != null) { ... } // If the intent includes a data URI, then we want to collect all of // the filters that match its scheme (we will further refine matches // on the authority and path by directly matching each resulting filter). if (scheme != null) { schemeCut = mSchemeToFilter.get(scheme); if (debug) Slog.v(TAG, "Scheme list: " + schemeCut); } // If the intent does not specify any data -- either a MIME type or // a URI -- then we will only be looking for matches against empty // data. if (resolvedType == null && scheme == null && intent.getAction() != null) { firstTypeCut = mActionToFilter.get(intent.getAction());//在addFilter的時候將其add進去,見上一篇部落格 if (debug) Slog.v(TAG, "Action list: " + firstTypeCut); } FastImmutableArraySet<String> categories = getFastIntentCategories(intent); //finalList就是我們的結果集 if (firstTypeCut != null) { //buildResolveList迴圈List,檢查是否符合條件,然後複製到finalList中 buildResolveList(intent, categories, debug, defaultOnly, resolvedType, scheme, firstTypeCut, finalList); } if (secondTypeCut != null) { buildResolveList(intent, categories, debug, defaultOnly, resolvedType, scheme, secondTypeCut, finalList); } if (thirdTypeCut != null) { buildResolveList(intent, categories, debug, defaultOnly, resolvedType, scheme, thirdTypeCut, finalList); } if (schemeCut != null) { buildResolveList(intent, categories, debug, defaultOnly, resolvedType, scheme, schemeCut, finalList); } sortResults(finalList); ... return finalList;}
這裡我們也看到關於排序代碼,具體如下,很簡單
@SuppressWarnings("unchecked")protected void sortResults(List<R> results) { Collections.sort(results, mResolvePrioritySorter);}// Sorts a List of IntentFilter objects into descending priority order.@SuppressWarnings("rawtypes")private static final Comparator mResolvePrioritySorter = new Comparator() { public int compare(Object o1, Object o2) { final int q1 = ((IntentFilter) o1).getPriority(); final int q2 = ((IntentFilter) o2).getPriority(); return (q1 > q2) ? -1 : ((q1 < q2) ? 1 : 0); }};
上面就是簡單的按優先順序排序,大的放在前面,否則位置不動
下面看看靜態註冊的Receiver情況,最終也會和動態註冊的Receiver一樣,調用同一個函數
public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags) { //ComponentName之後應該均為null,我們不討論只發給特定組件的情況,因為那樣不涉及優先順序和順序的問題 ComponentName comp = intent.getComponent(); if (comp == null) { if (intent.getSelector() != null) { intent = intent.getSelector(); comp = intent.getComponent(); } } if (comp != null) { List<ResolveInfo> list = new ArrayList<ResolveInfo>(1); ActivityInfo ai = getReceiverInfo(comp, flags); if (ai != null) { ResolveInfo ri = new ResolveInfo(); ri.activityInfo = ai; list.add(ri); } return list; } // reader //ComponentName=null,所以會執行下面代碼 synchronized (mPackages) { String pkgName = intent.getPackage(); //只考慮pkgName=null的情況,同一個package中,哪個receiver先接收到廣播暫時不關心 if (pkgName == null) { return mReceivers.queryIntent(intent, resolvedType, flags);//最終會調用IntentResolver.queryIntent,上面已經分析過 } final PackageParser.Package pkg = mPackages.get(pkgName); if (pkg != null) { return mReceivers.queryIntentForPackage(intent, resolvedType, flags, pkg.receivers); } return null; }}
現在看來,上面兩個查詢都是按優先順序從高到低排序的,如果優先順序相同,順序則保持不變
之後是調用scheduleBroadcastsLocked來發廣播給每一個receiver
至於廣播後續如何處理,我們就不再深究了
到這裡已經能看到應用中的接收順序了
總結:
情況分為兩種(scheduleBroadcastsLocked),ordered廣播和非ordered廣播
非ordered廣播
先處理動接收器,然後處理靜態接收器
ordered廣播
同時處理動態接收器和靜態接收器
先將動態接收器與靜態接收器合并,保持著與優先順序相同的順序,優先順序高的在前面,否則順序不變。靜態接收器與動態接收器優先順序相同的話,動態接收器在前
轉貼請保留以下連結
本人blog地址
http://su1216.iteye.com/
http://blog.csdn.net/su1216/