一、DcTracker介紹
DcTracker是Telephony框架中負責數據業務的核心(類似於GsmCallTracker是通話業務核心,GsmServiceStateTracker是網絡CS業務的核心),所有對數據網絡的請求(打開關閉數據開關、切換數據卡、修改APN參數等)都會發送到該處理中心來處理。本節所涉及的大部分代碼也都在該對象中。下面我們先來看該對象的初始化流程。
DcTracker是在Phone對象創建過程中被創建的,確切來說,如果當前使用的是GSMPhone,那麼在GSMPhone創建過程中就會創建DcTracker對象: @GSMPhone.java
public GSMPhone (Context context, CommandsInterface ci, PhoneNotifier notifier, boolean unitTestMode) {
//創建DcTracker對象,傳遞的參數就是當前的GSMPhone對象
mDcTracker = new DcTracker(this);
}
那麼DcTracker是什麼屬性呢? public final class DcTracker extends DcTrackerBase {}
public abstract class DcTrackerBase extends Handler {}
從這裏看到,DcTracker的父類是DcTrackerBase,他們的本質都是Handler。然後來看DcTracker的初始化過程:
@DcTracker.java
public DcTracker(PhoneBase p) {
super(p);
mDataConnectionTracker = this;
//註冊監聽器
update();
//監聽APN狀態
mApnObserver = new ApnChangeObserver();
p.getContext().getContentResolver().registerContentObserver(Telephony.Carriers.CONTENT_URI, true, mApnObserver);
//從networkAttributes數組中初始化APN參數
initApnContexts();
for (ApnContext apnContext : mApnContexts.values()) {
IntentFilter filter = new IntentFilter();
filter.addAction(INTENT_RECONNECT_ALARM + '.' + apnContext.getApnType());
filter.addAction(INTENT_RESTART_TRYSETUP_ALARM + '.' + apnContext.getApnType());
mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
}
ConnectivityManager cm = (ConnectivityManager)p.getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
//添加各種網絡請求過濾器,用於根據這些過濾器發起不同的數據連接
mNetworkFilter = new NetworkCapabilities();
mNetworkFilter.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
mNetworkFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS);
mNetworkFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL);
mNetworkFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
mNetworkFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA);
mNetworkFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
mNetworkFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS);
mNetworkFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_IA);
mNetworkFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_RCS);
mNetworkFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_XCAP);
mNetworkFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS);
mNetworkFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
mNetworkFilter.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
//初始化數據網絡的NetworkFactory,並設置數據網絡的分值爲50
mNetworkFactory = new TelephonyNetworkFactory(this.getLooper(), p.getContext(), "TelephonyNetworkFactory", mNetworkFilter);
mNetworkFactory.setScoreFilter(50);
mNetworkFactoryMessenger = new Messenger(mNetworkFactory);
cm.registerNetworkFactory(mNetworkFactoryMessenger, "Telephony");
//初始化緊急APN
initEmergencyApnSetting();
addEmergencyApnSetting();
mProvisionActionName = "com.android.internal.telephony.PROVISION" + p.getPhoneId();
}
在上面這個初始化過程中,主要完成了三個任務:1、初始化各種監聽器;
2、初始化TelephonyNetworkFactory對象;
3、初始化一些基本的APN參數;
下面我們分別詳細介紹這三個過程。
二、DcTracker中監聽器的初始化
public void update() {
if (isActiveDataSubscription()) {
//註冊各種監聽器
registerForAllEvents();
//註冊SIM卡狀態監聽器
onUpdateIcc();
//mUserDataEnabled就是用戶是否打開網絡開關的標誌位,當爲0時,表示當前數據流量被關閉
mUserDataEnabled = Settings.Global.getInt(mPhone.getContext().getContentResolver(), Settings.Global.MOBILE_DATA, 1) == 1;
if (mPhone instanceof CDMALTEPhone) {
((CDMALTEPhone)mPhone).updateCurrentCarrierInProvider();
supplyMessenger();
} else if (mPhone instanceof GSMPhone) {
((GSMPhone)mPhone).updateCurrentCarrierInProvider();
supplyMessenger();
} else {
log("Phone object is not MultiSim. This should not hit!!!!");
}
} else {
unregisterForAllEvents();
log("update(): NOT the active DDS, unregister for all events!");
}
}
在這個方法裏面,首先遇到的是isActiveDataSubscription()的判斷,該方法定義爲: protected boolean isActiveDataSubscription() {
// FIXME This should have code like
// return (mPhone.getSubId() == SubscriptionManager.getDefaultDataSubId());
return true;
}
可以看到,其內部直接返回了true,貌似沒有什麼作用,其實這是Google在爲雙卡項目預留的判斷。對於雙卡的手機,由於任意時刻只能有一個SIM卡在上網,因此就需要根據用戶選擇的上網卡來配置不同的APN參數,該方法的原始意圖應該是在內部判斷當前的DcTracker是否就是用戶設置的上網SIM(用戶當前使用哪張卡上網可以通過SubscriptionManager.getDefaultDataSubId()方法查詢到SIM的SubID),如果是當前卡的話返回true,否則返回false。但是由於Google並未完善其雙卡邏輯,因此這裏直接返回了true。
接下來繼續來看判斷後的registerForAllEvents()的過程:
protected void registerForAllEvents() {
//監聽射頻是否打開,沒有處理動作
mPhone.mCi.registerForAvailable(this, DctConstants.EVENT_RADIO_AVAILABLE, null);
//監聽射頻是否可用,沒有處理動作
mPhone.mCi.registerForOffOrNotAvailable(this, DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
//監聽當前連接狀態,沒有處理動作
mPhone.mCi.registerForDataNetworkStateChanged(this, DctConstants.EVENT_DATA_STATE_CHANGED, null);
//監聽當前通話狀態,沒有處理動作
mPhone.getCallTracker().registerForVoiceCallEnded (this, DctConstants.EVENT_VOICE_CALL_ENDED, null);
//監聽當前通話狀態,沒有處理動作
mPhone.getCallTracker().registerForVoiceCallStarted (this, DctConstants.EVENT_VOICE_CALL_STARTED, null);
//監聽是否PS域Attach狀態
mPhone.getServiceStateTracker().registerForDataConnectionAttached(this, DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null);
//監聽是否PS域Detach狀態
mPhone.getServiceStateTracker().registerForDataConnectionDetached(this, DctConstants.EVENT_DATA_CONNECTION_DETACHED, null);
//監聽漫遊狀態,沒有處理動作
mPhone.getServiceStateTracker().registerForRoamingOn(this, DctConstants.EVENT_ROAMING_ON, null);
//監聽漫遊狀態,沒有處理動作
mPhone.getServiceStateTracker().registerForRoamingOff(this, DctConstants.EVENT_ROAMING_OFF, null);
mPhone.getServiceStateTracker().registerForPsRestrictedEnabled(this, DctConstants.EVENT_PS_RESTRICT_ENABLED, null);
mPhone.getServiceStateTracker().registerForPsRestrictedDisabled(this, DctConstants.EVENT_PS_RESTRICT_DISABLED, null);
//監聽接入技術狀態
mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged(this, DctConstants.EVENT_DATA_RAT_CHANGED, null);
}
這裏註冊了大量的監聽器,實際上很多都沒有對監聽動作進行處理(可能是爲了擴展後續代碼預留的),但是其中有兩個比較重要的監聽器,他們將會對上網事件產生影響,分別是:EVENT_DATA_CONNECTION_ATTACHED
----監聽PS的Attach事件,觸發時將進入onDataConnectionAttached()
EVENT_DATA_RAT_CHANGED
----監聽當前所註冊的網絡技術(LTE/UMTS/GSM)是否發生改變,觸發時將進入setupDataOnConnectableApns()
然後在update()中還對SIM卡狀態進行監聽:
protected void onUpdateIcc() {
if (mUiccController == null ) {
return;
}
IccRecords newIccRecords = getUiccRecords(UiccController.APP_FAM_3GPP);
IccRecords r = mIccRecords.get();
if (r != newIccRecords) {
if (r != null) {
r.unregisterForRecordsLoaded(this);
mIccRecords.set(null);
}
if (newIccRecords != null) {
mIccRecords.set(newIccRecords);
//監聽SIM各項數據是否載入完畢
newIccRecords.registerForRecordsLoaded( this, DctConstants.EVENT_RECORDS_LOADED, null);
}
}
}
這裏監聽的是SIM卡數據(SPN/FDN/ADN等)是否載入完畢。 除了以上的update()和onUpdateIcc()外,在DcTracker的構造方法裏面還註冊了對APN參數的監聽器:
public DcTracker(PhoneBase p) {
update();
mApnObserver = new ApnChangeObserver();
p.getContext().getContentResolver().registerContentObserver( Telephony.Carriers.CONTENT_URI, true, mApnObserver);
}
這裏監聽器內容爲: private class ApnChangeObserver extends ContentObserver {
public ApnChangeObserver () {
super(mDataConnectionTracker);
}
@Override
public void onChange(boolean selfChange) {
sendMessage(obtainMessage(DctConstants.EVENT_APN_CHANGED));
}
}
也就是說,當APN參數的數據庫(Telephony.Carriers.CONTENT_URI)發生改動時,將會觸發ApnChangeObserver,向DcTracker發送EVENT_APN_CHANGED的消息。至此,DcTracker中所有監聽器準備就緒,其中比較重要的監聽器被觸發時所產生的影響我們會在稍後過程中介紹。
三、DcTracker中NetworkFactory的創建過程
public DcTracker(PhoneBase p) {
ConnectivityManager cm = (ConnectivityManager)p.getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
//初始化數據網絡的NetworkFactory,並設置數據網絡的分值爲50
mNetworkFactory = new TelephonyNetworkFactory(this.getLooper(), p.getContext(), "TelephonyNetworkFactory", mNetworkFilter);
mNetworkFactory.setScoreFilter(50);
mNetworkFactoryMessenger = new Messenger(mNetworkFactory);
cm.registerNetworkFactory(mNetworkFactoryMessenger, "Telephony");
}
看一下該TelephonyNetworkFactory定義: private class TelephonyNetworkFactory extends NetworkFactory {
public TelephonyNetworkFactory(Looper l, Context c, String TAG, NetworkCapabilities nc) {
super(l, c, TAG, nc);
}
@Override
protected void needNetworkFor(NetworkRequest networkRequest, int score) {
ApnContext apnContext = apnContextForNetworkRequest(networkRequest);
if (apnContext != null) apnContext.incRefCount();
}
@Override
protected void releaseNetworkFor(NetworkRequest networkRequest) {
ApnContext apnContext = apnContextForNetworkRequest(networkRequest);
if (apnContext != null) apnContext.decRefCount();
}
}
其中的兩個方法分別用來覆蓋父類,needNetworkFor()是當其他高分值的網絡斷開時,用於開啓當前數據流量用的,而releaseNetworkFor()是當有更高分值的網絡打開時,關閉當前數據流量用的。更多的NetworkFactory使用與原理請點擊《連接管理中的評分機制》。
四、DcTracker中初始APN的創建過程
public DcTracker(PhoneBase p) {
//從networkAttributes數組中初始化APN參數
initApnContexts();
//初始化緊急APN
initEmergencyApnSetting();
addEmergencyApnSetting();
}
4.1、初始化networkAttributes數組中的APN參數
protected void initApnContexts() {
//載入networkAttributes數組
String[] networkConfigStrings = mPhone.getContext().getResources().getStringArray( com.android.internal.R.array.networkAttributes);
for (String networkConfigString : networkConfigStrings) {
NetworkConfig networkConfig = new NetworkConfig(networkConfigString);
ApnContext apnContext = null;
//根據不同類型創建不同APN參數
switch (networkConfig.type) {
case ConnectivityManager.TYPE_MOBILE:
apnContext = addApnContext(PhoneConstants.APN_TYPE_DEFAULT, networkConfig);
break;
case ConnectivityManager.TYPE_MOBILE_MMS:
apnContext = addApnContext(PhoneConstants.APN_TYPE_MMS, networkConfig);
break;
case ConnectivityManager.TYPE_MOBILE_EMERGENCY:
apnContext = addApnContext(PhoneConstants.APN_TYPE_EMERGENCY, networkConfig);
break;
default:
log("initApnContexts: skipping unknown type=" + networkConfig.type);
continue;
}
}
}
這個過程比較簡單,就是從networkAttributes數組中獲取所有APN字串,然後根據類型分別創建APN參數。
4.2、初始化緊急APN參數
1、通過initEmergencyApnSetting()載入緊急APN;
2、通過addEmergencyApnSetting()將緊急APN添加到APN列表中;
先來看載入過程:
private void initEmergencyApnSetting() {
String selection = "type=\"emergency\"";
Cursor cursor = mPhone.getContext().getContentResolver().query( Telephony.Carriers.CONTENT_URI, null, selection, null, null);
if (cursor != null) {
if (cursor.getCount() > 0) {
if (cursor.moveToFirst()) {
mEmergencyApn = makeApnSetting(cursor);
}
}
cursor.close();
}
}
這個載入過程其實就是從數據庫中搜索條件爲"type="emergency""的APN參數,那麼這個數據庫中的內容是哪裏來的呢?這些參數是在TelephonyProvider被創建的時候被初始化,下面簡單看一下初始化流程:
@TelephonyProvider.java
private void initDatabase(SQLiteDatabase db) {
Resources r = mContext.getResources();
//從apns文件讀取APN參數
XmlResourceParser parser = r.getXml(com.android.internal.R.xml.apns);
int publicversion = -1;
try {
XmlUtils.beginDocument(parser, "apns");
//解析該文件
publicversion = Integer.parseInt(parser.getAttributeValue(null, "version"));
loadApns(db, parser);
} catch (Exception e) {
} finally {
parser.close();
}
XmlPullParser confparser = null;
//從"etc/apns-conf.xml"配置文件讀取APN參數
File confFile = new File(Environment.getRootDirectory(), PARTNER_APNS_PATH);
FileReader confreader = null;
try {
confreader = new FileReader(confFile);
confparser = Xml.newPullParser();
confparser.setInput(confreader);
XmlUtils.beginDocument(confparser, "apns");
int confversion = Integer.parseInt(confparser.getAttributeValue(null, "version"));
if (publicversion != confversion) {
throw new IllegalStateException("Internal APNS file version doesn't match " + confFile.getAbsolutePath());
}
//將配置文件解析後存入數據庫
loadApns(db, confparser);
} catch (FileNotFoundException e) {
} catch (Exception e) {
} finally {
try { if (confreader != null) confreader.close(); } catch (IOException e) { }
}
}
從上面可以看到,從com.android.internal.R.xml.apns和"etc/apns-conf.xml"兩個路徑下讀取了APN的配置文件並解析,然後存入了數據庫,這些配置文件的配置內容如下: @apns-conf.xml
<?xml version="1.0" encoding="utf-8"?>
<apns version="8">
<apn carrier="T-Mobile US"
mcc="310"
mnc="260"
apn="epc.tmobile.com"
user="none"
server="*"
password="none"
mmsc="http://mms.msg.eng.t-mobile.com/mms/wapenc"
/>
<apn carrier="T-Mobile US 250"
mcc="310"
mnc="250"
apn="epc.tmobile.com"
user="none"
server="*"
password="none"
mmsc="http://mms.msg.eng.t-mobile.com/mms/wapenc"
/>
</apns>
從這個配置文件中可以看到,其每一項數據都包含了某個運營商的PLMN name、MCC、MNC、apn、user、server、password、mmsc等信息。上面是數據庫初始化的過程,回到DcTracker的緊急APN初始化過程中來,當從數據庫中搜索到"type="emergency""的緊急APN參數後,通過initEmergencyApnSetting()方法將這些參數放入mEmergencyApn中,接下來還需要通過addEmergencyApnSetting()方法把這些緊急APN存入統一的APN庫中:
private void addEmergencyApnSetting() {
if(mEmergencyApn != null) {
if(mAllApnSettings == null) {
mAllApnSettings = new ArrayList<ApnSetting>();
} else {
boolean hasEmergencyApn = false;
for (ApnSetting apn : mAllApnSettings) {
if (ArrayUtils.contains(apn.types, PhoneConstants.APN_TYPE_EMERGENCY)) {
//如果之前的SIM APN中已經包含了緊急APN,就無需再次添加
hasEmergencyApn = true;
break;
}
}
if(hasEmergencyApn == false) {
//將mEmergencyApn添加到APN列表中
mAllApnSettings.add(mEmergencyApn);
} else {
log("addEmergencyApnSetting - E-APN setting is already present");
}
}
}
}
通過以上步驟,緊急APN的創建過程就完成了。至此,DcTracker所有初始化工作全部完成,在這個過程中註冊了監聽器、創建了TelephonyNetworkFactory、初始化了緊急APN,接下來就是等待監聽器被觸發。