Android7.0 資料撥號前的準備工作
背景
在介紹PhoneApp的建立過程時,我們知道為了支援雙卡手機,PhoneFactory建立了兩個Phone物件。
然而由於通訊制式、功耗等的限制,目前底層的晶片廠商規定modem工作於DSDS模式下,於是同一時間內只有一個Phone具有上網的能力。
本文旨在揭示啟用Phone撥號能力的過程,即講述資料撥號前的準備工作。
版本
android 7.0
1 TelephonyProvider的啟動
資料業務在建立之前,必須有可用的APN,因此我們首先看看Android 7.0中APN載入的過程。
之前分析PhoneApp啟動過程時,我們知道PhoneApp的onCreate函式是靠ActivityThread.java中的handleBindApplication函式呼叫的。
private void handleBindApplication(AppBindData data) {
........
try {
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
//載入App中的provider
installContentProviders(app, data.providers);
...........
}
}
try {
mInstrumentation.onCreate(data.instrumentationArgs);
} catch (Exception e) {
.........
}
try {
//呼叫App的onCreate函式
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
..............
}
} finally {
.............
}
}
從上面的程式碼,我們知道在PhoneApp的onCreate被呼叫前,先載入了PhoneApp中的ContentProvider(PM解析xml得到這種包含關係)。
private void installContentProviders(Context context, List<ProviderInfo> providers) {
........
for (ProviderInfo cpi : providers) {
..........
//依次安裝provider
IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
............
}
try {
//釋出provider
ActivityManagerNative.getDefault().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
從TelephonyProvider的AndroidManifest.xml,我們知道TelephonyProvider就是執行在Phone程序中的,因此結合上面的程式碼,可以得出結論:
TelephonyProvider將在PhoneApp的onCreate被呼叫前被載入。
Android7.0 TelephonyProvider啟動後的過程,與Android6.0中一致。
之前的blog分析Android6.0中APN載入過程時,已經做過描述了,其實就是建立資料庫、解析xml檔案並存入資料庫的過程,此處不再贅述。
2 設定具有撥號能力的Phone
我們回憶一下PhoneApp啟動後,利用PhoneFactory的makeDefaultPhone建立物件的程式碼,其中:
.........
//建立了一個PhoneSwitcher
sPhoneSwitcher = new PhoneSwitcher(MAX_ACTIVE_PHONES, numPhones,
sContext, sc, Looper.myLooper(), tr, sCommandsInterfaces,
sPhones);
.........
sTelephonyNetworkFactories = new TelephonyNetworkFactory[numPhones];
for (int i = 0; i < numPhones; i++) {
//建立了兩個TelephonyNetworkFactory,分別與每個Phone關聯起來
sTelephonyNetworkFactories[i] = new TelephonyNetworkFactory(
sPhoneSwitcher, sc, sSubscriptionMonitor, Looper.myLooper(),
sContext, i, sPhones[i].mDcTracker);
}
在剛開機時,不插卡的情況下,上面程式碼提及的PhoneSwitcher和TelephonyNetworkFactory將決定具有撥號能力的Phone。
我們在這裡先分析不插卡情況下的流程,主要原因是:
框架對卡資訊有記錄,將會根據記錄資訊改變具有撥號能力的Phone。這個過程是通過呼叫Phone程序提供的介面完成的,我們以後再做分析。
2.1 PhoneSwitcher
我們先來看看PhoneSwitcher:
//PhoneSwitcher繼承自Handler
public class PhoneSwitcher extends Handler {
......
public PhoneSwitcher(......) {
........
//建立NetworkCapabilities,為建立NetworkFactory作準備
//NetworkCapabilities決定了NetworkFactory可以處理的Network Request種類
NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL);
netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA);
netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS);
netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_IA);
netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_RCS);
netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_XCAP);
netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS);
netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
netCap.setNetworkSpecifier(NetworkCapabilities.MATCH_ALL_REQUESTS_NETWORK_SPECIFIER);
//建立了一個NetworkFactory物件
NetworkFactory networkFactory = new PhoneSwitcherNetworkRequestListener(looper, context,
netCap, this);
//每個NetworkFactory僅能處理分數比自己低的NetworkRequest
//這裡設定分數為101,將能夠處理所有的request
networkFactory.setScoreFilter(101);
//註冊該NetworkFactory物件
networkFactory.register();
}
}
上面的程式碼涉及到了很多內容,以後有機會再分析,此時我們僅需要有一個映像就是:
PhoneSwitcher建立了一個能夠處理所有Network Request的NetworkFactory(實際上是子類),並呼叫了register方法。
我們來看看NetworkFactory的register方法:
//NetworkFactory繼承Handler
public class NetworkFactory extends Handler {
........
public void register() {
if (DBG) log("Registering NetworkFactory");
if (mMessenger == null) {
//構造Messenger物件,其中包含Handler
mMessenger = new Messenger(this);
//呼叫ConnectivityManager的registerNetworkFactory方法
ConnectivityManager.from(mContext).registerNetworkFactory(mMessenger, LOG_TAG);
}
}
..........
}
繼續跟進ConnectivityManager中的函式:
public static ConnectivityManager from(Context context) {
return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
}
public void registerNetworkFactory(Messenger messenger, String name) {
try {
//可以看到將呼叫用mService的registerNetworkFactory函式
mService.registerNetworkFactory(messenger, name);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
mService的型別為IConnectivityManager,其實是ConnectivityService的Binder通訊的服務端代理,因此上述程式碼最終將呼叫到ConnectivityService的registerNetworkFactory函式。
ConnectivityService是開機時,由SystemServer載入的,這裡我們不分析ConnectivityService啟動的過程,以後有機會單獨介紹ConnectivityService。這裡我們只需要知道ConnectivityService主要負責管理Android中的各種網路。
我們看看ConnectivityService的registerNetworkFactory函式:
@Override
public void registerNetworkFactory(Messenger messenger, String name) {
//許可權檢查
enforceConnectivityInternalPermission();
//構造NetworkFactoryInfo, 該變數用於在ConnectivityService中代表註冊的NetworkFactory
//注意Messager中,包含註冊NetworkFactory內部的Handler
NetworkFactoryInfo nfi = new NetworkFactoryInfo(name, messenger, new AsyncChannel());
//傳送訊息給給內部的Handler處理,將並行的介面呼叫變為序列的訊息處理
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_FACTORY, nfi));
}
ConnectivityService內部的handler將呼叫handleRegisterNetworkFactory處理EVENT_REGISTER_NETWORK_FACTORY訊息:
............
case EVENT_REGISTER_NETWORK_FACTORY: {
handleRegisterNetworkFactory((NetworkFactoryInfo)msg.obj);
break;
}
...........
private void handleRegisterNetworkFactory(NetworkFactoryInfo nfi) {
if (DBG) log("Got NetworkFactory Messenger for " + nfi.name);
//以鍵值對的方式,儲存NetworkFactory的messenger和對應的NetworkFactoryInfo
mNetworkFactoryInfos.put(nfi.messenger, nfi);
//呼叫AsyncChannel的connect方法,將ConnectivityService的TrackerHandler與NetworkFactory的Messenger聯絡起來
nfi.asyncChannel.connect(mContext, mTrackerHandler, nfi.messenger);
}
我們看看AsyncChannel的connect方法:
//此處傳遞的srcHandler是ConnectivityService的mTrackerHandler
//dstMessenger為NetworkFactory內部的Messenger
public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
if (DBG) log("connect srcHandler to the dstMessenger E");
// We are connected
connected(srcContext, srcHandler, dstMessenger);
// Tell source we are half connected
replyHalfConnected(STATUS_SUCCESSFUL);
if (DBG) log("connect srcHandler to the dstMessenger X");
}
public void connected(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
if (DBG) log("connected srcHandler to the dstMessenger E");
// Initialize source fields
mSrcContext = srcContext;
mSrcHandler = srcHandler;
mSrcMessenger = new Messenger(mSrcHandler);
// Initialize destination fields
mDstMessenger = dstMessenger;
//監聽目的端斷開
linkToDeathMonitor();
if (DBG) log("connected srcHandler to the dstMessenger X");
}
看到connnected函式,我們知道每個NetworkFactory註冊後,ConnectivityService將維護對應的NetworkFactoryInfo,對應的鍵值為NetworkFactory中的Messenger物件。
每個NetworkFactoryInfo中有一個AsyncChannel物件,該物件的源端Messenger包裹著ConnectivityService的mTrackerHandler,目的端Messenger為註冊NetworkFactory的Messenger。
ConnectivityService這麼做的目的是:簡化ConnectivityService與每個NetworkFactory通訊時的函式呼叫。
//這裡傳送CMD_CHANNEL_HALF_CONNECTED給ConnectivityService的mTrackerHandler
//replyTo NetworkFactory中的Messenger
private void replyHalfConnected(int status) {
Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_HALF_CONNECTED);
msg.arg1 = status;
msg.obj = this;
msg.replyTo = mDstMessenger;
if (!linkToDeathMonitor()) {
// Override status to indicate failure
msg.arg1 = STATUS_BINDING_UNSUCCESSFUL;
}
mSrcHandler.sendMessage(msg);
}
根據上面的程式碼,我們知道接下來應該是ConnectivityService的mTrackerHandler處理CMD_CHANNEL_HALF_CONNECTED事件。
mTrackerHandler的型別為ConnectivityService的內部類NetworkStateTrackerHandler:
private class NetworkStateTrackerHandler extends Handler {
...........
@Override
public void handleMessage(Message msg) {
//這裡的程式碼寫法還是很讚的,特意看了Android6.0之前的程式碼
//在Android6.0之前,是在一個handleMessage中,根據msg.what使用大量的switch-case語句
//現在這樣寫,雖然本質上仍是大量的switch-case,但至少作了個分類
if (!maybeHandleAsyncChannelMessage(msg) && !maybeHandleNetworkMonitorMessage(msg)) {
maybeHandleNetworkAgentMessage(msg);
}
}
.......
}
在這裡我們看看maybeHandleAsyncChannelMessage:
private boolean maybeHandleAsyncChannelMessage(Message msg) {
switch (msg.what) {
//說實話這個程式碼寫的辣眼睛
//自己寫switch-case,一直將default寫在最後,導致自己認為default是可以匹配所有的case
//實際上,當剩餘的case匹配不上時,default才會去匹配,default寫在之前之後,沒有關係
default:
return false;
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
//這裡我們呼叫這個函式
handleAsyncChannelHalfConnect(msg);
break;
}
case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
if (nai != null) nai.asyncChannel.disconnect();
break;
}
case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
handleAsyncChannelDisconnected(msg);
break;
}
}
return true;
}
private void handleAsyncChannelHalfConnect(Message msg) {
AsyncChannel ac = (AsyncChannel) msg.obj;
//從前面的程式碼,我們知道msg.replyTo為NetworkFactory對應的Messenger
if (mNetworkFactoryInfos.containsKey(msg.replyTo)) {
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
if (VDBG) log("NetworkFactory connected");
// A network factory has connected. Send it all current NetworkRequests.
//這裡的NetworkRequest等下說明
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
//NetworkRequest分為監聽網路用的,和申請網路用的
//僅處理申請網路用的
if (!nri.isRequest()) continue;
//判斷NetworkRequest是否有對應的NetworkAgentInfo
//當一個網路建立後,將會生成對應的NetworkAgent註冊到ConnectivityService
//這裡是取出NetworkRequest對應的NetworkAgentInfo
NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
//將CMD_REQUEST_NETWORK的訊息傳送給NetworkFactory,同時指定該request的分數
ac.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK,
(nai != null ? nai.getCurrentScore() : 0), 0, nri.request);
}
} else {
loge("Error connecting NetworkFactory");
mNetworkFactoryInfos.remove(msg.obj);
}
} else if (mNetworkAgentInfos.containsKey(msg.replyTo)) {
//這裡是傳送給NetworkAgent的訊息,我們暫時不關注
.........
}
}
上面的程式碼其實就是發訊息給NetworkFactory。這裡存在的疑點是:1、開機時是否有NetworkRequest?
2、傳送CMD_REQUEST_NETWORK時為什麼要攜帶分數?
我們先來分析第一個問題:
//建構函式
public ConnectivityService(.......) {
if (DBG) log("ConnectivityService starting up");
//初始時建立了DefaultRequest
mDefaultRequest = createInternetRequestForTransport(-1);
//建立對應的NetworkRequestInfo
NetworkRequestInfo defaultNRI = new NetworkRequestInfo(null, mDefaultRequest, new Binder(), NetworkRequestType.REQUEST);
//按鍵值對儲存
mNetworkRequests.put(mDefaultRequest, defaultNRI);
mNetworkRequestInfoLogs.log("REGISTER " + defaultNRI);
............
}
//初始時,ConnectivityService建立NetworkRequest傳入的引數為-1
private NetworkRequest createInternetRequestForTransport(int transportType) {
NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addCapability(NET_CAPABILITY_INTERNET);
netCap.addCapability(NET_CAPABILITY_NOT_RESTRICTED);
if (transportType > -1) {
netCap.addTransportType(transportType);
}
return new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId());
}
從上面的程式碼,我們知道了開機後,ConnectivityService中預設就會存在一個NetworkRequest,於是每當一個新的NetworkFactory註冊到ConnectivityService後,都會處理這個預設的NetworkRequest。
現在我們再來回答第二個問題,為什麼傳送CMD_REQUEST_NETWORK時,需要攜帶分數。
這個問題其實涉及了Android框架,管理整個網路的架構。具體的情況,今後介紹ConnectivityService時,會詳細介紹。
在這裡直接丟擲近似的答案:Android通過ConnectivityService管理所有的網路,每一種網路都有各自的NetworkFactory。當NetworkFactory收到NetworkRequest後,將建立實際的網路物件。ConnectivityService用NetworkAgent和NetworkAgentInfo來抽象實際的網路物件的一些特性(還有其它的物件,例如Network,LinkProperties等共同抽象網路)。
然而,同一個裝置在同一時間內不需要連線多個網路。比如說,使用者的目的就是上網,但WiFi和行動網路均能滿足使用者上網的需求,因此裝置沒有必要同時連線WiFi和行動網路。因此,Android需要一種機制來權衡各種網路的是否應該保留。
為此android引入了分數的概念,當兩個網路能同時滿足一個NetworkRequest時,分數高者就留下。根據這個規則,我們其實也知道如果兩個網路分別滿足不同的NetworkRequest時,是可以共存的。
以上是基本的結論,今後分析ConnectivityService時,會分析對應的原始碼。
知道原理後,現在回過頭來看看傳送CMD_REQUEST_NETWORK時,為什麼要攜帶分數。
當訊息傳送給NetworkFactory後,NetworkFactory如果能滿足NetworkRequest的需求,需要生成對應當NetworkAgent註冊到ConnectivityService。如果這個新生成的NetworkAgent的分數,比之前滿足NetworkRequest的已存在的NetworkAgent分數低,那麼這個NetworkAgent將被處理掉。
因此,與其浪費資源生成一個無用的NetworkAgent,不如一開始就讓NetworkFactory通過比較分數,不處理高分NetworkRequest。
這種設計類似於從聲源阻止噪聲吧!
回答完問題後,讓我們繼續回到之前ConnectivityService的流程,接下來看看AsyncChannel如何傳送訊息:
//一層層的sendMessage呼叫,最後會呼叫到這個sendMessage
public void sendMessage(Message msg) {
//根據前面的分析,我們知道ConnectivityService中NetworkFactoryInfo的AsyncChannel中
//mSrcMessenger中包裹的是mTrackerHandler
msg.replyTo = mSrcMessenger;
try {
//DstMessenger中包裹的是NetworkFactory中的handler
//binder通訊,發往了NetworkFactory
mDstMessenger.send(msg);
} catch (RemoteException e) {
replyDisconnected(STATUS_SEND_UNSUCCESSFUL);
}
}
至此,訊息終於又發回了PhoneSwitcher中建立的NetworkFactory,訊息的處理由父類NetworkFactory處理:
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case CMD_REQUEST_NETWORK: {
//呼叫該函式處理
handleAddRequest((NetworkRequest)msg.obj, msg.arg1);
break;
}
case CMD_CANCEL_REQUEST: {
handleRemoveRequest((NetworkRequest) msg.obj);
break;
}
case CMD_SET_SCORE: {
handleSetScore(msg.arg1);
break;
}
case CMD_SET_FILTER: {
handleSetFilter((NetworkCapabilities) msg.obj);
break;
}
}
}
@VisibleForTesting
protected void handleAddRequest(NetworkRequest request, int score) {
//判斷之前是否處理過該NetworkRequest
NetworkRequestInfo n = mNetworkRequests.get(request.requestId);
if (n == null) {
if (DBG) log("got request " + request + " with score " + score);
n = new NetworkRequestInfo(request, score);
mNetworkRequests.put(n.request.requestId, n);
} else {
if (VDBG) log("new score " + score + " for exisiting request " + request);
//對於處理過的NetworkRequest,僅更新器分數
//原因是:ConnectivityService中匹配該NetworkRequest的NetworkAgent可能變化,所以需要更新分數
n.score = score;
}
if (VDBG) log(" my score=" + mScore + ", my filter=" + mCapabilityFilter);
//評估NetworkRequest
evalRequest(n);
}
private void evalRequest(NetworkRequestInfo n) {
if (VDBG) log("evalRequest");
//n.requested == false表示該request未被處理過
//n.score < mScore表示request的分數小於NetworkFactory的分數(子類定義)
//satisfiedByNetworkCapabilities是為了判斷NetworkFactory(子類)的網路能力能滿足NetworkRequest
//acceptRequest函式衡返回true,這是留給子類覆蓋用的吧,一般沒有覆蓋
if (n.requested == false && n.score < mScore &&
n.request.networkCapabilities.satisfiedByNetworkCapabilities(
mCapabilityFilter) && acceptRequest(n.request, n.score)) {
if (VDBG) log(" needNetworkFor");
//進入子類的實現
needNetworkFor(n.request, n.score);
n.requested = true;
} else if (n.requested == true &&
(n.score > mScore || n.request.networkCapabilities.satisfiedByNetworkCapabilities(
mCapabilityFilter) == false || acceptRequest(n.request, n.score) == false)) {
if (VDBG) log(" releaseNetworkFor");
releaseNetworkFor(n.request);
n.requested = false;
} else {
if (VDBG) log(" done");
}
}
繞了一圈,我們現在可以看看PhoneSwitcher中建立的NetworkFactory的needNetworkFor函式:
@Override
protected void needNetworkFor(NetworkRequest networkRequest, int score) {
if (VDBG) log("needNetworkFor " + networkRequest + ", " + score);
Message msg = mPhoneSwitcher.obtainMessage(EVENT_REQUEST_NETWORK);
msg.obj = networkRequest;
//傳送訊息給自己的handler處理,將呼叫PhoneSwitcher中的onRequestNetwork處理
msg.sendToTarget();
}
private void onRequestNetwork(NetworkRequest networkRequest) {
//利用NetworkRequest構造DcRequest
final DcRequest dcRequest = new DcRequest(networkRequest, mContext);
//之前沒有處理過的dcRequest,才會進行處理
if (mPrioritizedDcRequests.contains(dcRequest) == false) {
mPrioritizedDcRequests.add(dcRequest);
//根據優先順序進行排序
Collections.sort(mPrioritizedDcRequests);
//評估,REQUESTS_CHANGED的值為true
onEvaluate(REQUESTS_CHANGED, "netRequest");
}
}
這裡的唯一的問題就是“根據優先順序排序”的概念。
我們花點時間來看看DcRequest這個類:
public class DcRequest implements Comparable<DcRequest> {
..........
public DcRequest(NetworkRequest nr, Context context) {
//初始化優先順序排序規則
initApnPriorities(context);
networkRequest = nr;
//從NetworkReqeust得出對應的匹配APN id
apnId = apnIdForNetworkRequest(networkRequest);
//得到這個NetworkReqeust的優先順序
priority = priorityForApnId(apnId);
}
.........
private void initApnPriorities(Context context) {
synchronized (sApnPriorityMap) {
//初始化優先順序配置僅用執行一次
if (sApnPriorityMap.isEmpty()) {
//解析xml檔案,得到配置資訊
String[] networkConfigStrings = context.getResources().getStringArray(
com.android.internal.R.array.networkAttributes);
for (String networkConfigString : networkConfigStrings) {
NetworkConfig networkConfig = new NetworkConfig(networkConfigString);
//從配置資訊中,得到apnId
final int apnId = ApnContext.apnIdForType(networkConfig.type);
//以鍵值對的方式,將apnId和優先順序資訊放入到sApnPriorityMap中
sApnPriorityMap.put(apnId, networkConfig.priority);
}
}
}
}
.........
private int apnIdForNetworkRequest(NetworkRequest nr) {
NetworkCapabilities nc = nr.networkCapabilities;
// For now, ignore the bandwidth stuff
if (nc.getTransportTypes().length > 0 &&
nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == false) {
return APN_INVALID_ID;
}
int apnId = APN_INVALID_ID;
boolean error = false;
//其實就是根據NetworkRequest申請的NetworkCapbility,來決定對應的apnId
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
if (apnId != APN_INVALID_ID) error = true;
apnId = APN_DEFAULT_ID;
}
if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) {
if (apnId != APN_INVALID_ID) error = true;
apnId = APN_MMS_ID;
}
......
return apnId;
}
..........
public int compareTo(DcRequest o) {
return o.priority - priority;
}
}
從上面的程式碼,我們知道DcRequest其實就是根據xml中的配置檔案得到每個Apn對應的優先順序;然後,根據每個NetworkRequest需要的NetworkCapabilities得到對應的apnId;最終,根據apnId的優先順序,完成對NetworkRequest的優先順序排序。
其中,networkAttributes定義於frameworks/base/core/res/res/values/config.xml中:
<string-array translatable="false" name="networkAttributes">
<item>"wifi,1,1,1,-1,true"</item>
<item>"mobile,0,0,0,-1,true"</item>
<item>"mobile_mms,2,0,2,60000,true"</item>
<item>"mobile_supl,3,0,2,60000,true"</item>
<item>"mobile_hipri,5,0,3,60000,true"</item>
<item>"mobile_fota,10,0,2,60000,true"</item>
<item>"mobile_ims,11,0,2,60000,true"</item>
<item>"mobile_cbs,12,0,2,60000,true"</item>
<item>"wifi_p2p,13,1,0,-1,true"</item>
<item>"mobile_ia,14,0,2,-1,true"</item>
<item>"mobile_emergency,15,0,2,-1,true"</item>
</string-array>
根據NetworkConfig的建構函式,我們看出每一行的第4位表示優先順序。結合DcReqeust中的compareTo函式,我們知道數字越大,優先順序越高,例如mobile_mms的優先順序要高於mobile:
public NetworkConfig(String init) {
String fragments[] = init.split(",");
name = fragments[0].trim().toLowerCase(Locale.ROOT);
type = Integer.parseInt(fragments[1]);
radio = Integer.parseInt(fragments[2]);
priority = Integer.parseInt(fragments[3]);
restoreTime = Integer.parseInt(fragments[4]);
dependencyMet = Boolean.parseBoolean(fragments[5]);
}
現在繼續回到PhoneSwitcher處理NetworkRequest的流程中來,PhoneSwitcher根據優先順序對NetworkRequest排序後,將呼叫onEvaluate方法:
private void onEvaluate(boolean requestsChanged, String reason) {
........
//此時,傳入的引數為true
boolean diffDetected = requestsChanged;
//檢測卡資訊是否發生變化,我們此時分析不插卡的流程,暫不關注這個
//即使插了卡,也不影響當前流程
........
if (diffDetected) {
List<Integer> newActivePhones = new ArrayList<Integer>();
//按優先順序依次處理DcRequest
for (DcRequest dcRequest : mPrioritizedDcRequests) {
//根據networkRequest中NetworkCapabilities攜帶的Specifier引數,決定phoneId
int phoneIdForRequest = phoneIdForRequest(dcRequest.networkRequest);
if (phoneIdForRequest == INVALID_PHONE_INDEX) continue;
if (newActivePhones.contains(phoneIdForRequest)) continue;
newActivePhones.add(phoneIdForRequest);
//目前mMaxActivePhones的值為1,意味者找到優先順序最高的DcRequest對應phone後,結束迴圈
if (newActivePhones.size() >= mMaxActivePhones) break;
}
..........
for (int phoneId = 0; phoneId < mNumPhones; phoneId++) {
if (newActivePhones.contains(phoneId) == false) {
//去啟用 無法處理最高優先順序DcRequest的phone 的資料撥號能力
deactivate(phoneId);
}
}
// only activate phones up to the limit
for (int phoneId : newActivePhones) {
//啟用 能處理最高優先順序DcRequest的phone 的資料撥號能力
activate(phoneId);
}
}
}
private int phoneIdForRequest(NetworkRequest netRequest) {
//在此流程中,specifier的值為空
//一般傳送彩信,建立彩信網路時,才會指定specifier為對應卡的subId
String specifier = netRequest.networkCapabilities.getNetworkSpecifier();
int subId;
if (TextUtils.isEmpty(specifier)) {
//為空時,取subId為預設資料卡的subId
//我們分析不插卡的流程時,mDefaultDataSubscription為初始值0
subId = mDefaultDataSubscription;
} else {
subId = Integer.parseInt(specifier);
}
int phoneId = INVALID_PHONE_INDEX;
if (subId == INVALID_SUBSCRIPTION_ID) return phoneId;
for (int i = 0 ; i < mNumPhones; i++) {
if (mPhoneSubscriptions[i] == subId) {
//我們分析不插卡的流程時,phoneId最後取0
//實際插卡情況,將有specifier來具體決定
phoneId = i;
break;
}
}
return phoneId;
}
最後,我們來看看deactivate和activate函式:
private void deactivate(int phoneId) {
PhoneState state = mPhoneStates[phoneId];
if (state.active == false) return;
//記錄對應phone的啟用狀態
state.active = false;
log("deactivate " + phoneId);
state.lastRequested = System.currentTimeMillis();
//mCommandsInterfaces[phoneId]其實就是RIL物件
//這裡其實就是通過RIL傳送訊息給modem,取消對應phone的資料撥號能力
mCommandsInterfaces[phoneId].setDataAllowed(false, null);
//通知觀察者
mActivePhoneRegistrants[phoneId].notifyRegistrants();
}
private void activate(int phoneId) {
PhoneState state = mPhoneStates[phoneId];
if (state.active == true) return;
state.active = true;
log("activate " + phoneId);
state.lastRequested = System.currentTimeMillis();
//同樣,通過RIL傳送訊息
mCommandsInterfaces[phoneId].setDataAllowed(true, null);
//通知觀察者
mActivePhoneRegistrants[phoneId].notifyRegistrants();
}
PhoneSwitcher的上述流程,最終激活了一個Phone的資料撥號能力,完成撥號前的第一部分準備工作。
2.2 TelephonyNetworkFactory
PhoneSwitcher只是決定了那一個Phone具有撥號能力,在實際撥號前還需要啟用可用的APN。這一部分工作需要TelephonyNetworkFactory來主動承擔。
public class TelephonyNetworkFactory extends NetworkFactory {
..........
public TelephonyNetworkFactory(.....) {
super(looper, context, "TelephonyNetworkFactory[" + phoneId + "]", null);
//建立內部的handler
mInternalHandler = new InternalHandler(looper);
//設定TelephonyNetworkFactory的網路能力,決定了它能處理的NetworkReqeust
setCapabilityFilter(makeNetworkFilter(subscriptionController, phoneId));
//NetworkFactory的分數為50
setScoreFilter(TELEPHONY_NETWORK_SCORE);
............
//建立時,TelephonyNetworkFactory是非啟用的
mIsActive = false;
//是PhoneSwitcher的觀察者
mPhoneSwitcher.registerForActivePhoneSwitch(mPhoneId, mInternalHandler,
EVENT_ACTIVE_PHONE_SWITCH, null);
//監聽卡變化的情況
............
//同樣註冊到ConnectivityService
register();
}
private NetworkCapabilities makeNetworkFilter(SubscriptionController subscriptionController,
int phoneId) {
final int subscriptionId = subscriptionController.getSubIdUsingPhoneId(phoneId);
return makeNetworkFilter(subscriptionId);
}
//指定了TelephonyNetworkFactory的NetworkCapabilities
private NetworkCapabilities makeNetworkFilter(int subscriptionId) {
NetworkCapabilities nc = new NetworkCapabilities();
nc.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_IA);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_RCS);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_XCAP);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
nc.setNetworkSpecifier(String.valueOf(subscriptionId));
return nc;
}
}
從上面的程式碼我們知道,在初始時每個Phone對應的TelephonyNetworkFactory的啟用態均是false,並且均註冊到了ConnectivityService。於是,與PhoneSwitcher一樣,TelephonyNetworkFactory也會收到ConnectivityService傳送的NetworkReqeust請求。
由於ConnectivityService建立的DefaultRequest只要求Internet能力,並且在沒有建立其它網路的條件下(沒有連線WiFi等網路),DefaultRequest的分數為0,因此兩個Phone對應的TelephonyNetworkFactory均會處理ConnectivityService傳送的NetworkReqeust請求。
與PhoneSwitcher一樣,我們看看TelephonyNetworkFactory的needNetworkFor函式:
@Override
public void needNetworkFor(NetworkRequest networkRequest, int score) {
//傳送訊息,由onNeedNetworkFor函式處理
Message msg = mInternalHandler.obtainMessage(EVENT_NETWORK_REQUEST);
msg.obj = networkRequest;
msg.sendToTarget();
}
private void onNeedNetworkFor(Message msg) {
NetworkRequest networkRequest = (NetworkRequest)msg.obj;
boolean isApplicable = false;
LocalLog localLog = null;
//當NetworkReqeust的specifier為空時
if (TextUtils.isEmpty(networkRequest.networkCapabilities.getNetworkSpecifier())) {
// request only for the default network
localLog = mDefaultRequests.get(networkRequest);
if (localLog == null) {
localLog = new LocalLog(REQUEST_LOG_SIZE);
localLog.log("created for " + networkRequest);
mDefaultRequests.put(networkRequest, localLog);
//只有default phone才能處理這個NetworkReqeust
isApplicable = mIsDefault;
}
} else {
//當NetworkReqeust的specifier不為空時,只要之前沒有處理過,就可以處理
//因為在收到NetworkRequest時,父類的evalRequest中,已經通過NetworkCapabilities匹配過了
localLog = mSpecificRequests.get(networkRequest);
if (localLog == null) {
localLog = new LocalLog(REQUEST_LOG_SIZE);
mSpecificRequests.put(networkRequest, localLog);
isApplicable = true;
}
}
//只有TelephonyNetworkFactory處於啟用態,並且能夠處理時,才進入該分支
if (mIsActive && isApplicable) {
String s = "onNeedNetworkFor";
localLog.log(s);
log(s + " " + networkRequest);
mDcTracker.requestNetwork(networkRequest, localLog);
} else {
String s = "not acting - isApp=" + isApplicable + ", isAct=" + mIsActive;
localLog.log(s);
log(s + " " + networkRequest);
}
}
從上面的程式碼我們知道,在TelephonyNetworkFactory被啟用前,它收到NetworkRequest的請求,但不會進行實際操作,直到PhoneSwitcher完成了啟用Phone撥號能力的操作。
在TelephonyNetworkFactory的建構函式中,向PhoneSwitcher註冊了觀察物件,當發現Phone的啟用態改變後,將向內部handler傳送EVENT_ACTIVE_PHONE_SWITCH訊息。內部handler收到訊息後,將呼叫onActivePhoneSwitch進行處理。
private void onActivePhoneSwitch() {
//根據PhoneSwitcher中記錄的phoneState決定自己是否active
final boolean newIsActive = mPhoneSwitcher.isPhoneActive(mPhoneId);
//自己的active狀態改變後,處理request
if (mIsActive != newIsActive) {
mIsActive = newIsActive;
String logString = "onActivePhoneSwitch(" + mIsActive + ", " + mIsDefault + ")";
if (DBG) log(logString);
//mIsActive的值,決定了requestNetwork,還是releaseNetwork
//android7.0中,defaultRequest與specificRequest分別裝在了不同的map中
if (mIsDefault) {
applyRequests(mDefaultRequests, (mIsActive ? REQUEST : RELEASE), logString);
}
applyRequests(mSpecificRequests, (mIsActive ? REQUEST : RELEASE), logString);
}
}
從上面的程式碼,可以看出當PhoneSwitcher改變TelephonyNetworkFactory的啟用狀態後,TelephonyNetworkFactory將根據各自的狀態,呼叫applyRequests函式:
private void applyRequests(HashMap<NetworkRequest, LocalLog> requestMap, boolean action,
String logStr) {
for (NetworkRequest networkRequest : requestMap.keySet()) {
LocalLog localLog = requestMap.get(networkRequest);
localLog.log(logStr);
if (action == REQUEST) {
//我們關注active phone的操作
mDcTracker.requestNetwork(networkRequest, localLog);
} else {
mDcTracker.releaseNetwork(networkRequest, localLog);
}
}
}
根據上面的程式碼,我們知道了流程走到了DcTracker。
DcTracker是PhoneFactory建立GsmCdmaPhone時,在GsmCdmaPhone內部建立的,與TelephonyNetworkFactory一樣,每個phone物件持有一個。
public GsmCdmaPhone(.....) {
........
mDcTracker = mTelephonyComponentFactory.makeDcTracker(this);
........
}
TelephonyComponentFactory是一個單例物件:
public static TelephonyComponentFactory getInstance() {
if (sInstance == null) {
sInstance = new TelephonyComponentFactory();
}
return sInstance;
}
public DcTracker makeDcTracker(Phone phone) {
return new DcTracker(phone);
}
我們回到撥號前的準備流程,看看DcTracker的requestNetwork函式:
public void req