看此篇看不太懂的同志们可以先看看我另一篇文章:http://blog.csdn.net/yankebin/article/details/44035489
理解了建立GPRS通信的流程再来看这篇文章就相对来说好理解了
android通信流程本身就是一个很复杂的问题,我只是以我工作过程中遇到的细节为主要线索,理一理整个过程中需要注意的地方,如有错误和不当,还望大家海涵和指正。
(1).先回过头来看看数据网络开启过程中的CdmaDataConnection和GsmDataConnection中的onConnect()方法,该方法中在调用RIL的setupDataCall()时,传入了标识为EVENT_SETUP_DATA_CONNECTION_DONE 的Message,在RIL的setupDataCall()使用该Message获取了RILRequest的实例并将该message作为RIL Parcel的第一个元素写入到其中。
RILRequest rr = RILRequest. obtain(RIL_REQUEST_SETUP_DATA_CALL, result);
该obtain方法作用是从池中检索一个新的RILRequest实例,第一个参数是定义在RILConstants中以RIL_REQUEST_打头的整型值,第二个参数是操作完成后要发送的东西。由此看来之前标识EVENT_SETUP_DATA_CONNECTION_DONE 的Message会在执行完后将发送出去。RILRequest,代表着一个即将发送出去的RIL请求,它里面包含了Request请求号、序列号(自0开始累加)和保存请求结果的Message。
/**
* Retrieves a new RILRequest instance from the pool.
*
* @param request RIL_REQUEST_*
* @param result sent when operation completes
* @return a RILRequest instance from the pool.
*/
static RILRequest obtain( int request, Message result) {
RILRequest rr = null;
synchronized(sPoolSync ) {
if (sPool != null) {
rr = sPool;
sPool = rr.mNext ;
rr. mNext = null;
sPoolSize--;
}
}
if (rr == null) {
rr = new RILRequest();
}
synchronized(sSerialMonitor ) {
rr. mSerial = sNextSerial++;
}
rr. mRequest = request;
rr. mResult = result;
rr. mp = Parcel.obtain();
//这里要求message不能为空,并且已经关联了相应的handler,看来在执行完操作之后发送的消息将由此handler接收并处理
if (result != null && result.getTarget() == null) {
throw new NullPointerException("Message target must not be null");
}
// first elements in any RIL Parcel
rr. mp.writeInt(request);
rr. mp.writeInt(rr.mSerial );
return rr;
}
(2).当RILSender发送一个RIL请求后(即Android启动GPRS流程(MTK)中(21)的send方法),rild收到请求后会进行分发,发送AT命令,若AT命令没有得到响应回复(分为2种,请求回应和主动上报),rild的分发线程被阻塞,java部分的RIL请求送出去后将得不到处理。当RIL请求得到正常处理时,RILReciver所在的线程(在RIL的构造方法中初始化)将接收到回送的response消息,并进行解析。
//***** Instance Variables
//定义在RIL类中的成员变量
LocalSocket mSocket;
HandlerThread mSenderThread;/
RILSender mSender;
Thread mReceiverThread;
RILReceiver mReceiver;
------------------------------------------------------------------
public RIL(Context context, int preferredNetworkType, int cdmaSubscription) {
super(context);
if (RILJ_LOGD) {
riljLog("RIL(context, preferredNetworkType=" + preferredNetworkType +
" cdmaSubscription=" + cdmaSubscription + ")");
} mCdmaSubscription = cdmaSubscription;
mPreferredNetworkType = preferredNetworkType;
mPhoneType = RILConstants.NO_PHONE;
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
mWakeLock.setReferenceCounted(false);
mWakeLockTimeout = SystemProperties.getInt(TelephonyProperties.PROPERTY_WAKE_LOCK_TIMEOUT,
DEFAULT_WAKE_LOCK_TIMEOUT);
mRequestMessagesPending = 0;
mRequestMessagesWaiting = 0;
//初始化SenderThread,RILSender
mSenderThread = new HandlerThread("RILSender");
mSenderThread.start();
Looper looper = mSenderThread.getLooper();
mSender = new RILSender(looper);
ConnectivityManager cm = (ConnectivityManager)context.getSystemService(
Context.CONNECTIVITY_SERVICE);
if (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false) {
riljLog("Not starting RILReceiver: wifi-only");
} else {
riljLog("Starting RILReceiver");
//初始化RILReceiver和mReceiverThread
mReceiver = new RILReceiver();
mReceiverThread = new Thread(mReceiver, "RILReceiver");
mReceiverThread.start();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
context.registerReceiver(mIntentReceiver, filter);
}
}
(3).RILReceiver实现了Runnable。因此这里消息的接收应该在重写的run方法中。该方法中先用一个外层的死循环来连接socket套接字的端点(endPoint,不太懂),但通过 s.connect(l);可能会连接不成功(原因不是很理解,之后再查吧),因此这里如果尝试不成功,会终止执行后面的代码,开始下一次循环,记录尝试次数的变量retryCount 在这里没有看到太大用处,仅是用来根据其值输出相应的日志信息,连接成功后,重置了该retryCount,并将套接字赋值给了全局的变量mSocket ,此后开始其嵌套的内层死循环。内层死循环是从socket中获取到rild的响应,通过readRilMessage(InputStream is, byte[] buffer)将回应写入到buffer缓冲区里,并返回消息的长度,之后再通过 p.unmarshall( buffer , 0, length)得到原始的字节(发送的时候进行了marshall处理),接下来就是通过 processResponse(p)进行消息的处理了,之后先设置当前radio的状态,再关闭socket,重置RILRequest的序列号,并放回RILRequest池里,清空mRequestsList ,最后再用notifyRegistrantsRilConnectionChanged通知所有的registrants(应该是监听ril的吧,具体细节不知),ril已经连接或者断开,当然这里是通知断开了。
public void run() {
int retryCount = 0;
String socketRil = getRilSocketName( mySimId);
try {
for (;;) {
//外层死循环,用来处理socket的连接
LocalSocket s = null;
LocalSocketAddress l;
socketRil = getRilSocketName( mySimId);
..........
try {
s = new LocalSocket();
l = new LocalSocketAddress(socketRil,
LocalSocketAddress.Namespace. RESERVED);
s.connect(l);
} catch (IOException ex){
try {
if (s != null) {
s.close();
}
} catch (IOException ex2) {
//ignore failure to close after failure to connect
}
// don't print an error message after the the first time
// or after the 8th time
//官方源码这里值是8,注释也可以看出来,这里MTK做了修改,将值增大到16
if (retryCount == 16) {
Log. e (LOG_TAG,
"Couldn't find '" + socketRil
+ "' socket after " + retryCount
+ " times, continuing to retry silently" );
} else if (retryCount > 0 && retryCount < 16) {
Log. i (LOG_TAG,
"Couldn't find '" + socketRil
+ "' socket; retrying after timeout" );
}
try {
Thread. sleep(SOCKET_OPEN_RETRY_MILLIS);
} catch (InterruptedException er) {
}
retryCount++;
continue;
}
retryCount = 0;
mSocket = s;
Log. i(LOG_TAG, "Connected to '" + socketRil + "' socket" );
int length = 0;
try {
InputStream is = mSocket.getInputStream();
for (;;) { //内层死循环用来从输入流中读取数据
Parcel p;
//readRilMessage是把以 little-endian 存储的数据还原到本来的样子
//以 little-endian 存储时,数据低位存储在内存低地址,数据高位存储在内存高地址;
//read(buffer, offset, remaining); 读入 remaining 长度的流数据;
//JVM 用 little-endian 存储 int 型的 messageLength ,所以得用位操作转换一下
length = readRilMessage(is, buffer);
if (length < 0) {
// End-of-stream reached
break;
}
p = Parcel. obtain();
p.unmarshall( buffer, 0, length);//返回原始字节数据
p.setDataPosition(0);
//Log.v(LOG_TAG, "Read packet: " + length + " bytes");
processResponse(p);//处理回应
p.recycle();
}
} catch (java.io.IOException ex) {
Log. i(LOG_TAG, "'" + socketRil + "' socket closed" ,
ex);
} catch (Throwable tr) {
Log. e(LOG_TAG, "Uncaught exception read length=" + length +
"Exception:" + tr.toString());
}
Log. i(LOG_TAG, "Disconnected from '" + socketRil
+ "' socket");
setRadioState (RadioState. RADIO_UNAVAILABLE);//存储新的radio状态为无效状态并发送通知
try {
mSocket.close();
} catch (IOException ex) {
}
mSocket = null;
RILRequest. resetSerial();//重置序列号
// Clear request list on close
synchronized (mRequestsList ) {
for (int i = 0, sz = mRequestsList.size() ; i < sz ; i++) {
RILRequest rr = mRequestsList.get(i);
rr.onError( RADIO_NOT_AVAILABLE, null );
rr.release();//将RILRequest放回池中
}
mRequestsList.clear(); //清理请求列表
}
}} catch (Throwable tr) {
Log. e(LOG_TAG, "Uncaught exception", tr);
}
//MTK-END [mtk04070][111121][ALPS00093395]Refined for supporting Gemini
/* We're disconnected so we don't know the ril version */
notifyRegistrantsRilConnectionChanged(-1);//通知所有的Registrants,ril的连接状态改变
}
(4).接下来看一下 processResponse(p)这里的操作。主要是对消息的处理,这里的分支是因为为了匹配定义在ril.cpp的常量值也就是AT的response的两个分类,这两个值分别是:RESPONSE_SOLICITED请求响应(对ril的请求响应,比如这里的开启数据网络)和RESPONSE_UNSOLICITED非请求响应(主动响应,主动上报的,比如网络状态,短信,来电等)。因此processSolicited (p)是对数据网络请求的回应的处理。之后调用releaseWakeLockIfDone释放唤醒锁(唤醒锁是一种机制用来表示有应用程序需要设备留在唤醒状态)。
private void processResponse (Parcel p) {
int type;
type = p.readInt();
//MTK修改开始
/* Solve [ALPS00308613]Receive incoming VT call causes EE, mtk04070, 20120628 */
if (mIsFirstResponse &&
((type == RESPONSE_UNSOLICITED) || (type == RESPONSE_SOLICITED))) {
if (FeatureOption.MTK_VT3G324M_SUPPORT == false) {
Log. d(LOG_TAG, "FeatureOption.MTK_VT3G324M_SUPPORT == false" );
disableVTCapability();
}
mIsFirstResponse = false;
}
//MTK修改结束
if (type == RESPONSE_UNSOLICITED) {
processUnsolicited (p);
} else if (type == RESPONSE_SOLICITED) {
processSolicited (p);
}
releaseWakeLockIfDone();
}
(5).processSolicited(p)方法对响应进行处理。方法中从parcel读取数据,根据读取的serial(从Android启用GPRS流程(MTK)的(21)中加入到mRequestsList)使用findAndRemoveRequestFromList找到RILRequest实例,再根据RILRequest的request值找到相应的分支。根据在setupDataCall(Android启用GPRS流程(MTK) (20) )请求的code是RIL_REQUEST_SETUP_DATA_CALL找到该分支。该该分支中调用了responseSetupDataCall(p)方法对响应的结果进行解析,包括ip地址,网关,dns等等。如果解析发现有错误,将抛出异常。不管是否存在异常,都会将AR执行的返回结果放置到AsyncResult中,并赋值给RILRequest中的Message的obj,再通过sendToTarget()发送给调用者处理。这里的Mssage最初是在GsmDataConnection或CdmaDataConnection中的onConnect()方法中创建的,其关联的handler是StateMachine.java中的SmHandler,因此最终的消息处理在DataConnection.java中。进入到DataConnection.java,找到处理Message.what为EVENT_SETUP_DATA_CONNECTION_DONE的地方。
private void processSolicited (Parcel p) {
int serial, error;
boolean found = false;
serial = p.readInt();
error = p.readInt();
RILRequest rr;
rr = findAndRemoveRequestFromList(serial);
if (rr == null) {
Log. w(LOG_TAG, "Unexpected solicited response! sn: "
+ serial + " error: " + error);
return;
}
………..
if (error == 0 || p.dataAvail() > 0) {
// either command succeeds or command fails but with data payload
try {switch (rr.mRequest) {
/*
cat libs/telephony/ril_commands.h \
| egrep “^ *{RIL_” \
| sed -re ‘s/{([^,]+),[^,]+,([^}]+).+/case \1: ret = \2(p); break;/’
*/
//接下来非常多的RIL请求处理,目测有几十种(这里省略部分)
case RIL_REQUEST_GET_SIM_STATUS : ret = responseIccCardStatus(p); break;
.........
case RIL_REQUEST_SEND_SMS : ret = responseSMS(p); break;
case RIL_REQUEST_SEND_SMS_EXPECT_MORE : ret = responseSMS(p); break;
case RIL_REQUEST_SETUP_DATA_CALL : ret = responseSetupDataCall(p); break;
.....
case RIL_REQUEST_QUERY_MODEM_TYPE : ret = responseInts(p); break;
default:
throw new RuntimeException("Unrecognized solicited response: " + rr. mRequest);
//break;
}} catch (Throwable tr) {
// Exceptions here usually mean invalid RIL responses
//解析发现RILResponse存在错误,就会抛出异常
Log. w(LOG_TAG, rr.serialString() + "< "
+ requestToString(rr.mRequest)
+ " exception, possible invalid RIL response" , tr);
if (rr.mResult != null) {//只要message不为空,仍然将其回送到调用者并由其进行处理
AsyncResult. forMessage(rr.mResult, null, tr);//使用新构造一个AsyncResult ,并赋值给message的obj对象
rr. mResult.sendToTarget();
}
rr.release();//将RILRequest放回池中
return;
}
}
if (error != 0) {
rr.onError(error, ret);
rr.release();
return;
}
if (RILJ_LOGD ) riljLog(rr.serialString() + "< " + requestToString(rr.mRequest)
+ " " + retToString(rr.mRequest , ret));
if (rr.mResult != null) {
//解析正常,并且message存在,将AT执行的返回结果放到AsyncResult中,并赋值rr. mResult的obj,再由sendToTarget()回送到调用者并由其进行处理
AsyncResult. forMessage(rr.mResult, ret, null);//赋值到rr.mResult的obj对象
rr. mResult.sendToTarget();
}
rr.release();
}
//使用异常和处理的结果,及消息,新构造一个AsyncResult ,并赋值给message的obj对象,该变了obj的值,obj就包含了更多信息。
/* Saves and sets m.obj /
public static AsyncResult
forMessage (Message m, Object r, Throwable ex)
{
AsyncResult ret;
//此时m.obj是
ret = new AsyncResult (m.obj , r, ex);
m. obj = ret;
return ret;
}
(6).DataConnection.java对EVENT_SETUP_DATA_CONNECTION_DONE的处理在DcActivatingState的processMessage()方法中。说明当ril的消息返回时,此时的连接状态正处在激活中。msg当中包含的obj就是在上AsyncResult. forMessage()处理之后的AsyncResult,而AsyncResult中的userObj实际上就是在GsmDataConnection/CdmaDataConnection的OnConnect()方法当中封装到Message当中的ConnectionParams。如果在RIL.java中处理AT的响应详细有异常,AsyncResult中的成员变量result将为null,如果没有异常则成员变量exception为null ,当从消息之中获取到这些消息后,调用onSetupConnectionCompleted方法对AsyncResult进行处理和分类。只有当结果为 DataCallState.SetupResult为SUCCESS时才会切换到ActiveState状态.在切换到ActiveState状态之前,先通过setEnterNotificationParams()方法将连接参数cp和failCause设置给DcActiveState状态的成员变量mConnectionParams和mFailCause,在transitionTo到Activestate后,将会在其enter()方法中调用notifyConnectCompleted()将连接成功的消息发送出去。
/**
* The state machine is activating a connection.
*/
private class DcActivatingState extends State {
@Override
public boolean processMessage(Message msg) {
boolean retVal;
AsyncResult ar;
ConnectionParams cp;
........
case EVENT_SETUP_DATA_CONNECTION_DONE :
if (DBG ) log("DcActivatingState msg.what=EVENT_SETUP_DATA_CONNECTION_DONE");
ar = (AsyncResult) msg. obj; //
cp = (ConnectionParams) ar. userObj;
DataCallState.SetupResult result = onSetupConnectionCompleted(ar);
if (DBG ) log("DcActivatingState onSetupConnectionCompleted result=" + result);
switch (result) {
case SUCCESS :
// All is well
mActiveState.setEnterNotificationParams(cp, FailCause.NONE);
transitionTo( mActiveState);
break;
case ERR_BadCommand ://指令错误
// Vendor ril rejected the command and didn't connect.
// Transition to inactive but send notifications after
// we've entered the mInactive state.
mInactiveState.setEnterNotificationParams(cp, result.mFailCause, -1);
transitionTo( mInactiveState);
break;
case ERR_UnacceptableParameter :
// The addresses given from the RIL are bad
tearDownData(cp);
transitionTo( mDisconnectingErrorCreatingConnection );
break;
case ERR_GetLastErrorFromRil :
//获取到了上一次RIL请求的消息,然后会切换到mInactiveState状态去
// Request failed and this is an old RIL
phone.mCM .getLastDataCallFailCause(
obtainMessage( EVENT_GET_LAST_FAIL_DONE, cp));
break;
case ERR_RilError ://ril层存在错误
// Request failed and mFailCause has the reason
mInactiveState.setEnterNotificationParams(cp, result.mFailCause,
getSuggestedRetryTime(ar));
transitionTo( mInactiveState);
break;
case ERR_Stale :
// Request is stale, ignore.
break;
default:
throw new RuntimeException("Unknown SetupResult, should not happen");
}
retVal = HANDLED;
break;
........
}
(7).看看onSetupConnectionCompleted()方法都做是怎么做的处理吧。先根据成员变量exception是否为null判断。如果不为null根据exception的类型,判断是属于何种异常则关联上相应的失败原因。如果为null,则判断连接参数的tag值与当前的tag值是否一致(该值在DataConnection的DcInactiveState中的enter方法修改值并在EVENT_CONNECT发生时,放置到了ConnectionParams中),一致则说明是同一次网络开启请求的处理,否则则认为本次响应的信息是错误的。即没有异常,tag值也能匹配,并且响应的staus是正确的,说明本次开启操作正确,接下来更新使用updateLinkProperty()方法更新LinkProperties(包括HttpProxy,会调用DataCallState中的setLinkProperties()方法,将DataCallState的成员变量ifname,addresses, dnses ,gateways 等关联到LinkProperties中,ifname就是interface name对应apn创建成功之后的网络设备名),并将状态机切换到ActiveState状态。
private DataCallState