快速入口: Apn_change完整时序图
1. APN概念
APN全称是Access Point Name,中文即接入点,是通过手机上网时必须配置的一个参数,它决定了手机通过哪种接入方式来访问网络。
2. Apn参数及初始化
Apn的创建在ContentProvider里面,他所在的Process启动了,ContentProvider就会在ActivityThread中启动,流程如下:
注:统一使用@类.方法表示流程中的方法
@SQLiteOpenHelper.getDatabaseLocked
@TelephonyProvider.DatabaseHelper.onCreate
createCarriersTable(db, CARRIERS_TABLE);//创建数据库(carriers)
initDatabase(db);//初始化apn数据
Carriers内的字段都定义在Telephony.java文件的子类Carriers中,各个字段详情见附件1([Carriers参数定义]。
Android的网络配置作为资源文件写入了XML(/frameworks/base/core/res/res/xml/apns.xml),这个资源文件作为Android的默认Apns配置,不建议修改该文件,且一般为空。因为Apn的配置是根据不同的硬件产品而不同,所以为不同的硬件产品建立各自的配置文件(system/etc/apns-conf.xml) ,而不去改动默认的配置文件(apns.xml)。
在apn数据库的初始化中,会先加载系统apn然后加载厂商apn。以下为部分代码:
// Read internal APNS data
XmlResourceParser parser = r.getXml(com.android.internal.R.xml.apns)
loadApns(db, parser);
// Read external APNS data (partner-provided)
File confFile = new File(Environment.getRootDirectory(), "etc/apns-conf.xml");
loadApns(db, confparser);
3. Apn数据库的增删改查
(1)首先创建URI连接:
public Uri createUri = Uri.parse("content://telephony/carriers");
(2)然后根据前后文获取ContentResolver访问数据库:
ContentResolver contentResolver =context.getContentResolver();
(3)写一个要修改的参数对,contentValues 里面使用HashMap保存:
ContentValues contentValues =new ContentValues();
contentValues.put("name", apn.getName());
(4)增删改查语句:
contentResolver.insert(createUri, contentValues);
contentResolver.delete(createUri,"_id=?",new String[] { id });
contentResolver.update(createUri, contentValues, null,null);
contentResolver.query(createUri,projection,selection,selectionArgs,sortOrder)
4. 上层设置默认apn
用户通过设置界面进入apn选项界面,当更换当前的apn选项时,会更新当前的apn数据库,然后就会触发重建数据连接流程,时序图如下:
4.1上层UI进入apn界面流程
在设置界面中点击“移动网络”选项,进入手机移动网络设置界面。然后点击“接入点名称”时会触发点击事件,然后通过intent又返回到设置里面开启apn界面,显示当前apn项,逻辑如下:
@leui_settings_headers//设置界面布局文件
android:targetClass="com.android.phone.MobileNetworkSettings">
@MobileNetworkSettings//移动网络界面创建Options
mGsmUmtsOptions = new GsmUmtsOptions(this, prefSet, mPhone.getPhoneId());
@GsmUmtsOptions//options界面就是“接入点名称”选项菜单
intent.setAction(Settings.ACTION_APN_SETTINGS);//开启ApnSettings
@ApnSettings.fillList//打开apn界面后遍历当前apn列表并显示
@ApnPreference//承载一个apn显示列
this(context, attrs, R.attr.apnPreferenceStyle);//引入apn的xml文件
@res/values/themes.xml:129:
<item name="apnPreferenceStyle">@style/ApnPreference</item>
@res/values/styles.xml:351://apn的界面有几次跳转,最后在styles中
<style name="ApnPreference">
<item name="android:layout">@layout/apn_preference_layout</item>
</style>
@apn_preference_layout//apn布局文件
4.2 UI重选apn
在apn界面点击更换一个apn后,会通过uri更新当前的apn数据库。接着Dctracker监听到了数据库的变化,从而开始重新创建数据连接:
@ApnSettings.onPreferenceChange//ui改动preferapn
setSelectedApnKey((String) newValue);
@ApnSettings.setSelectedApnKey//更新apn数据库
resolver.update(getUri(PREFERAPN_URI), values, null, null);
@DcTracker//mApnObserver监听apn数据库变化
p.getContext().getContentResolver().registerContentObserver(Telephony.Carriers.CONTENT_URI, true, mApnObserver);
@DcTracker.ApnChangeObserver.onChange
sendMessage(obtainMessage(DctConstants.EVENT_APN_CHANGED));
@DcTracker.handleMessage//消息id为270355
case DctConstants.EVENT_APN_CHANGED//(270355)
@DcTracker.onApnChanged//创建数据连接的方法
tryRestartDataConnections(true, Phone.REASON_APN_CHANGED);
5. DcTracker创建数据连接
这里面,我们首先说一下会触发Dctraker创建数据连接的几个消息:
EVENT_APN_CHANGED//(270335)监听apn数据库变化
EVENT_RECORDS_LOADED//(270338)卡加载
EVENT_VOICE_CALL_ENDED//(270344)
EVENT_ROAMING_ON//(270347)
EVENT_ROAMING_OFF//(270348)
EVENT_DATA_CONNECTION_ATTACHED//(270352)
EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED//(270357)
EVENT_DATA_RAT_CHANGED//(270377)监听底层主动上报的 RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED
EVENT_MODEM_DATA_PROFILE_READY//(270379)
在重选apn引起的创建数据连接流程中,首先通过网络类型获取卡信息,读取其中的mcc mnc值,再通过该值查找apn数据库创建apn列表,这些逻辑都在方法createAllApnList里面;有了apn列表之后,会先找一个初始的apn下发到modem,这一步的作用目前不是很明确,像创建数据连接的一个预处理。它在以下几种情况下被调用:
EVENT_RECORDS_LOADED
EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED
EVENT_DATA_RAT_CHANGED
EVENT_APN_CHANGED
然后依据当前情况清空原有数据连接,重新创建。
@DcTracker.tryRestartDataConnections//开始创建连接
createAllApnList//创建apn列表
getPreferredApn//有优选apn则获取
setPreferredApn(-1);//没有则设为-1
setInitialAttachApn//设置初始apn(ia->Preferred->default->first)会通过RIL下发RIL_REQUEST_SET_INITIAL_ATTACH_APN命令,预绑定
cleanUpAllConnections
setupDataOnConnectableApns
接着setupDataOnConnectableApns方法选用apn列表中合适的apn开始创建数据连接。trySetupData里面的buildWaitingApns方法会对apn列表进行进一步过滤,有PreferredApn则加入列表并直接返回,使用该apn;如没有preferredApn会把apn表中满足条件的apn都加入列表并返回,再使用第一个apn创建连接。
@DcTracker.setupDataOnConnectableApns
trySetupData
@DcTracker.trySetupData
buildWaitingApns
setupData
@DcTracker.setupData
apnSetting = apnContext.getNextWaitingApn();//使用第一个
dcac.bringUp(apnContext...);
经过一系列的验证,各个参数准备完毕,发出创建连接请求。走到状态机,StateMachine的子类接受消息,能继续创建连接的有DcInactiveState/DcRetryingState,这里我们用的是DcInactiveState。
@DcAsyncChannel.bringUp
sendMessage(DataConnection.EVENT_CONNECT...)
@StateMachine.SmHandler.processMsg
while (!curStateInfo.state.processMessage(msg)) {
@DataConnection.DcInactiveState.processMessage//当前的子类状态处理
onConnect(mConnectionParams);//EVENT_CONNECT//(关键字)
transitionTo(mActivatingState);//状态由断开变为正在连接
@DataConnection.onConnect
mPhone.mCi.setupDataCall(Integer.toString(cp.mRilRat + 2)...);//rillrat代表网络类型,其值+2传到ril。[部分log](#_部分log:)及[网络类型](#_网络类型:)详情参见附件2。
@RIL.setupDataCall
RIL_REQUEST_SETUP_DATA_CALL//SETUP_DATA_CALL(关键字)
时序图如下:
6. 底层网络状态变化触发创建apn连接的流程
底层网络状态发生变化,会发出UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED消息通知上层。
@RIL.processUnsolicited
case RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED:
mVoiceNetworkStateRegistrants.notifyRegistrants(new AsyncResult(null, null, null));//mVoiceNetworkStateRegistrants发出通知
@GsmServiceStateTracker//ServiceStateTracker//监听该通知
mCi.registerForVoiceNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED, null);
mVoiceNetworkStateRegistrants.add(r);
上层收到通知,会向底层重新请求新的数据连接的各个参数,具体下发的消息如下(以GsmServiceStateTracker为例):
EVENT_POLL_STATE_OPERATOR//spn
EVENT_POLL_STATE_GPRS//RilData
EVENT_POLL_STATE_REGISTRATION//服务状态,RilVoice
EVENT_POLL_STATE_NETWORK_SELECTION_MODE//网络模式
代码逻辑如下:
@GsmServiceStateTracker.handleMessage
case EVENT_NETWORK_STATE_CHANGED
@GsmServiceStateTracker.pollState//下发四个命令
mPollingContext[0]++;
mCi.getOperator(obtainMessage(EVENT_POLL_STATE_OPERATOR, mPollingContext));
mPollingContext[0]++;
mCi.getDataRegistrationState(obtainMessage(EVENT_POLL_STATE_GPRS, mPollingContext));
mPollingContext[0]++;
mCi.getVoiceRegistrationState(obtainMessage(EVENT_POLL_STATE_REGISTRATION, mPollingContext));
mPollingContext[0]++;
mCi.getNetworkSelectionMode(obtainMessage(EVENT_POLL_STATE_NETWORK_SELECTION_MODE, mPollingContext));
各个请求连续发送完毕后,底层会一一回复。然后上层收到新的参数,重新配置各个参数:运营商名称,数据类型,voice类型,网络模式。配置完后,开始创建数据连接。
@GsmServiceStateTracker.handleMessage
...
handlePollStateResult(msg.what, ar);
@GsmServiceStateTracker.handlePollStateResult//负责配置各个参数
mPollingContext[0]//;//当mPollingContext[0]为0时才会走pollStateDone
@GsmServiceStateTracker.pollStateDone
notifyDataRegStateRilRadioTechnologyChanged();
@ServiceStateTracker.notifyDataRegStateRilRadioTechnologyChanged
mDataRegStateOrRatChangedRegistrants.notifyResult(new Pair<Integer, Integer>(drs, rat));
@DcTracker.handleMessage//DCTracker注册mPhone.getServiceStateTracker().registerForDataRegStateOrRatChanged
case DctConstants.EVENT_DATA_RAT_CHANGED(270377)(this,DctConstants.EVENT_DATA_RAT_CHANGED, null);
onUpdateIcc//更据当前网络类型更新sim信息
tryRestartDataConnections//创建数据连接
底层网络状态变化后,触发重新创建数据连接的整个时序图如下:
7. 底层成功创建数据连接后的处理
当底层成功创建数据连接后,会向上层发出RIL_REQUEST_SETUP_DATA_CALL消息。RIL层接受该消息后,通知状态机。状态机根据底层上报的参数,判断出重新创建连接成功,将状态由DcActivatingState改为ActiveState。ActiveState又会发出通知告诉DcTrackerBase,而后续处理操作onDataSetupComplete由DcTrackerBase的子类DcTracker完成。
@RIL.run
case RIL_REQUEST_SETUP_DATA_CALL
@RIL.processResponse
@RIL.processSolicited
transitionTo(mActiveState);
@DcActivatingState.handleMessage()
case EVENT_SETUP_DATA_CONNECTION_DONE:
onSetupConnectionCompleted
transitionTo(mActiveState);
@DcActiveState.enter()
notifyAllOfConnected(Phone.REASON_CONNECTED);
@DataConnection.notifyAllOfConnected
notifyAllWithEvent(null, DctConstants.EVENT_DATA_SETUP_COMPLETE, reason);
@DcTrackerBase.handleMessage
case EVENT_DATA_SETUP_COMPLETE
onDataSetupComplete():
@DcTracker.onDataSetupComplete
时序图如下:
附件1_
创建carriers表语句:
db.execSQL("CREATE TABLE " + tableName +
"(_id INTEGER PRIMARY KEY," +
"name TEXT DEFAULT ''," +
"numeric TEXT DEFAULT ''," +
"mcc TEXT DEFAULT ''," +
"mnc TEXT DEFAULT ''," +
"apn TEXT DEFAULT ''," +
"user TEXT DEFAULT ''," +
"server TEXT DEFAULT ''," +
"password TEXT DEFAULT ''," +
"proxy TEXT DEFAULT ''," +
"port TEXT DEFAULT ''," +
"mmsproxy TEXT DEFAULT ''," +
"mmsport TEXT DEFAULT ''," +
"mmsc TEXT DEFAULT ''," +
"authtype INTEGER DEFAULT -1," +
"type TEXT DEFAULT ''," +
"current INTEGER," +
"protocol TEXT DEFAULT 'IP'," +
"roaming_protocol TEXT DEFAULT 'IP'," +
"carrier_enabled BOOLEAN DEFAULT 1," +
"bearer INTEGER DEFAULT 0," +
"bearer_bitmask INTEGER DEFAULT 0," +
"mvno_type TEXT DEFAULT ''," +
"mvno_match_data TEXT DEFAULT ''," +
"sub_id INTEGER DEFAULT " +
SubscriptionManager.INVALID_SUBSCRIPTION_ID +
"," +
"profile_id INTEGER DEFAULT 0," +
"modem_cognitive BOOLEAN DEFAULT 0," +
"max_conns INTEGER DEFAULT 0," +
"wait_time INTEGER DEFAULT 0," +
"max_conns_time INTEGER DEFAULT 0," +
"mtu INTEGER DEFAULT 0," +
"edited INTEGER DEFAULT " +
Telephony.Carriers.UNEDITED +
"," +
"read_only BOOLEAN DEFAULT 0," +
"UNIQUE (numeric, mcc, mnc, apn, proxy, port,
mmsproxy, mmsport, mmsc," +
"carrier_enabled, bearer, mvno_type, mvno_match_data, profile_id));");
carriers参数定义
定义在Telephony.Carriers中,
_id//key
name//Entry name
numeric//Numeric operator ID (as String). Usually MCC + MNC.
mcc//Mobile Country Code (MCC).
mnc//Mobile Network Code (MNC).
apn//APN name.
user//APN username.
server//Server address.
password//APN password.
proxy//Proxy address.
port//Proxy port.
mmsproxy//MMS proxy address.
mmsport//MMS proxy port.
mmsc//MMSC URL.
authtype//Authentication type.
(1:PAP/2:CHAP/3:PAP|CHAP)需要跟user password联合使用。
type//Comma-delimited list of APN types.(default,mms,supl,dun,ims,ia...)
current// Is this the current APN?
Protocol
//The protocol to use to connect to this APN. One of the PDP_type values in TS 27.007 section 10.1.1. For example: IP, IPV6, IPV4V6, or PPP.
roaming_protocol
//The protocol to use to connect to this APN when roaming. The syntax is the same as protocol.
carrier_enabled//Is this APN enabled?
Bearer
//Radio Access Technology info. To check what values are allowed, refer to android.telephony.ServiceState. This should be spread to other technologies, but is currently only used for LTE (14) and eHRPD (13).
bearer_bitmask
//Radio Access Technology bitmask. To check what values can be contained, refer to android.telephony.ServiceState. 0 indicates all techs otherwise first bit refers to RAT/bearer 1, second bit refers to RAT/bearer 2 and so on. Bitmask for a radio tech R is (1 << (R - 1))
mvno_type
//MVNO type: SPN (Service Provider Name), IMSI, GID (Group Identifier Level 1).
mvno_match_data
//MVNO data. Use the following examples.
SPN: A MOBILE, BEN NL, ...
IMSI: 302720x94, 2060188, ...
GID: 4E, 33, ...
sub_id//The subscription to which the APN belongs to
profile_id//The profile_id to which the APN saved in modem
modem_cognitive//Is the apn setting to be set in modem
max_conns//The max connections of this apn
wait_time//The wait time for retry of the apn
max_conns_time//The time to limit max connection for the apn
mtu//The MTU size of the mobile interface to which the APN connected
edited//Is this APN added/edited/deleted by a user or carrier?
read_only//default?
附件2
网络类型:
ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN
RIL_RADIO_TECHNOLOGY_UNKNOWN = 0;
RIL_RADIO_TECHNOLOGY_GPRS = 1;
RIL_RADIO_TECHNOLOGY_EDGE = 2;
RIL_RADIO_TECHNOLOGY_UMTS = 3;
RIL_RADIO_TECHNOLOGY_IS95A = 4;
RIL_RADIO_TECHNOLOGY_IS95B = 5;
RIL_RADIO_TECHNOLOGY_1xRTT = 6;
RIL_RADIO_TECHNOLOGY_EVDO_0 = 7;
RIL_RADIO_TECHNOLOGY_EVDO_A = 8;
RIL_RADIO_TECHNOLOGY_HSDPA = 9;
RIL_RADIO_TECHNOLOGY_HSUPA = 10;
RIL_RADIO_TECHNOLOGY_HSPA = 11;
RIL_RADIO_TECHNOLOGY_EVDO_B = 12;
RIL_RADIO_TECHNOLOGY_EHRPD = 13;
RIL_RADIO_TECHNOLOGY_LTE = 14;
RIL_RADIO_TECHNOLOGY_HSPAP = 15;
RIL_RADIO_TECHNOLOGY_GSM = 16;
RIL_RADIO_TECHNOLOGY_TD_SCDMA = 17;
SIM//RUIM//ISIM
SIMRecords(APPTYPE_USIM,APPTYPE_SIM)
RuimRecords(APPTYPE_RUIM,APPTYPE_CSIM)
IsimUiccRecords(APPTYPE_ISIM)
附件3
apn文件的替换
MTK平台
@device/mediatek/common/device.mk
PRODUCT_COPY_FILES += device/mediatek/common/apns-conf.xml:system/etc/apns-conf.xml
修改为:
#PRODUCT_COPY_FILES += device/mediatek/common/apns-conf.xml:system/etc/apns-conf.xml
APN_PATH := device/mediatek/common
XXX_APN_CONFIG_FILE := $(if $(wildcard vendor/xxx/frameworks/etc/apnsconfig/apns-conf.xml),yes,no)
ifeq ($(XXX_APN_CONFIG_FILE),yes)
APN_PATH := vendor/xxx/frameworks/etc/apnsconfig
endif
PRODUCT_COPY_FILES := $(APN_PATH)/apns-conf.xml:system/etc/apns-conf.xml $(PRODUCT_COPY_FILES)
高通平台
@xx/device/qcom/msm8994/msm8994.mk:69:
ifeq ($(wildcard $(QC_PROP_ROOT)/common-noship/etc/device-vendor-noship.mk),)
ifneq ($(call is-android-codename-in-list,GINGERBREAD HONEYCOMB),true)
APN_PATH := vendor/xxx/frameworks/etc/apnsconfig
PRODUCT_COPY_FILES := $(APN_PATH)/apns-conf.xml:system/etc/apns-conf.xml $(PRODUCT_COPY_FILES)
endif
endif
feature 控制_telephony.ct.apn
vendor/xxx/frameworks/featureslist/
手机目录: system/etc/permissions/
附件4_部分log:
//////////////-DcTracker创建数据连接流程log//////////////////-
09-09 16:53:33.053 D/GsmDCT ( 3057): [1]handleMessage msg={ when=-546ms what=270352 obj=android.os.AsyncResult@3651063e target=com.android.internal.telephony.dataconnection.DcTracker }
09-09 16:53:33.358 D/GsmDCT ( 3057): [1]setupDataOnConnectableApns: dataAttached
09-09 16:53:33.360 D/GsmDCT ( 3057): [1]buildWaitingApns: usePreferred=true canSetPreferApn=true mPreferredApn=null operator=310410 radioTech=11
09-09 16:53:33.374 D/GsmDCT ( 3057): [1]createDataConnection() X id=0 dc={DC-1: State=DcInactiveState mApnSetting=null RefCount=0 mCid=-1 mCreateTime=-1 mLastastFailTime=-1 mLastFailCause=NONE mTag=1 mRetryManager=RetryManager: { forever=false maxRetry=0 curMaxRetry=0 retry=0 config={null} retryArray={}} mLinkProperties={LinkAddresses: [] Routes: [] DnsAddresses: [] Domains: null MTU: 0} linkCapabilities=[ Transports: CELLULAR Capabilities: NOT_RESTRICTED&TRUSTED&NOT_VPN LinkUpBandwidth>=5898Kbps LinkDnBandwidth>=14336Kbps Specifier: <1>] mApnContexts=[]}
09-09 16:53:33.374 D/GsmDCT ( 3057): [1]setupData: dcac=DC-1 apnSetting=[ApnSettingV3] Cingular 410, 446, 310410, wap.cingular, , http://mmsc.cingular.com/, wireless.cingular.com, , , -1, default | supl | mms, IP, IP, true, 0, 0, false, 0, 0, 0, 0, ,
09-09 16:53:33.385 D/DC-1 ( 3057): DcInactiveState: mag.what=EVENT_CONNECT
09-09 16:53:33.389 D/DC-1 ( 3057): onConnect: carrier='Cingular 410' APN='wap.cingular' proxy='' port=''
09-09 16:53:33.390 D/RILJ ( 3057): [3952]> SETUP_DATA_CALL 13 0 wap.cingular WAP@CINGULARGPRS.COM CINGULAR1 3 IP [SUB1]
//////////////-DcTracker创建数据连接流程log//////////////////-
//////////-底层网络状态变化log//////////-
09-10 07:53:32.374 D/GsmSST ( 3057): [GsmSST1] Poll ServiceState done: oldSS=[1 1 home null null null Unknown Unknown CSS not supported -1 -1 RoamInd=-1 DefRoamInd=-1 EmergOnly=false] newSS=[0 0 home AT&T AT&T 310410 UMTS HSPA CSS not supported -1 -1 RoamInd=-1 DefRoamInd=-1 EmergOnly=false] oldMaxDataCalls=20 mNewMaxDataCalls=20 oldReasonDataDenied=-1 mNewReasonDataDenied=-1
09-09 16:53:32.500 D/GsmSST ( 3057): [GsmSST1] notifyDataRegStateRilRadioTechnologyChanged: drs=0 rat=11
09-09 16:53:32.655 D/GsmDCT ( 3057): [1]handleMessage msg={ when=-150ms what=270377 obj=android.os.AsyncResult@29a9f9c0 target=com.android.internal.telephony.dataconnection.DcTracker }
//////////-底层网络状态变化log//////////-