Android多APN同时工作原理分析

平台:android5.1
场景:客户使用运营商的流量卡,希望多路APN同时工作。
时间:2016.7.1

大概了解Mms机制,发现发送彩信时,使用的是另一路APN。因此以此为追踪入口。

     MmsNetworkManager.java
 >>>connectivityManager.requestNetwork(
                mNetworkRequest, mNetworkCallback, NETWORK_REQUEST_TIMEOUT_MILLIS);

mNetworkRequest.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS),为后面的APN匹配查找给出了条件。
mNetworkCallback作为request后的调用响应,将把申请的Network返回。此Network带有ID,每次创建释放后递增。Mms根据此Network来查询Mms对应的APN信息。

ConnectivityService.java
>>>requestNetwork()
>>>handleRegisterNetworkRequest(Message msg)

以Mms类型创建NetworkRequest对象和NetworkRequestInfo对象,以键值对形式保存到mNetworkRequests中:

mNetworkRequests.put(nri.request, nri);
if (DBG) log("sending new NetworkRequest to factories")
//此时network.satisfies(nri.request)为false,并且bestNetwork为null。

            for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
                nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score,
                        0, nri.request);
            }

mNetworkFactoryInfos对象来源于

    public void registerNetworkFactory(Messenger messenger, String name) {
        enforceConnectivityInternalPermission();
        NetworkFactoryInfo nfi = new NetworkFactoryInfo(name, messenger, new AsyncChannel());
        mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_FACTORY, nfi));

过程如下:
在Phone的初始化中调用,

PhoneFactory.makeDefaultPhones(this)

ProxyController.getInstance(context, sProxyPhones,
                        mUiccController, sCommandsInterfaces)

DctController.makeDctController(phoneProxy)

updatePhoneBaseForIndex(i, phoneBase)

        mNetworkFactory[index] = new TelephonyNetworkFactory(this.getLooper(),
                mPhones[index].getContext(), "TelephonyNetworkFactory", phoneBase,
                mNetworkFilter[index]);
        mNetworkFactory[index].setScoreFilter(50);
        mNetworkFactoryMessenger[index] = new Messenger(mNetworkFactory[index]);
        cm.registerNetworkFactory(mNetworkFactoryMessenger[index], "Telephony");

此处两个关键点:
1.mNetworkFactory[index] = new TelephonyNetworkFactory()
TelephonyNetworkFactory继承于NetworkFactory,其使用DctController内部的Looper对象,并且将调用到NetworkFactory.java::handleMessage()

2.cm.registerNetworkFactory(mNetworkFactoryMessenger[index], “Telephony”)调用上面的注册函数

NetworkFactory.java

>>>handleAddRequest(NetworkRequest request, int score)
>>>evalRequest(NetworkRequestInfo n)               //此时为TelephonyNetworkFactory对象,其继承于NetworkFactory。TelephonyNetworkFactory为DctController.java的内部类

打印 :[TNF 1]evalRequest request = NetworkRequest [ id=4, legacyType=2,
[ Transports: CELLULAR Capabilities:
MMS&NOT_RESTRICTED&TRUSTED&NOT_VPN Specifier: <1>] ] with requested =
false

DctController.java
private class TelephonyNetworkFactory extends NetworkFactory
>>>needNetworkFor(NetworkRequest networkRequest, int score)
                String apn = apnForNetworkRequest(networkRequest);     //apnForNetworkRequest()函数通过nr.networkCapabilities,即Mms中requestNetwork传入的Mms类型进行匹配,返回PhoneConstants.APN_TYPE_MMS。
                if (dcTracker.isApnSupported(apn)) {
                    requestNetwork(networkRequest, dcTracker.getApnPriority(apn), mGroupId);
                } else {
                    log("Unsupported APN");
                }

>>>requestNetwork(NetworkRequest request, int priority, int gid)

打印:[DctController] requestNetwork request=NetworkRequest [ id=3,
legacyType=2, [ Transports: CELLULAR Capabilities:
MMS&NOT_RESTRICTED&TRUSTED&NOT_VPN Specifier: <1>] ], priority=2, gid
= 0

mRequestInfos以键值对保存request.requestId和requestInfo。

>>>onProcessGroup(int group)
        if (mDataAllowed) {               //mDataAllowed默认为true,除非setDataAllowed()的主动调用设置false。
            if (activePhoneId == -1 || activePhoneId == phoneId) {
                Iterator iterator = mRequestInfos.keySet().iterator();

                if (activePhoneId == -1 && !iterator.hasNext()) {
                    logd("No active phone, set phone" + phoneId + " to attaching state");
                    transitToAttachingState(phoneId);
                }

                while (iterator.hasNext()) {
                    RequestInfo requestInfo = mRequestInfos.get(iterator.next());         //取出requestNetwork()住保存的requestInfo变量
                    if (getRequestPhoneId(requestInfo.request) == phoneId
                            && requestInfo.mGId == group
                            && !requestInfo.executed) {
                        mDcSwitchAsyncChannel[phoneId].connectSync(requestInfo);          //发起connect请求
                    }
                }
            } else {
                mDcSwitchAsyncChannel[activePhoneId].disconnectAllSync();
            }
        }

DcSwitchAsyncChannel.java
>>>connectSync(RequestInfo apnRequest)
Message response = sendMessageSynchronously(REQ_CONNECT, apnRequest)          
**//此处是sendMessageSynchronously(),其实现在AsyncChannel.java中,静态内部类SyncMessenger,标准的同步消息队列,通过其内部对象对象 sm.mHandler.mLockObject来完成:**
           public void handleMessage(Message msg) {
                mResultMsg = Message.obtain();
                mResultMsg.copyFrom(msg);
                synchronized(mLockObject) {
                    mLockObject.notify();
                }
            }

        private static Message sendMessageSynchronously(Messenger dstMessenger, Message msg) {
            SyncMessenger sm = SyncMessenger.obtain();
            try {
                if (dstMessenger != null && msg != null) {
                    msg.replyTo = sm.mMessenger;
                    synchronized (sm.mHandler.mLockObject) {
                        dstMessenger.send(msg);
                        sm.mHandler.mLockObject.wait();
                    }
...
}

过程如下:
在构造函数中,

            int status = mDcSwitchAsyncChannel[i].fullyConnectSync(mPhones[i].getContext(),
                mDcSwitchStateHandler[i], mDcSwitchStateMachine[i].getHandler());

和下面的分析类似,此时mDcSwitchStateMachine[i].getHandler()即为sendMessageSynchronously()的目的地Looper队列。
在DcSwitchStateMachine构造函数中,使用super(name)方式,即状态机内部新建一个HandlerThread,使用自己的新建内部Looper队列:

    protected StateMachine(String name) {
        mSmThread = new HandlerThread(name);
        mSmThread.start();
        Looper looper = mSmThread.getLooper();

        initStateMachine(name, looper);
    }

//DcSwitchStateMachine的默认状态是setInitialState(mIdleState)。此处为分析Mms请求过程,继而会直接到AttachedState状态

DcSwitchStateMachine.java
private class AttachedState extends State {     
...
                case DcSwitchAsyncChannel.REQ_CONNECT: {
                    RequestInfo apnRequest = null;

                    if (msg.obj != null) {
                        apnRequest = (RequestInfo) msg.obj;
                        DctController.getInstance().executeRequest(apnRequest);     //apnRequest=[ request=NetworkRequest [ id=3, legacyType=2, [ Transports: CELLULAR Capabilities: MMS&NOT_RESTRICTED&TRUSTED&NOT_VPN Specifier: <1>] ], executed=false, priority=2, gid=0, phoneId=0]
                    }

                    if (DBG) {
                        log("AttachedState: REQ_CONNECT, apnRequest=" + apnRequest);
                    }

                    mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_CONNECT,
                            PhoneConstants.APN_REQUEST_STARTED);
                    retVal = HANDLED;
                    break;
                }
...
}

DctController.java
>>>executeRequest(RequestInfo request)
>>>onExecuteRequest(RequestInfo requestInfo)
        if (needExecuteRequest(requestInfo)) {            //   needExecuteRequest()查询requestInfo.executed是否为true,若是则返回false。
            requestInfo.executed = true;
            String apn = apnForNetworkRequest(requestInfo.request);
            int phoneId = getRequestPhoneId(requestInfo.request);
            logd("onExecuteRequest apn = " + apn + " phoneId=" + phoneId);     //打印:[DctController] onExecuteRequest apn = mms phoneId=0
            requestInfo.phoneId = phoneId; // remember the executed phone id.
            PhoneBase phoneBase = getActivePhone(phoneId);
            DcTrackerBase dcTracker = phoneBase.mDcTracker;               //每个PhoneBase对应一个DcTracker
            dcTracker.incApnRefCount(apn);
        }

DcTracker.java
>>>incApnRefCount(String name)
        ApnContext apnContext = mApnContexts.get(name);     //此处的name为mms,mApnContexts以name为key,保存了不同网络类型对应的ApnContext对象
        log("incApnRefCount name = " + name);
        if (apnContext != null) {
            log("incApnRefCount apnContext = " + apnContext);
            apnContext.incRefCount();                                        //ApnContext基于res下的config.xml中的networkAttributes数组,通过DcTrack中的initApnContexts()完成初始化。
        }

ApnContext.java                         
>>>incRefCount()
        synchronized (mRefCountLock) {
            if (mRefCount++ == 0) {
                mDcTracker.setEnabled(mDcTracker.apnTypeToId(mApnType), true);
            }
        }

DcTrackerBase.java

>>>setEnabled(int id, boolean enable)              

//打印:request,setEnabled(1, true) with old state = false and
enabledCount = 1。release,setEnabled(1, true) with old state = false
and enabledCount = 1

DcTracker.java

>>>onEnableApn(int apnId, int enabled)
>>>applyNewState(ApnContext apnContext, boolean enabled, boolean met)     
"applyNewState(" + apnContext.getApnType() + ", " + enabled +
                    "(" + apnContext.isEnabled() + "), " + met + "(" +
                    apnContext.getDependencyMet() +"))"

//打印: applyNewState(mms, true(false), true(true)) if (trySetup)
trySetupData(apnContext); //trySetup为true

>>>trySetupData(ApnContext apnContext, ArrayList<ApnSetting> waitingApns)     //此时 waitingApns为null
                 if (waitingApns == null) {
                    waitingApns = buildWaitingApns(apnContext.getApnType(), radioTech);                  }
                    ...
                    boolean retValue = setupData(apnContext, radioTech);
//buildWaitingApns ()根据传入的ApnType类型(mms) 去查询匹配的APN信息,并保存到waitingApns数组
/*
 /**
     * Build a list of APNs to be used to create PDP's.
**/
private ArrayList<ApnSetting> buildWaitingApns(String requestedApnType, int radioTech) {
...
            for (ApnSetting apn : mAllApnSettings) {        
                if (DBG) log("buildWaitingApns: apn=" + apn);
                if (apn.canHandleType(requestedApnType)) {
                    if (apn.bearer == 0 || apn.bearer == radioTech) {
                        if (DBG) log("buildWaitingApns: adding apn=" + apn.toString());
                        apnList.add(apn);
                    } else {
                        if (DBG) {
                            log("buildWaitingApns: bearer:" + apn.bearer + " != "
                                    + "radioTech:" + radioTech);
                        }
                    }
                }
...
}
*/

mAllApnSettings为匹配核心变量。其在createAllApnList()中,通过mAllApnSettings = createApnList(cursor)被赋值。cursor定义如下:

            Cursor cursor = mPhone.getContext().getContentResolver().query(
                    Telephony.Carriers.CONTENT_URI, null, selection, null, null);

此处Telephony.Carriers.CONTENT_URI是何时被赋值的?请自行研究。


>>>setupData(ApnContext apnContext, int radioTech)          //radioTech = mPhone.getServiceState().getRilDataRadioTechnology()用于后面的匹配是否支持多DC通路

//打印:setupData: apnContext={mApnType=mms mState=IDLE
mWaitingApns={[[ApnSettingV3] 中国联通 Wap 网络 (China Unicom), 1153, 46001,
3gwap, 10.0.0.172, , , , 80, -1, *, IPV4V6, IP, true, 0, 0, false, 0,
0, 0, 0, , , [ApnSettingV3] 中国联通 3g 彩信 (China Unicom), 1154, 46001,
3gwap, , http://mmsc.myuni.com.cn, 10.0.0.172, 80, , -1, mms, IPV4V6,
IP, true, 0, 0, false, 0, 0, 0, 0, , ]}
mWaitingApnsPermanentFailureCountDown=2 mApnSetting={null}
mReason=dataEnabled mDataEnabled=true mDependencyMet=true}

   ...
     apnSetting = apnContext.getNextWaitingApn();     //getNextWaitingApn()函数返回apnContext数组的[0]
     ...
     dcac = checkForCompatibleConnectedApnContext(apnContext);    //checkForCompatibleConnectedApnContext()函数会遍历mApnContexts,查询各个网络类型当前的Dcac通路,默认都是null。第一次发送Mms时,mms也为null。何时不为null?
     ...
                 if (dcac == null) {
                if (DBG) log("setupData: No ready DataConnection found!");
                // TODO: When allocating you are mapping type to id. If more than 1 free,
                // then could findFreeDataConnection get the wrong one??
                dcac = findFreeDataConnection();     //findFreeDataConnection()遍历mDataConnectionAcHashMap,是否有处于Inactive状态同时未被占用的dcac。第一次发送Mms时,返回null。当第二次发送Mms时,会返回之前创建的dcac对象
            }
     ...

            if (dcac == null) {
                dcac = createDataConnection();     //MTK平台getPdpConnectionPoolSize()mtk上默认最大pdp连接数设定是3.
            }
...

createDataConnection()
1.创建DataConnection对象,并以自增的id为Key保存到mDataConnections
2.建立对应的DcAsyncChannel对象,以同一个自增的id为Key保存到mDataConnectionAcHashMap
3.在MTK的默认设计中,此自增id不能大于3,MTK对此解释: throttling/high throughput apn start
(DataConnection类继承于StateMachine,在其构造函数中,会调用自己的start()函数,使StateMachine运行起来。)

>>>createDataConnection()
...
        DataConnection conn = DataConnection.makeDataConnection(mPhone, id,
                                                this, mDcTesterFailBringUpAll, mDcc);
        mDataConnections.put(id, conn);
        DcAsyncChannel dcac = new DcAsyncChannel(conn, LOG_TAG);
        int status = dcac.fullyConnectSync(mPhone.getContext(), this, conn.getHandler()); 
...

//dcac.fullyConnectSync(mPhone.getContext(), this, conn.getHandler()) 是一个关键调用:

AsyncChannel.java::    public void connected(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
        if (DBG) log("connected srcHandler to the dstMessenger  E");
                    RuntimeException here = new RuntimeException("here");
            here.fillInStackTrace();
             Slog.i(TAG, "CallTrace : ", here);       



// Initialize source fields
        mSrcContext = srcContext;
        mSrcHandler = srcHandler;
        mSrcMessenger = new Messenger(mSrcHandler);

        // Initialize destination fields
        mDstMessenger = dstMessenger;

        if (DBG) log("connected srcHandler to the dstMessenger X");
    }

请注意mDstMessenger = dstMessenger,此时调用端传入conn.getHandler():
conn是DataConnection类的对象,DataConnection继承于StateMachine,即conn.getHandler()为StateMachine的内部对象mSmHandler。仔细阅读代码后,会发现这个mSmHandler绕了一圈
DataConnection构造函数中,super(name, dcc.getHandler())—dcc为DcController对象,其也继承于StateMachine。
DcTrackerBase构造函数中:

        HandlerThread dcHandlerThread = new HandlerThread("DcHandlerThread");
        dcHandlerThread.start();
        Handler dcHandler = new Handler(dcHandlerThread.getLooper());
        mDcc = DcController.makeDcc(mPhone, this, dcHandler);

总结:

Android多APN同时工作原理分析_第1张图片

Android多APN同时工作原理分析_第2张图片



...
        if(0 == apnContext.getDefaultBearerConfig().mIsValid) {
           dcac.bringUp(apnContext, getInitialMaxRetry(), profileId, radioTech, mAutoAttachOnCreation,
                 msg);
        }
...

DcAsyncChannel.java
>>>bringUp(ApnContext apnContext, int initialMaxRetry, int profileId,
            int rilRadioTechnology, boolean retryWhenSSChange, Message onCompletedMsg) 
此函数注释为:     * Bring up a connection to the apn and return an AsyncResult in onCompletedMsg.
     * Used for cellular networks that use Acesss Point Names (APN) such
     * as GSM networks.

AsyncChannel.java

>>>sendMessage(Message msg){
         msg.replyTo = mSrcMessenger;
        try {
            mDstMessenger.send(msg);     //mDstMessenger将msg发送到目标Handler,其为DcInactiveState。是如何联系上的???
        } catch (RemoteException e) {
            replyDisconnected(STATUS_SEND_UNSUCCESSFUL);
        }    

打印log如下:
Line 5121: 06-27 11:28:45.152 1397 1481 D DC-2 : DcInactiveState nothandled msg.what=CMD_CHANNEL_FULL_CONNECTION
Line 5122: 06-27 11:28:45.152 1397 1481 D DC-2 : DcDefault msg=CMD_CHANNEL_FULL_CONNECTION RefCount=0
Line 5123: 06-27 11:28:45.152 1397 1481 D DC-2 : DcDefaultState: FULL_CONNECTION reply connected
Line 5131: 06-27 11:28:45.155 1397 1397 D DC-2 : makeNetworkCapabilities: check data enable:true Line 5132: 06-27
11:28:45.157 1397 1397 D DCT : [0]createDataConnection() X id=1
dc={DC-2: 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 PcscfAddresses:
[] } linkCapabilities=[ Transports: CELLULAR Capabilities:
NOT_RESTRICTED&TRUSTED&NOT_VPN LinkUpBandwidth>=51200Kbps
LinkDnBandwidth>=102400Kbps Specifier: <1>] mApnContexts=[]}
Line 5133: 06-27 11:28:45.158 1397 1397 D DCT : [0]setupData: dcac=DC-2 apnSetting=[ApnSettingV3] 中国联通 Wap 网络 (China
Unicom), 1153, 46001, 3gwap, 10.0.0.172, , , , 80, -1, *, IPV4V6, IP,
true, 0, 0, false, 0, 0, 0,
Line 5140: 06-27 11:28:45.161 1397 1481 D DC-2 : DcInactiveState nothandled
msg.what=EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED
Line 5141: 06-27 11:28:45.161 1397 1481 D DC-2 : DcDefault msg=EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED RefCount=0
Line 5142: 06-27 11:28:45.161 1397 1481 D DC-2 : DcDefaultState: EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED drs=0
mRilRat=14
Line 5144: 06-27 11:28:45.162 1397 1481 D DC-2 : DcInactiveState nothandled msg.what=EVENT_DATA_CONNECTION_ROAM_OFF
Line 5145: 06-27 11:28:45.162 1397 1481 D DC-2 : DcDefault msg=EVENT_DATA_CONNECTION_ROAM_OFF RefCount=0
Line 5164: 06-27 11:28:45.175 1397 1481 D DC-2 : DcInactiveState: mag.what=EVENT_CONNECT

上面已经说明mDstMessenger目的Looper,其为DataConnection以及DcController两个状态机对象对应Looper。在DataConnection构造函数中:

            addState(mDefaultState);
            addState(mInactiveState, mDefaultState);
            addState(mActivatingState, mDefaultState);
            addState(mRetryingState, mDefaultState);
            addState(mActiveState, mDefaultState);
            addState(mDisconnectingState, mDefaultState);
            addState(mDisconnectingErrorCreatingConnection, mDefaultState);
            setInitialState(mInactiveState);

结合上面log,在EVENT_CONNECT到来之前的几条msg处理,并未转换状态,继而直接被DcInactiveState处理。

DataConnection.java
>>>private class DcInactiveState extends State {
     public boolean processMessage(Message msg){
...               
                    case EVENT_CONNECT:
                    if (DBG) log("DcInactiveState: mag.what=EVENT_CONNECT");
...
                    if (initConnection(cp)) {               //检查cp参数的兼容性,其mApnSetting是否为null,亦或Apntype是否可以被处理。不返回false的时候,将cp赋给mConnectionParams
                        onConnect(mConnectionParams);
                        transitionTo(mActivatingState);     //状态切换,将首先运行mActivatingState的enter()函数.
                    }
...
     }
}

>>>onConnect(ConnectionParams cp)
此函数注释为:
    /**
     * Begin setting up a data connection, calls setupDataCall
     * and the ConnectionParams will be returned with the
     * EVENT_SETUP_DATA_CONNECTION_DONE AsyncResul.userObj.
     */
...
        if (1 == cp.mDefaultBearerConfig.mIsValid ) {   //request from VA for VoLTE
            DefaultBearerConfig defaultBearerConfig = new DefaultBearerConfig();
            defaultBearerConfig.copyFrom(cp.mDefaultBearerConfig);

            mPhone.mCi.setupDataCall(Integer.toString(cp.mRilRat + 2),
                Integer.toString(cp.mProfileId),
                mApnSetting.apn, mApnSetting.user, mApnSetting.password,
                Integer.toString(authType),
                protocol, String.valueOf(mId + 1), defaultBearerConfig, msg);
        } else {
            /// M: [C2K][IRAT] Use RilArbitrator to setup data.
            setupDataCall(Integer.toString(cp.mRilRat + 2),
                Integer.toString(cp.mProfileId), mApnSetting.apn,
                mApnSetting.user, mApnSetting.password,
                Integer.toString(authType), protocol, String.valueOf(mId + 1),
                msg);
        }
...
//以上部分,MTK有做定制。核心调用mPhone.mCi.setupDataCall()。

RIL.java

>>>setupDataCall(String radioTechnology, String profile, String apn,
            String user, String password, String authType, String protocol,
            String interfaceId, DefaultBearerConfig defaultBearerConfig, Message result)
RILRequest rr
                = RILRequest.obtain(RIL_REQUEST_SETUP_DATA_CALL, result);
...
send(rr);

打印log如下:
Line 5214: 06-27 11:28:45.201 1397 1481 D RILJ : [4160]> SETUP_DATA_CALL 16 0 3gwap 0 IPV4V6 2 [isValid=0, qos=[qci=0,
dlGbr=0, ulGbr=0, dlMbr=0, ulMbr=0], emergency_ind=0,
pcscf_discovery_flag=0, signaling_flag=0] 0 [SUB0]
Line 5243: 06-27 11:28:45.207 719 732 D RILC : SOCKET RIL_SOCKET_1 REQUEST: SETUP_DATA_CALL length:180
Line 5246: 06-27 11:28:45.207 719 732 D RILC-MTK: SETUP_DATA_CALL pRI=0xb6437280 p=0xb646f520 dispatched to
RIL_CMD_PROXY_4
Line 5267: 06-27 11:28:45.208 719 724 D RILC-MTK: SETUP_DATA_CALL pRI=0xb6437280 p=0xb646f520 execute on RIL_CMD_PROXY_4
using channel 4
Line 5269: 06-27 11:28:45.208 719 724 D RIL : onRequest: SETUP_DATA_CALL, datalen = 32
Line 6218: 06-27 11:28:45.374 1397 1469 D RILJ : [4160]< SETUP_DATA_CALL DataCallResponse: {version=10 status=0 retry=0 cid=1
active=2 type=IP ifname=ccmni1 mtu=0 addresses=[10.151.32.118]
dnses=[221.4.8.1,221.4.8.1] gateways=[10.151.32.118] pcscf=[]} [SUB0]

以Mms的Apn信息创建新的PDP通路。上面的log可以看出,PDP通路建立后,会请求分配到运营商的内网IP。
Mms在使用建立的通路发送完短信后,会调用releaseNetwork()释放此network,其过程可自行分析。

关于PDP的知识摘要:

(1)终端设备向移动终端发送AT指令激活IP协议,在指令中包含终端想要连接的APN(AccessPointName,访问点名称)
(2)终端设备向移动终端发送PPPPLC帧给移动终端,表明PAP是在PDP激活过程中的身份认证协议
(3)终端设备开始进行PAP认证,认证通过后,移动终端将对终端设备给以回应,表明承认其身份,并且会将用户ID和密码储存下来
(4)终端设备通过发送NCP-IPCP配置请求信息给移动终端,帧内IP地址为空,表明请求动态分配IP地址
(5)移动终端向SGSN(ServicingGPRSSupportNode,服务GPRS节点)发送激活PDP上下文的请求信息,信息中包含如下信息:APN、PDP类型,PDP地址为空,代表请求动态分配IP地址
(6)SGSN请求DNS(DomainNameSystem,域名系统)服务器对APN进行解析,得到APN对应的GGSN的IP地址
(7)SGSN发送建立PDP上下文的请求消息给被选定的GGSN,消息中应包含:APN、PDP类型,PDP地址为空,代表请求动态分配IP地址、用户更改的QoS和其他选项
(8)GGSN对用户进行认证,认证通过后,使用RADIUS(RemoteAuthenticationDia-inUserService,远程认证拨入用户服务)服务器、DHCP(DynamicHostConfigurationProtocol,动态主机配置协议)服务器或直接由GGSN为用户分配动态IP地址,GGSN向SGSN返回建立PDP上下文相应消息
(9)SGSN向移动终端发送激活PDP、上下文接受消息
(10)移动终端发送NCP-IPCP配置回应帧给终端设备,回应帧包含了被动态分配的IP地址

一个PDP上下文提供了在UE和网络侧之间交换IP包的一个数据包连接。利用这个数据包连接可以访问一些特定的业务。PDP上下文一般来说主要有以下两个目的。第一点是PDP上下文设计用于分配一个PDP地址,或者是IPV4或者是IPV6地址给一个终端。第二点用于决定一个到终端的带有QOS profile的逻辑连接,即为一个PDP上下文协商的一组贯穿整个UMTS网络都能得到执行的QOS profile。
由于移动终端的发展,他们需要同时在手机上建立多个并行的PS连接。这些PS连接,他们的QOS参数,以及访问的目标网络都有可能不同。多PDP上下文也就是说一个移动终端可以存在多个PDP上下文。其中第一个被创建的PDP就是Primary PDP,在Primary PDP的基础上可以创建多个Secondary PDP,这些Secondary PDP和Primary PDP拥有相同的IP地址也就是PDP地址,接入的网络类型是一样的,但是具有不同的Qos Profile,这样就产生了多Secondary PDP。如果接入的网络不一样,那就是多Primary PDP了,例如移动网络的CMWAP和CMNET,如果手机用户同时使用这两个网络上网,那么将会产生多个Primary PDP。上面介绍了多PDP Context的概念。同时也把多Primary PDP和多Secondary PDP介绍了。
现在开始进入正题,用户开启多个应用访问网络的时候,IP地址的分配问题。首先看用户接入网络的类型,如果两个应用使用了不同的接入类型,例如一个使用CMWAP,一个使用CMNET,最后肯定会导致申请两个Primaey PDP,这样就会生成两个IP地址来进行数据传输,同时也会有2个NSAPI。如果用户使用一种接入方式的话。就不会产生新的IP地址。是否有Secondary PDP产生的话就要看用户使用的服务了。打个比方,如果用户首先浏览网页,这个时候生成看了一个Primary PDP,分配了IP地址、NSAPI、控制面和用户面TEID,紧跟着开启其他的相同Qos的业务的话,就不会产生Secondary PDP。如果新的业务是3g视频通信的话,需要很好的Qos,这样就会生成新的Secondary PDP,分配新的NSAPI、用户面TEID,注意控制面TEID和IP是不变的。具体使用什么样的Qos,这是开发软件者和运营商去协商。正常来说交钱少的就是直接使用Primary PDP了,交钱多的就会开启Secondary PDP来提高质量。
注:平时上网我们一般是使用CMNET,但是在上网的同时,如果发送彩信的话,就会使用CMWAP了,这个时候应该就会出现多Primary PDP的情况。

gprs上网首先要设置pdp,接着建立ppp连接,ppp连接建立后,就可以进行tcp/ip传输了,要进行tcp/ip数据传输,很多时候都采用socket.
PDP:是GPRS连接的软硬件环境,指定GPRS连接的接入点APN,连接类型IP或PPP,还有其他一些可选项;
PPP:终端和MODEM之间点对点的协议,包括终端于MODEM之间的链路层协商(LCP),服务器对终端的认证(PAP或CHAP,这一步非强制),以及终端与服务器的网络层协商(基本都是IPCP);
SOCKET:进程之间的通信方式,手机上的应用程序(客户进程)要和服务器的某个服务进程通信,就用socket通过邦定的TCP或UDP端口基于IP
进行数据传输
再补充一下:
PPP协商过程中的IPCP配置中,终端通过MODEM请求激活PDP上下文获得IP地址完成网络连接,PDP中设置的APN就是终端所在的这个网络的网关,终端访问internet时就得通过这个网关; 而终端的客户进程与服务器的服务进程进行socket通信时,就基于这个IP地址。
据我所知道的:从网络侧来看,PPP连接最重要的一步是获取IP地址,这个IP由GGSN分配,GGSN是GPRS网到internet的网关,GSM和WCDMA协议规定一个MODEM可以和多个GGSN建立PDP上下文,不知道你所说的服务器是否指GGSN。而socket连接的服务器和GGSN完全
是两码事,socket连接的是internet网络中的服务器,socket是用于进程间通信的,它将进程与TCP/UDP端口进行绑顶,一个client端的socket只能连接一个server socket。也就决定了它只能连接一台服务器。

移动终端开机连接到运营商后,访问3G网络外部IPv6业务,主要包括以下三个过程:
  (1)附着(ATTACH)。附着过程的目的是系统根据移动终端的签约数据确定是否允许移动终端在当前请求的GPRS路由区域中进行数据业务访问。附着过程与IPv6无关。
  终端可以在开机通过无线接入鉴权获得无线信道后即向SGSN发起“附着请求”消息,SGSN得到终端IMSI标识后,向HLR中请求进行认证,并根据HLR下达的用户签约数据对终端进行鉴权,同时SGSN将终端的当前位置信息上传HLR。鉴权通过后,SGSN就会向终端返回“接受附着”消息。
  (2)PDP(Packet Data Protocol)上下文激活。通过PDP上下文激活过程,用户获得相应的GGSN的鉴权许可,分配相应的IPv6地址,建立终端与基于GPRS的3G分组域之间的数据通道。
  PDP激活过程由用户终端发起。终端首先向SGSN发起“激活PDP上下文请求”消息,消息中携带APN(服务访问点名称),服务质量等信息;SGSN根据消息中携带的APN向HLR中查寻相应的GGSN的地址,获得GGSN地址后,再向GGSN发送“创建PDP上下文请求”;GGSN可以通过本地/DHCP/RADIUS对终端进行签权并分配IPv6地址或者地址前缀,以及其他参数,如QoS参数等,并将鉴权结果以及各项参数携带在“响应创建PDP上下文请求”消息中,发送给SGSN,由SGSN再向终端发送“接受激活PDP上下文请求”消息,将各参数配置传递给用户终端,从而完成PDP激活过程。
  这一过程中,与IPv6相关的功能主要集中在IPv6地址请求/分配上。首先,终端在“激活PDP上下文请求”消息中需要携带请求地址类型为IPv6的信息;其次,系统要能够分配IPv6地址。如果系统采用GGSN本地地址池分配IPv6地址方式,那么GGSN需要支持IPv6地址池设置与分配,如果采用DHCP服务器或者RADIUS服务器要进行地址分配,那么就需要DHCP,RADIUS服务器支持IPv6。
  (3)业务访问。在PDP之后,从终端到3G系统分组域的IPv6应用数据通道已经打开。如果终端访问3G系统外的业务服务器,就需要保持3G系统分组域与外部网络直到业务服务器之间的转发路径通畅,即3G系统分组域边界网关需要具备访问IPv6外部网络的路由与转发能力。通常的IPv6 3G应用解决方案中,GGSN作为3G分组域的对外网关要求具备双栈能力。

Android多APN同时工作原理分析_第3张图片

你可能感兴趣的:(Android多APN同时工作原理分析)