看此篇看不太懂的同志們可以先看看我另一篇文章: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.SetupResult onSetupConnectionCompleted (AsyncResult ar) {
DataCallState response = (DataCallState) ar.result;
ConnectionParams cp = (ConnectionParams) ar.userObj;
DataCallState.SetupResult result;
if(ar.exception!=null) {
if(DBG) {
log("onSetupConnectionCompleted failed, ar.exception="+ ar.exception+
" response="+ response);
}
if(ar.exceptioninstanceofCommandException
&& ((CommandException) (ar.exception)).getCommandError()
== CommandException.Error.RADIO_NOT_AVAILABLE) {
result = DataCallState.SetupResult.ERR_BadCommand;
result.mFailCause= FailCause.RADIO_NOT_AVAILABLE;
}elseif((response ==null) || (response.version< 4)) {
result = DataCallState.SetupResult.ERR_GetLastErrorFromRil;
}else{
result = DataCallState.SetupResult.ERR_RilError;
result.mFailCause= FailCause.fromInt(response.status);
}
}elseif(cp.tag!=mTag) {//在EVENT_CONNECT發送時,mtag賦值到cp的tag,這裏進行匹配判斷,確定是否是同一次操作
if(DBG) {
log("BUG: onSetupConnectionCompleted is stale cp.tag="+ cp.tag+", mtag="+mTag);
}
result = DataCallState.SetupResult.ERR_Stale;
}elseif(response.status!= 0) {
result = DataCallState.SetupResult.ERR_RilError;
result.mFailCause= FailCause.fromInt(response.status);
}else{//
if(DBG) log("onSetupConnectionCompleted received DataCallState: "+ response);
cid= response.cid;
result = updateLinkProperty(response).setupResult;
}
return result;
}
(8).根據notifyConnectCompleted()名,明顯可以知道該方法是通知網絡連接完成用的。那來看看這個方法都做了些啥子東西。是怎麼把消息發送出去的。
private void notifyConnectCompleted(ConnectionParams cp, FailCause cause) {
Log. e("YKB","notifyConnectCompleted-->ConnectionParams "+cp.apn.toString()+" msg:"+ cp.onCompletedMsg.toString());
Message connectionCompletedMsg = cp.onCompletedMsg;
if(connectionCompletedMsg==null) {
return;
}
longtimeStamp = System.currentTimeMillis();
//cid是上面第(7)點從response中取出的成員變量cid ;根據DataCallState註釋說是在ril.h中的,
//查到源碼目 錄/hardware/ril/include/telephony/ril.h,
//找到cid的註釋爲:Context ID, uniquely identifies this call
connectionCompletedMsg.arg1=cid;
if(cause == FailCause.NONE) {
createTime= timeStamp;
AsyncResult. forMessage(connectionCompletedMsg);
}else{
lastFailCause= cause;
lastFailTime= timeStamp;
AsyncResult. forMessage(connectionCompletedMsg, cause,
newCallSetupException(mRetryOverride));
}
if(DBG) log("notifyConnectionCompleted at "+ timeStamp +" cause="+ cause);
connectionCompletedMsg.sendToTarget();
}
方法先從cp中取相應的Message,這個message是哪兒來的呢,回憶一下。在Android 啓用GPRS流程(MTK)中,當DataConnection.java的bringUp()方法時將傳遞過來的參數message和apnStting封裝成了ConnectionParams,所以這裏判斷的cp. onCompletedMsg就是傳遞給bringUp的那個參數Message,好吧。再回憶一下,這個message是在哪兒傳給bringUp()的。實際上這個message是在CdmaDataConnectionTracker和GsmDataConnectionTracker中的setupData()方法中進行構造的。
Message msg = obtainMessage();
msg.what= DctConstants.EVENT_DATA_SETUP_COMPLETE;
msg.obj= reason;
conn. bringUp(msg,mActiveApn) ;
接下來將唯一標識當前數據請求的id放置在message的arg1中,並根據記錄下當前的時間戳,然後將該Message又構造成一個一個AsyncResult實例。(爲什麼要構造成AsyncResult來進行傳遞,不是很明白)發送給相應的handler進行處理。由於在構造message時是使用的GsmDataConnectionTracker.java和CdmaDataConnectionTracker.java的obtainMessage方法,而這兩個Tracker繼承自DataConnectionTracker而DataConnectionTracker又繼承自Handler,因此該消息將在DataConnectionTracker中進行處理。
(9).DataConnectionTracker.java中的handleMessage方法中找到分支
(/frameworks/opt/telephony/src/java/com/android/internal/telephony/DataConnectionTracker.java)
case DctConstants.EVENT_DATA_SETUP_COMPLETE:
mCidActive= msg.arg1;
onDataSetupComplete((AsyncResult) msg.obj);
break;
看到將arg1(也就是唯一的請求標識cid)賦值給了成員變量mCidActive 。比較簡單沒什麼說的,接下來還是看onDataSetupComplete()方法。不出意外的肯定又會分支到CdmaDataConnectionTracker和GsmDataConnectionTracker。
(10).先看CdmaDataConnectionTracker中onDataSetupComplete()方法吧
protected void onDataSetupComplete(AsyncResult ar) {
String reason =null;
if(ar.userObjinstanceofString) {
reason = (String) ar.userObj;
}
if(isDataSetupCompleteOk(ar)) {
// Everything is setup
notifyDefaultData(reason);
}else{
FailCause cause = (FailCause) (ar.result);
if(DBG) log("Data Connection setup failed "+ cause);
// No try for permanent failure
if(cause.isPermanentFail()) {
notifyNoData(cause);
return;
}
intretryOverride = -1;
if(ar.exceptioninstanceofDataConnection.CallSetupException) {
retryOverride =
((DataConnection.CallSetupException)ar.exception).getRetryOverride();
}
if(retryOverride == RILConstants.MAX_INT) {
if(DBG) log("No retry is suggested.");
}else{
startDelayedRetry(cause, reason, retryOverride);
}
}
}
首先是將AsyncResult的uerObj成員變量取出來(CdmaDataConnectionTracker在構造EVENT_DATA_SETUP_COMPLETE時,傳遞的reason是字符串,而GsmDataConnectionTracker傳遞是ApnContext)。稍後使用DataConnectionTracker中的isDataSetupCompleteOk()對AsyncResult進行是否創建完成的判斷。
protected boolean isDataSetupCompleteOk(AsyncResult ar) {
if(ar.exception!=null) {
if(DBG) log("isDataSetupCompleteOk return false, ar.result="+ ar.result);
returnfalse;
}
if(mFailDataSetupCounter<= 0) {
if(DBG) log("isDataSetupCompleteOk return true");
returntrue;
}
ar.result=mFailDataSetupFailCause;
if(DBG) {
log("isDataSetupCompleteOk return false"+
" mFailDataSetupCounter="+mFailDataSetupCounter+
" mFailDataSetupFailCause="+mFailDataSetupFailCause);
}
mFailDataSetupCounter-= 1;
returnfalse;
}
由於在成功返回構造AsyncResult只使用了Message,因此這裏AsyncResult的exception成員變量必定爲null,接下來是通過判斷CdmaDataConnectionTracker的成員變量mFailDataSetupCounter來決定數據連接的創建返回值。而mFailDataSetupCounter的值只有在接收到廣播
// Used for debugging. Send the INTENT with an optional counter value with the number
// of times the setup is to fail before succeeding. If the counter isn’t passed the
// setup will fail once. Example fail two times with FailCause.SIGNAL_LOST(-3)
// adb shell am broadcast \
// -a com.android.internal.telephony.dataconnectiontracker.intent_set_fail_data_setup_counter \
// –ei fail_data_setup_counter 3 – ei fail_data_setup_fail_cause -3
protectedstaticfinalStringINTENT_SET_FAIL_DATA_SETUP_COUNTER=
"com.android.internal.telephony.dataconnectiontracker. intent_set_fail_data_setup_counter";
時纔會改變。根據該廣播的註釋可知它是用於調試的,用來記錄接續之前建立數據失敗的次數。實際上這裏沒什麼用,所以這裏通常都是返回成功的。因此將直接調用notifyDefaultData(String reason)通知網絡狀態改變了。
privatevoidnotifyDefaultData(String reason) {
setState(DctConstants.State.CONNECTED);//修改狀態,在setState中會將CdmaDataConnectionTracker成員變量mState標記爲Connected
notifyDataConnection(reason);//會調用到DataConnectionTracker.java中,該方法用來發送通知-所有活動的apn當前數據連接狀態。
和GsmDataConnectionTracker不同,GsmDataConnectionTracker重寫了這個方法。
startNetStatPoll();//開始NetStat投票(作用不明確,暫時未理解)
startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);//啓動數據失速警報(大概是在指定的一個時間後發出一個廣播INTENT_DATA_STALL_ALARM 這個指定的時間從Settings.Global取出)
mDataConnections.get(0).resetRetryCount();//重置嘗試次數爲0
}
notifyDataConnection(reason);調用的是DataConnectionTracker.java方法,在該方法中通過PhoneBase的notifyDataConnection(string,string)把消息傳遞出去。
protected void notifyDataConnection(String reason) {
for(intid = 0; id < DctConstants.APN_NUM_TYPES; id++) {
// Only active apns' state changed
if(dataEnabled[id] &&mActiveApn!=null
&&mActiveApn.canHandleType(apnIdToType(id))) {
mPhone.notifyDataConnection(reason, apnIdToType(id));
}
}
notifyOffApnsOfAvailability(reason);
}
再回到onDataSetupComplete(AsyncResult ar),如果失敗了,根據參數中ar攜帶的FailCause進行分支處理,如果FailCause是固定錯誤,則不會進行失敗嘗試(MTK這裏進行了修改,cause.isPermanentFail()始終返回的爲false),如果ar攜帶的異常類型是DataConnection.CallSetupException,則從取該異常的成員變量mRetryOverride進行重試數據建立建立
(11).回到GsmDataConnectionTracker.java的onDataSetupComplete(AsyncResult ar).由於在GsmDataConnectionTracker中的setupData()方法中進行構造Message傳遞的message的obj對象是ApnContext,因此這裏首先從ar中取出userobj進行類型判斷和強轉,如果類型不匹配,將拋出異常,反之則得到一個ApnConext實例。和CdmaDataConnectionTracker一樣,也是調用DataConnectionTracker的isDataSetupCompleteOk(ar)對數據連接完成進行確認的判斷,若爲false則需要進行相應的處理,處理之後使用trySetupData()方法對建立數據連接進行重試。如果爲ture,則從apnContext中取出DataConnectionAc實例,該實例是用來在不同handler之間進行消息傳遞的通道,這個DataConnectionAc的設置是在setupData(ApnContext apnContext)進行的,如果該實例不存在,則存在該錯誤,並進行數據連接重試,若存在,則再從apnContext中取出DataConnection和apnSetting的實例,並根據apnSetting中包含的端口號,代理信息構造ProxyProperties ,再由DataConnectionAc更新到當前LinkProperties上去。最後判斷一下當前的連接是不是default連接,並更新systemProperties(gsm.defaultpdpcontext.active),如果是默認連接且mPreferredApn 尚爲設置,則更新mPreferredApn 的引用。最後通過notifyDefaultData(apnContext)通知當前數據連接狀態;
@Override
protected void onDataSetupComplete(AsyncResult ar) {
DataConnection.FailCause cause = DataConnection.FailCause.UNKNOWN;//新構造的unkonw
booleanhandleError =false;
ApnContext apnContext =null;
if(ar.userObjinstanceofApnContext){//obj的類型判斷
apnContext = (ApnContext)ar.userObj;
}else{
thrownewRuntimeException("onDataSetupComplete: No apnContext");
}
if(isDataSetupCompleteOk(ar)) {
//AsyncChannel to a DataConnection(數據連接的通信通道),DataConnectionAc 是爲了讓GsmDataConnectionTracker所在的
//handler和DataConnection所在的Handler進行通信
DataConnectionAc dcac = apnContext.getDataConnectionAc();
........
if(dcac ==null) {//說明存在錯誤。
log("onDataSetupComplete: no connection to DC, handle as error");
cause = DataConnection.FailCause.CONNECTION_TO_DATACONNECTIONAC_BROKEN;
handleError =true;//更新標誌位,表示需要處理錯誤,以便重新嘗試
}else{
DataConnection dc = apnContext.getDataConnection();
ApnSetting apn = apnContext.getApnSetting();
if(DBG) {
log("onDataSetupComplete: success apn="+ (apn ==null?"unknown": apn.apn));
}
//獲取端口號,代理信息,若存在就關聯到LinkProperties上去
if(apn !=null&& apn.proxy!=null&& apn.proxy.length() != 0) {
try{
String port = apn.port;
if(TextUtils.isEmpty(port)) port ="8080";
ProxyProperties proxy =newProxyProperties(apn.proxy,
Integer. parseInt(port),null);
dcac.setLinkPropertiesHttpProxySync(proxy);
}catch(NumberFormatException e) {
loge("onDataSetupComplete: NumberFormatException making ProxyProperties ("+
apn.port+"): "+ e);
}
}
// everything is setup
if(TextUtils.equals(apnContext.getApnType(),PhoneConstants.APN_TYPE_DEFAULT)) {
SystemProperties. set("gsm.defaultpdpcontext.active","true");
if(canSetPreferApn&&mPreferredApn==null) {
if(DBG) log("onDataSetupComplete: PREFERED APN is null");
mPreferredApn= apn;
if(mPreferredApn!=null) {
setPreferredApn(mPreferredApn.id);
}
}
}else{
SystemProperties. set("gsm.defaultpdpcontext.active","false");
}
mTetheringExt.onDataSetupComplete(apnContext);
// Notify call start again if call is not IDLE and not concurrent
//如果當前gsm電話狀態不是空閒,並且語音通話和數據網絡不支持併發,那麼就要再次通知通話開始了
if(((GsmCallTracker)mGsmPhone.getCallTracker()).state!= PhoneConstants.State.IDLE&& !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
if(DBG) log("onDataSetupComplete: In 2G phone call, notify data REASON_VOICE_CALL_STARTED");
notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);
}
notifyDefaultData(apnContext);
/// M: SCRI and Fast Dormancy Feature @{
//MTK-START [mtk04070][111205][ALPS00093395]Add for SCRI
/* Add by MTK03594 for SCRI feature */
startScriPoll();
//MTK-END [mtk04070][111205][ALPS00093395]Add for SCRI
/// @}
}
//isDataSetupCompleteOk(ar)爲false
}else{
cause = (DataConnection.FailCause) (ar.result);
if(DBG) {
ApnSetting apn = apnContext.getApnSetting();
log(String. format("onDataSetupComplete: error apn=%s cause=%s",
(apn ==null?"unknown": apn.apn), cause));
}
if(cause.isEventLoggable()) {
// Log this failure to the Event Logs.
intcid = getCellLocationId();
EventLog. writeEvent(EventLogTags .PDP_SETUP_FAIL,
cause.ordinal(), cid, TelephonyManager. getDefault().getNetworkType());
}
// Count permanent failures and remove the APN we just tried
if(cause.isPermanentFail()) apnContext.decWaitingApnsPermFailCount();
if(cause == DataConnection.FailCause.GMM_ERROR&&
mPhone.getServiceStateTracker().getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE) {
// stop retry for GMM_ERROR and let GPRS attach event to trigger如果是GMM_ERROR停止重試,並且觸發GPRS附加事件
//更新ApnConext中的成員變量mWaitingApns和mWaitingApnsPermanentFailureCountDown(此時mWaitingApns的size爲0)
//mWaitingApnsPermanentFailureCountDown設置爲0,而0表示mWaitingApns中的所有的apn存在永久性錯誤
apnContext.setWaitingApns(newArrayList<ApnSetting>());
log("onDataSetupComplete: GMM_ERROR, wait for GPRS attach to retry.");
}elseif(mGsmDCTExt.needDelayedRetry(cause.getErrorCode())) {//根據FailCause中的errorcode判斷是否需要重試
log("onDataSetupComplete: need delayed retry");
intretryCount = apnContext.getRetryCount();
//配置一個簡單的線性序列的時間,加上一個隨機值
apnContext.getDataConnection().configureRetry(2, 45000, 0);//重試參數(最大重試次數,延遲時間,隨機值介於0和第二個參數之間)
apnContext.getDataConnection().setRetryCount(retryCount);
apnContext.removeWaitingApn(apnContext.getApnSetting());//移除所以出於waiting狀態的apn
}
if(DBG) {
log(String. format("onDataSetupComplete: WaitingApns.size=%d"+
" WaitingApnsPermFailureCountDown=%d",
apnContext.getWaitingApns().size(),
apnContext.getWaitingApnsPermFailCount()));
}
handleError =true;
}
//處理錯誤
if(handleError) {
// See if there are more APN's to try
if(apnContext.getWaitingApns().isEmpty()) {
if(apnContext.getWaitingApnsPermFailCount() == 0) {
if(DBG) {
log("onDataSetupComplete: All APN's had permanent failures, stop retrying");
}
apnContext.setState(DctConstants.State.FAILED);
mPhone.notifyDataConnection(Phone.REASON_APN_FAILED, apnContext.getApnType());
apnContext.setDataConnection(null);//清理DataConnection,在trySetupData將重新關聯新的實例
apnContext.setDataConnectionAc(null);//清理DataConnectionAc,在trySetupData將重新關聯新的實例
// M: try to enable apn which is in waiting list
if(apnContext.getApnType() == PhoneConstants.APN_TYPE_MMS) {
enableWaitingApn();
}
if(PhoneFactory.isDualTalkMode()) {
if(apnContext.getApnType() != PhoneConstants.APN_TYPE_DEFAULT&&mWaitingApnList.isEmpty()) {
// try to restore default
trySetupData(Phone.REASON_DATA_ENABLED, PhoneConstants.APN_TYPE_DEFAULT);
}
}
}else{
if(DBG) log("onDataSetupComplete: Not all permanent failures, retry");
// check to see if retry should be overridden for this failure.
intretryOverride = -1;
if(ar.exceptioninstanceofDataConnection.CallSetupException) {
retryOverride = ((DataConnection.CallSetupException)ar.exception).getRetryOverride();
}
if(retryOverride == RILConstants.MAX_INT) {
if(DBG) log("No retry is suggested.");
}else{
startDelayedRetry(cause, apnContext, retryOverride);
}
}
//handleError爲false
}else{
if(DBG) log("onDataSetupComplete: Try next APN");
apnContext.setState(DctConstants.State.SCANNING);
// Wait a bit before trying the next APN, so that
// we're not tying up the RIL command channel
startAlarmForReconnect(APN_DELAY_MILLIS, apnContext);
}
}
}
(12).來看看GsmDataConnectionTracker中的notifyDefaultData(apnContext)做了些什麼處理.和第(10)中CdmaDataConnectionTracker的notifyDefaultData(String)做的事情都差不多,更新ApnContext的狀態,開啓NetStatPoll,DataStallAlarm,重置重試次數,之後也是通過phoneBase的實例mPhone調用notifyDataConnection(string,string)把消息傳遞出去。Gsm和Cdma的分支在這裏又合併到一起。
private void notifyDefaultData(ApnContext apnContext) {
// M: If context is disabled and state is DISCONNECTING,
// should not change the state to confuse the following enableApnType() which returns PhoneConstants.APN_ALREADY_ACTIVE as CONNECTED
if(apnContext.getState() != DctConstants.State.DISCONNECTING) {
apnContext.setState(DctConstants.State.CONNECTED);
}
// setState(DctConstants.State.CONNECTED);
mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
startNetStatPoll();
startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
// reset reconnect timer
apnContext.setRetryCount(0);
}
(13).PhoneBase繼承自handler,實現了Phone接口。在這個方法中又調用PhoneNotifier的來傳遞消息,它是對所有系統範圍狀態改變通知的抽象。DefaultPhoneNotifier實現了phoneNotifier,這裏實際上調用的DefaultPhoneNotifier的notifyDataConnection(Phone sender, String reason, String apnType, DataState state)方法.此外在getDataConnectionState(apntype)會分支到GSMPhone.java和CDMAPhone.java,返回相對應的PhoneConstants中的DataState枚舉類型(這裏是返回PhoneConstants.DataState.CONNECTED)
(frameworks/opt/telephony/src/java/com/android/internal/telephony/PhoneBase.java)
(frameworks/base/telephony/java/com/android/internal/telephony/PhoneConstants.java)
(frameworks/opt/telephony/src/java/com/android/internal/telephony/cdma/CDMAPhone.java)
(frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/GSMPhone.java)
public void notifyDataConnection(String reason, String apnType) {
mNotifier.notifyDataConnection(this, reason, apnType, getDataConnectionState(apnType));
}
在DefaultPhoneNotifier中又調用了doNotifyDataConnection()方法,接下來就看看這個方法做了什麼處理
//MTK-START [mtk04070][111125][ALPS00093395]MTK modified
public void notifyDataConnection(Phone sender, String reason, String apnType,
PhoneConstants.DataState state) {
doNotifyDataConnection(sender, reason, apnType, state);
}
//MTK-END [mtk04070][111125][ALPS00093395]MTK modified
(14).doNotifyDataConnection() 根據傳入的 apnType先從phone 中取出連接參數 linkProperties 和linkCapabilities( 代表鏈接功能的類 ),再獲取包含手機狀態和服務相關信息的 ServiceState實例,作爲之後TelephonyRegistry和 notifyDataStateChangeCallback()的參數。之後通過ITelephonyRegistry.aidl跨進程調用到 TelephonyRegistry.java的notifyDataConnection() 方法。
(frameworks/opt/telephony/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java)
(/frameworks/base/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl)
private void doNotifyDataConnection(Phone sender, String reason, String apnType,
PhoneConstants.DataState state) {
//TODO
// use apnType as the key to which connection we're talking about.
// pass apnType back up to fetch particular for this one.
TelephonyManager telephony = TelephonyManager. getDefault();//TelephonyManager 單列模式類,懶漢式,通過此方法獲取 TelephonyManager 實例
LinkProperties linkProperties = null;
LinkCapabilities linkCapabilities = null;
booleanroaming =false;
if(state == PhoneConstants.DataState.CONNECTED) {
linkProperties = sender.getLinkProperties(apnType);
linkCapabilities = sender.getLinkCapabilities(apnType);
}
ServiceState ss = sender.getServiceState();/
if(ss !=null) roaming = ss.getRoaming();// 獲取到手機信息和服務信息類,獲取漫遊信息
intnetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
if(!FeatureOption.MTK_GEMINI_SUPPORT) {
// 上面通過 getDefault獲取到telephony ,由於是懶漢式,所以不會爲 null
networkType = ((telephony!= null) ? telephony.getNetworkType() : TelephonyManager.NETWORK_TYPE_UNKNOWN );
} else{
networkType = ((telephony!= null) ? telephony.getNetworkTypeGemini (sender.getMySimId()) : TelephonyManager.NETWORK_TYPE_UNKNOWN );
}
log( "doNotifyDataConnection " +"apnType="+ apnType + ",networkType="+ networkType +", state="+ state);
notifyDataStateChangeCallback(
state.toString(),
reason,
sender.getActiveApnHost(apnType),
apnType,
sender.isDataConnectivityPossible(apnType),
mSimId);
try{
// 將 PhoneConstants.DataState轉換成 TelephoneManager當中定義的狀態。這裏是將 PhoneConstants.DataState. CONNECTED 轉換爲TelephonyManager.DATA_CONNECTED
mRegistry.notifyDataConnection(
convertDataState(state),
sender.isDataConnectivityPossible(apnType),// 檢查該apn數據連接是否被允許,之後會通過調用到 PhoneBase.java,再分支到CdmaDataConnectionTracker和 GsmDataConnectionTracker的isDataPossible(String apnType) 方法
reason,
sender.getActiveApnHost(apnType), //返回或的apn 主機字符串 (其實是ApnSetting.apn)
apnType,
linkProperties,
linkCapabilities,
networkType,
roaming);
} catch(RemoteException ex) {
// system process is dead
}
// M: Update SIM indication state for data connection state change
if(senderinstanceofPhoneBase) {
((PhoneBase)sender).updateDataStateOfSimIndication(apnType, state);
}
}
notifyDataStateChangeCallback() 根據方法名推測是一個數據狀態改變的回調
private void notifyDataStateChangeCallback(String state, String reason, String apnName,
String apnType, booleanunavailable,intsimId)
{
Iterator<IDataStateChangedCallback> iterator = mDataStateChangedCallbacks.iterator();
while(iterator.hasNext()) {
IDataStateChangedCallback callback = iterator.next();
callback.onDataStateChanged(state, reason, apnName, apnType, unavailable, simId);
}
}
IDataStateChangedCallback 是一個接口,包含一個方法,該方法在 framework中沒有找到該接口的實現,推測是內置的系統級別應用程序進行實現。
public interface IDataStateChangedCallback {
voidonDataStateChanged(String state, String reason, String apnName,
String apnType, booleanunavailable,intsimId);
}
(15).TelephonyRegistry的 notifyDataConnection()方法。首先是檢查是否有 notify權限( android.Manifest.permission.MODIFY_PHONE_STATE ),沒有權限的話,將不會繼續執行連接消息的傳遞。再判斷是否需要對目前的連接信息進行修改,如果有進行修改,則標記 modified爲true 。方法中 mRecords.events的實際上就是在NetworkController.java中註冊的消息,如果這些消息存在,並且 過IPhoneStateListener的 onDataConnectionStateChanged()回調觸發相應的處理,(之前進行過 LISTEN_DATA_CONNECTION_STATE 註冊的都將觸發,已知 NetworkController.java)。最後通過broadcastDataConnectionStateChanged () 發送網絡狀態改變的廣播出去, ConnectivityService.java中將接收到這個廣播,再繼續做處理。
(frameworks/base/services/java/com/android/server/TelephonyRegistry.java)
public void notifyDataConnection(intstate, booleanisDataConnectivityPossible,
String reason, String apn, String apnType, LinkProperties linkProperties,
LinkCapabilities linkCapabilities, intnetworkType,booleanroaming) {
if(!checkNotifyPermission("notifyDataConnection()" )) {
return;
}
synchronized(mRecords ) {
// M: For synchronization of mRecords, move log into synchronization protection.
if(DBG ) {
Slog. i(TAG, "notifyDataConnection: state=" + state +" isDataConnectivityPossible="
+ isDataConnectivityPossible + " reason='"+ reason
+ "' apn='"+ apn +"' apnType=" + apnType +" networkType="+ networkType
+ " mRecords.size()="+mRecords .size() +" mRecords="+ mRecords);
}
booleanmodified =false;
if(state == TelephonyManager.DATA_CONNECTED) {// 數據已連接
if(!mConnectedApns .contains(apnType)) {
mConnectedApns.add(apnType);//添加到連接的 apn列表中
if(mDataConnectionState != state) {//修改當前的成員變量標識連接狀態
mDataConnectionState= state;
modified = true;
}
}
} else{// 如果不是連接,則移除
if(mConnectedApns .remove(apnType)) {
if(mConnectedApns .isEmpty()) {
mDataConnectionState= state;//連接狀態修改
modified = true;
} else{
// leave mDataConnectionState as is and
// send out the new status for the APN in question.
}
}
}
mDataConnectionPossible= isDataConnectivityPossible;
mDataConnectionReason= reason;
mDataConnectionLinkProperties= linkProperties;
mDataConnectionLinkCapabilities= linkCapabilities;
if(mDataConnectionNetworkType != networkType) {//網絡連接類型修改
mDataConnectionNetworkType= networkType;
// need to tell registered listeners about the new network type
modified = true;
}
if(modified) {
if(DBG ) {
Slog. d(TAG, "onDataConnectionStateChanged(" +mDataConnectionState
+ ", "+mDataConnectionNetworkType +")");
}
for(Record r :mRecords ) {
//PhoneStateListener. LISTEN_DATA_CONNECTION_STATE 偵聽更改數據連接狀態
if((r.events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
try{
// 觸發IPhoneStateListener的 onDataConnectionStateChanged回調
r.callback.onDataConnectionStateChanged( mDataConnectionState,
mDataConnectionNetworkType);
} catch(RemoteException ex) {
mRemoveList.add(r.binder );
}
}
}
handleRemoveListLocked();// mRemoveList 和 mRecords 是同步域,根據 mRemoveList的binder 移除mRecords中的 Record,
}
}
broadcastDataConnectionStateChanged (state, isDataConnectivityPossible, reason, apn,
apnType, linkProperties, linkCapabilities, roaming);
}
(16)在framework 中NetworkController.java(/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java )的構造方法中調用了TelephonyManager的 listen方法進行註冊。監聽包括網絡服務狀態 (LISTEN_SERVICE_STATE), 信號強弱 (LISTEN_SIGNAL_STRENGTHS狀態欄的信號強度圖標 ),手機通話狀態(通話,空閒,響鈴 LISTEN_CALL_STATE),數據連接狀態 (LISTEN_DATA_CONNECTION_STATE),以及數據連接上的數據業務方向改變 (狀態欄使用它來顯示相應的數據流量圖標 )。
public NetworkController(Context context) {
......
ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
Context. CONNECTIVITY_SERVICE);
mHasMobileDataFeature= cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
.......
// set up the default wifi icon, used when no radios have ever appeared
updateWifiIcons();
updateWimaxIcons();
// telephony
mPhone= (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
//用的或運算將多種類型都帶進去了
mPhone.listen(mPhoneStateListener ,
PhoneStateListener. LISTEN_SERVICE_STATE
| PhoneStateListener. LISTEN_SIGNAL_STRENGTHS
| PhoneStateListener. LISTEN_CALL_STATE
| PhoneStateListener. LISTEN_DATA_CONNECTION_STATE
| PhoneStateListener. LISTEN_DATA_ACTIVITY);
.......
// broadcasts
IntentFilter filter = newIntentFilter();
filter.addAction(WifiManager. RSSI_CHANGED_ACTION);
filter.addAction(WifiManager. WIFI_STATE_CHANGED_ACTION);
filter.addAction(WifiManager. NETWORK_STATE_CHANGED_ACTION );
filter.addAction(TelephonyIntents. ACTION_SIM_STATE_CHANGED);
filter.addAction(TelephonyIntents. SPN_STRINGS_UPDATED_ACTION );
filter.addAction(ConnectivityManager. CONNECTIVITY_ACTION);
filter.addAction(ConnectivityManager. INET_CONDITION_ACTION);
filter.addAction(Intent. ACTION_CONFIGURATION_CHANGED );
filter.addAction(Intent. ACTION_AIRPLANE_MODE_CHANGED );
.......
}
在框架中NetworkStatsService.java中也進行了TelephonyManager的listen方法進行PhoneStateListener. LISTEN_DATA_CONNECTION_STATE 註冊,不過貌似應該不會觸發。由於
COMBINE_SUBTYPE_ENABLED 恆爲true ,這裏只是提出來這個類有註冊的行爲。
(frameworks/base/services/java/com/android/server/net/NetworkStatsService.java)
public void systemReady() {
.........
// watch for networkType changes that aren't broadcast through
// CONNECTIVITY_ACTION_IMMEDIATE above.
// publicstaticfinalbooleanCOMBINE_SUBTYPE_ENABLED= true;
if(!COMBINE_SUBTYPE_ENABLED ) {
mTeleManager.listen(mPhoneListener ,LISTEN_DATA_CONNECTION_STATE);
}
registerPollAlarmLocked();
registerGlobalAlert();
}
(17).在系統內置的應用程序Settings中也調用了 TelephonyManager的listen 方法進行了 PhoneStateListener. LISTEN_DATA_CONNECTION_STATE註冊( 可能還有其他應用程序進行相同操作,但目前只發現 settings,內置程序phne中 /src/com/android/phone/CallNotifier.java進行了 PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR |PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR |PhoneStateListener.LISTEN_SERVICE_STATE單未進行 LISTEN_DATA_CONNECTION_STATE 的註冊)
(/packages/apps/Settings/src/com/android/settings/deviceinfo/Status.java)
(/packages/apps/Settings/src/com/android/settings/RadioInfo.java)
先來看下 Status.java
protected void onResume() {
super.onResume();
........
mTelephonyManager.listen(mPhoneStateListener ,
PhoneStateListener. LISTEN_DATA_CONNECTION_STATE);
mTelephonyManager.listen(mSignalStrengthListener ,
PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
mTelephonyManager.listen(mPhoneServiceListener ,
PhoneStateListener.LISTEN_SERVICE_STATE);
.......
}
再來看 RadioInfo.java
protected void onResume() {
.......
mPhoneStateReceiver.registerIntent();
mTelephonyManager.listen(mPhoneStateListener ,
PhoneStateListener. LISTEN_DATA_CONNECTION_STATE
| PhoneStateListener.LISTEN_DATA_ACTIVITY
| PhoneStateListener.LISTEN_CELL_LOCATION
| PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
| PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
| PhoneStateListener.LISTEN_CELL_INFO);
}
(18.)接下來看下TelephonyManager的 listen方法實現( /frameworks/base/telephony/java/android/telephony/TelephonyManager.java ):根據相應的PhoneStateListener類型,在 TelephonyRegistry.java實現相應的listen 方法。
public void listen (PhoneStateListener listener, intevents) {
//MTK-START [mtk04070][111116][ALPS00093395]Support Gemini
String pkgForDebug = sContext!=null ?sContext.getPackageName() : "<unknown>";
try{
// getITelephony()返回ITelephony (frameworks/base/telephony/java/com/android/internal/telephony/ITelephony.aidl)
// 由應用程序 Phone中的PhoneInterfaceManager 繼承ITelephony.Stub
Boolean notifyNow = ( getITelephony() !=null);
sRegistry.listen(pkgForDebug, listener.callback , events, notifyNow);
/* Add by mtk01411 for Data related event */
if(FeatureOption.MTK_GEMINI_SUPPORT) {
if(events == PhoneStateListener.LISTEN_NONE) {
/* Unregister previous listened events */
mRegistry2.listen(pkgForDebug, listener.callback , events, notifyNow);
} elseif(events == PhoneStateListener.LISTEN_CALL_STATE) {
mRegistry2.listen(pkgForDebug, listener.callback , events, notifyNow);
} else{
intdata_events = PhoneStateListener.LISTEN_NONE;
if((events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) !=0 ) {
data_events |= PhoneStateListener. LISTEN_DATA_CONNECTION_STATE ;
}
if((events & PhoneStateListener.LISTEN_DATA_ACTIVITY) !=0 ) {
data_events |= PhoneStateListener. LISTEN_DATA_ACTIVITY;
}
if(data_events != PhoneStateListener.LISTEN_NONE) {
/* For 3rd party application: */
/* This solution is only useful if only one PS can be attached for one of two sim cards */
/* If PS can be attached on both sim cards at the same time: This solution can't work */
/* => Because the same callback may recevie two same events and can't identify which event comes from which sim card */
mRegistry2.listen(pkgForDebug, listener.callback , data_events, notifyNow);
}
}
}
//MTK-END [mtk04070][111116][ALPS00093395]Support Gemini
} catch(RemoteException ex) {
// system process dead
ex.printStackTrace();
} catch(NullPointerException ex) {
// system process dead
ex.printStackTrace();
}
}
(19).回到TelephonyRegistry.java 來看看它的 listen方法實現
public void listen(String pkgForDebug, IPhoneStateListener callback, intevents,
booleannotifyNow) {
intcallerUid = UserHandle.getCallingUserId();// 多用戶的處理,獲取是調用的用戶 id
intmyUid = UserHandle.myUserId();
if(DBG ) {
Slog. d(TAG, "listen: E pkg="+ pkgForDebug +" events=0x" + Integer.toHexString (events)
+ " myUid="+ myUid
+ " callerUid="+ callerUid);
}
if(events != 0) {
/* Checks permission and throws Security exception */
checkListenerPermission(events);
synchronized(mRecords ) {
// register
Record r = null;
find_and_add: {
IBinder b = callback.asBinder();
finalintN =mRecords .size();
for(int i = 0; i < N; i++) {
r = mRecords.get(i);
if(b == r.binder ) {
breakfind_and_add;
}
}
r = newRecord();
r. binder= b;
r.callback= callback;
r. pkgForDebug= pkgForDebug;
r. callerUid= callerUid;
mRecords.add(r);//加入到了 mRecords 中,在 notifyDataConnection中for 循環中使用
if(DBG ) Slog.i (TAG ,"listen: add new record="+ r);
}
intsend = events & (events ^ r.events );
r. events= events;
if(notifyNow) {
if((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
try{
r.callback.onServiceStateChanged( newServiceState(mServiceState ));
} catch(RemoteException ex) {
remove(r. binder);
}
}
if((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH ) != 0) {
try{
intgsmSignalStrength =mSignalStrength .getGsmSignalStrength();
r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1
: gsmSignalStrength));
} catch(RemoteException ex) {
remove(r. binder);
}
}
if((events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) {
try{
r.callback.onMessageWaitingIndicatorChanged( mMessageWaiting);
} catch(RemoteException ex) {
remove(r. binder);
}
}
if((events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) {
try{
r.callback.onCallForwardingIndicatorChanged( mCallForwarding);
} catch(RemoteException ex) {
remove(r. binder);
}
}
if(validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION)) {
try{
r.callback.onCellLocationChanged( newBundle(mCellLocation ));
} catch(RemoteException ex) {
remove(r. binder);
}
}
if((events & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
try{
r.callback.onCallStateChanged( mCallState,mCallIncomingNumber );
} catch(RemoteException ex) {
remove(r. binder);
}
}
if((events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
try{
r.callback.onDataConnectionStateChanged( mDataConnectionState,
mDataConnectionNetworkType);
} catch(RemoteException ex) {
remove(r. binder);
}
}
if((events & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) {
try{
r.callback.onDataActivity( mDataActivity);
} catch(RemoteException ex) {
remove(r. binder);
}
}
if((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) {
try{
r.callback.onSignalStrengthsChanged( mSignalStrength);
} catch(RemoteException ex) {
remove(r. binder);
}
}
if((events & PhoneStateListener.LISTEN_OTASP_CHANGED) != 0) {
try{
r.callback.onOtaspChanged( mOtaspMode);
} catch(RemoteException ex) {
remove(r. binder);
}
}
if(validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO)) {
try{
if(DBG_LOC ) Slog.d (TAG ,"listen: mCellInfo="+ mCellInfo);
r.callback.onCellInfoChanged( mCellInfo);
} catch(RemoteException ex) {
remove(r. binder);
}
}
}
}
} else{
remove(callback.asBinder());
}
}
(20).當TelephonyRegistry 的notifyDataConnection()方法中觸發 onDataConnectionStateChanged()時,實際上將回調到以下地方:具體都是做一些更新的操作
a.在framework 中NetworkController.java
@Override
publicvoidonDataConnectionStateChanged (intstate,intnetworkType) {
if(DEBUG ) {
Slog. d(TAG, "onDataConnectionStateChanged : state=" + state
+ " type="+ networkType);
}
mDataState= state;
mDataNetType= networkType;
updateDataNetType();
updateDataIcon();// 更新數據圖標
refreshViews();// 刷新view
}
b.在framework 中NetworkStatsService.java
private PhoneStateListener mPhoneListener=new PhoneStateListener() {
@Override
publicvoidonDataConnectionStateChanged (intstate,intnetworkType) {
finalbooleanstateChanged = state !=mLastPhoneState ;
finalbooleannetworkTypeChanged = networkType !=mLastPhoneNetworkType;
if(networkTypeChanged && !stateChanged) {
// networkType changed without a state change, which means we
// need to roll our own update. delay long enough for
// ConnectivityManager to process.
//TODO: add direct event to ConnectivityService instead of
// relying on this delay.
if(LOGV ) Slog.v (TAG ,"triggering delayed updateIfaces()");
mHandler.sendMessageDelayed(
mHandler.obtainMessage(MSG_UPDATE_IFACES), SECOND_IN_MILLIS);//發消息進行更新
}
mLastPhoneState= state;
mLastPhoneNetworkType= networkType;
}
};
c.在應用程序Setting中 Status.java中:
private PhoneStateListener mPhoneStateListener= newPhoneStateListener() {
@Override
publicvoidonDataConnectionStateChanged (int state) {
updateDataState();//
updateNetworkType();
}
}
d.在應用程序Setting中 RadioInfo.java中:
public void onDataConnectionStateChanged(intstate) {
updateDataState();
updateDataStats();
updatePdpList();
updateNetworkType();
}
(21).TelephonyRegistry的 notifyDataConnection()方法最後調用 broadcastDataConnectionStateChanged ()方法發送數據連接變化的廣播.這裏將當前的狀態, apn類型,連接參數等都封裝在了 action爲ACTION_ANY_DATA_CONNECTION_STATE_CHANGED_MOBILE的intent 中,通過intent將這些內容發送出去。之後 frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/toolbar/QuickSettingsConnectionModel.java 和/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/toolbar/SimSwitchPanel.java 將接收到該廣播並進行處理。
private void broadcastDataConnectionStateChanged( intstate,
booleanisDataConnectivityPossible,
String reason, String apn, String apnType, LinkProperties linkProperties,
LinkCapabilities linkCapabilities, booleanroaming) {
// Note: not reporting to the battery stats service here, because the
// status bar takes care of that after taking into account all of the
// required info.
Intent intent =newIntent(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED_MOBILE);
intent.putExtra(PhoneConstants. STATE_KEY, DefaultPhoneNotifier.convertDataState(state).toString());
if(!isDataConnectivityPossible) {
intent.putExtra(PhoneConstants. NETWORK_UNAVAILABLE_KEY,true );
}
if(reason !=null) {
intent.putExtra(PhoneConstants. STATE_CHANGE_REASON_KEY, reason);
}
if(linkProperties !=null) {
intent.putExtra(PhoneConstants. DATA_LINK_PROPERTIES_KEY, linkProperties);
String iface = linkProperties.getInterfaceName();
if(iface !=null) {
intent.putExtra(PhoneConstants. DATA_IFACE_NAME_KEY, iface);
}
}
if(linkCapabilities !=null) {
intent.putExtra(PhoneConstants. DATA_LINK_CAPABILITIES_KEY , linkCapabilities);
}
if(roaming) intent.putExtra(PhoneConstants.DATA_NETWORK_ROAMING_KEY, true);
intent.putExtra(PhoneConstants. DATA_APN_KEY, apn);
intent.putExtra(PhoneConstants. DATA_APN_TYPE_KEY, apnType);
if(FeatureOption.MTK_GEMINI_SUPPORT) {
intent.putExtra(PhoneConstants. GEMINI_SIM_ID_KEY,mySimId );
}
// To make sure MobileDataStateTracker can receive this intent firstly,
// using oredered intent ACTION_ANY_DATA_CONNECTION_STATE_CHANGED_MOBILE
// redirect original intent. -->
mContext.sendOrderedBroadcast(intent,null,mDataConnChangeReceiver, null, 0,null,null);
// <--
}
(21).在廣播ACTION_ANY_DATA_CONNECTION_STATE_CHANGED_MOBILE發送之後,將被 MobileDataStateTracker中的內部類MobileDataStateReceiver和 TelephonyRegistry的mDataConnChangeReceiver 接收。TelephonyRegistry中將接收到 intent又使用action :TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED發送出去。
而在 MobileDataStateReceiver中處理則比較複雜,這裏就沒有不再進行細緻分析了。先根據 intent中的apntype 從全局的trakerMap中取出相應的 tracker 實例,再根據PhoneConstants. STATE_KEY 取出 state值,並由此進行分支處理。在 CONNECTED 分支中, tracker調用setDetailedState() 修改apn對應的 networkinfo中的狀態信息,併發送EVENT_STATE_CHANGED消息 ,ConnectionService的內部類NetworkStateTrackerHandler 將接收該消息並進行處理。
@Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED_MOBILE)) {
String apnType = intent.getStringExtra(PhoneConstants. DATA_APN_TYPE_KEY);
MobileDataStateTracker tracker = mTrackerMap.get(apnType);
if(tracker ==null) {
return;
}
......
// 子類型改變了,發出相應的消息
tracker. mNetworkInfo.setSubtype(newSubType, subTypeName);
if(newSubType != oldSubtype && tracker.mNetworkInfo.isConnected()) {
Message msg = tracker.mTarget.obtainMessage(EVENT_NETWORK_SUBTYPE_CHANGED,
oldSubtype, 0, tracker.mNetworkInfo );
msg.sendToTarget();
}
// 根據intent中傳遞過來的字符串 state值再次轉換爲 PhoneConstants.DataState ,並根據 state進行分支處理
PhoneConstants.DataState state = Enum. valueOf(PhoneConstants.DataState.class,
intent.getStringExtra(PhoneConstants. STATE_KEY));
String reason = intent.getStringExtra(PhoneConstants. STATE_CHANGE_REASON_KEY);
String apnName = intent.getStringExtra(PhoneConstants. DATA_APN_KEY);
tracker.mNetworkInfo .setRoaming(intent.getBooleanExtra(PhoneConstants. DATA_NETWORK_ROAMING_KEY,false ));
if(VDBG ) {
tracker.log(tracker. mApnType+" setting isAvailable to " +
!intent.getBooleanExtra(PhoneConstants. NETWORK_UNAVAILABLE_KEY,false ));
}
tracker.mNetworkInfo .setIsAvailable(!intent.getBooleanExtra(PhoneConstants. NETWORK_UNAVAILABLE_KEY,false ));
if(tracker.mMobileDataState != state) {
tracker. mMobileDataState= state;
switch(state) {
caseDISCONNECTED:
if(tracker.isTeardownRequested()) {
tracker.setTeardownRequested( false);
}
if(FeatureOption.MTK_GEMINI_SUPPORT) {
tracker.setDetailedStateGemini(DetailedState. DISCONNECTED, reason, apnName, slot);
} else{
tracker.setDetailedState(DetailedState. DISCONNECTED, reason, apnName);
}
// can't do this here - ConnectivityService needs it to clear stuff
// it's ok though - just leave it to be refreshed next time
// we connect.
//if (DBG) log("clearing mInterfaceName for "+ mApnType +
// " as it DISCONNECTED");
//mInterfaceName = null;
break;
caseCONNECTING:
if(FeatureOption.MTK_GEMINI_SUPPORT) {
tracker.setDetailedStateGemini(DetailedState. CONNECTING, reason, apnName, slot);
} else{
tracker.setDetailedState(DetailedState. CONNECTING, reason, apnName);
}
break;
caseSUSPENDED:
if(FeatureOption.MTK_GEMINI_SUPPORT) {
tracker.setDetailedStateGemini(DetailedState. SUSPENDED, reason, apnName, slot);
} else{
tracker.setDetailedState(DetailedState. SUSPENDED, reason, apnName);
}
break;
caseCONNECTED:
tracker. mLinkProperties= intent.getParcelableExtra(
PhoneConstants. DATA_LINK_PROPERTIES_KEY);
if(tracker.mLinkProperties ==null) {
tracker.loge( "CONNECTED event did not supply link properties." );
tracker. mLinkProperties=new LinkProperties();
}
tracker. mLinkCapabilities= intent.getParcelableExtra(
PhoneConstants. DATA_LINK_CAPABILITIES_KEY );
if(tracker.mLinkCapabilities ==null) {
tracker.loge( "CONNECTED event did not supply link capabilities." );
tracker. mLinkCapabilities=new LinkCapabilities();
}
if(FeatureOption.MTK_GEMINI_SUPPORT) {
tracker.setDetailedStateGemini(DetailedState. CONNECTED, reason, apnName, slot);
} else{
tracker.setDetailedState(DetailedState. CONNECTED, reason, apnName);
}
break;
}
}
.....
}
privatevoid setDetailedState(NetworkInfo.DetailedState state, String reason,
String extraInfo) {
if(state !=mNetworkInfo .getDetailedState()) {
booleanwasConnecting = (mNetworkInfo .getState() == NetworkInfo.State. CONNECTING);
String lastReason = mNetworkInfo.getReason();
/*
* If a reason was supplied when the CONNECTING state was entered, and no
* reason was supplied for entering the CONNECTED state, then retain the
* reason that was supplied when going to CONNECTING.
*/
if(wasConnecting && state == NetworkInfo.DetailedState.CONNECTED&& reason == null
&& lastReason != null)
reason = lastReason;
mNetworkInfo.setDetailedState(state, reason, extraInfo);//修改networkInfo 中的狀態信息
Message msg =mTarget.obtainMessage(EVENT_STATE_CHANGED, newNetworkInfo(mNetworkInfo ));//給ConnectionService 中NetworkStateTrackerHandler發消息
msg.sendToTarget();
} elseif(reason !=null&& (reason.equals(PhoneConstants. REASON_NO_SUCH_PDP) || reason.equals(Phone.REASON_APN_FAILED )) && state == DetailedState. DISCONNECTED){
mNetworkInfo.setDetailedState(state, reason, extraInfo);
Message msg = mTarget.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
msg.sendToTarget();
}
}
(22).回到ConnectionService.java 中來,這裏果然是連接管理的最核心啊,也是反饋的終點了。
private class NetworkStateTrackerHandler extendsHandler {
.....
@Override
publicvoidhandleMessage(Message msg) {
NetworkInfo info;
switch(msg.what ) {
caseNetworkStateTracker.EVENT_STATE_CHANGED:
info = (NetworkInfo) msg. obj;
inttype = info.getType();
NetworkInfo.State state = info.getState();
if(VDBG || (state == NetworkInfo.State.CONNECTED) ||
(state == NetworkInfo.State. DISCONNECTED)) {
log("ConnectivityChange for "+
info.getTypeName() + ": "+
state + "/"+ info.getDetailedState());
}
EventLogTags.writeConnectivityStateChanged(
info.getType(), info.getSubtype(), info.getDetailedState().ordinal());
if(info.getDetailedState() ==
NetworkInfo.DetailedState. FAILED) {
handleConnectionFailure(info);
} elseif(info.getDetailedState() ==
DetailedState. CAPTIVE_PORTAL_CHECK) {
handleCaptivePortalTrackerCheck(info);
} elseif(state == NetworkInfo.State.DISCONNECTED) {
handleDisconnect(info);
} elseif(state == NetworkInfo.State.SUSPENDED) {
//TODO: need to think this over.
// the logic here is, handle SUSPENDED the same as
// DISCONNECTED. The only difference being we are
// broadcasting an intent with NetworkInfo that's
// suspended. This allows the applications an
// opportunity to handle DISCONNECTED and SUSPENDED
// differently, or not.
Slog. d(TAG, "Change to Suspend_State due to reason=" + info.getReason() +" with network="+ info.getSubtypeName()) ;
if(((info.getReason() !=null) && info.getReason().equals(Phone.REASON_VOICE_CALL_STARTED ))
&& (info.getSubtype() == TelephonyManager. NETWORK_TYPE_GPRS
|| info.getSubtype() == TelephonyManager.NETWORK_TYPE_EDGE
|| info.getSubtype() == TelephonyManager.NETWORK_TYPE_UNKNOWN)) {
Xlog.e( MTK_TAG,"Suspend PS TX/RX Temporarily without deactivating PDP context");
sendSuspendedBroadcast(info);// 手機2G網絡掛起
} else{
Xlog.e( MTK_TAG,"Switch to Suspend:invoke handleDisconnect()" );
handleDisconnect(info);// 做斷開處理
}
} elseif(state == NetworkInfo.State.CONNECTED) {
handleConnect(info);// 做連接處理
}
if(mLockdownTracker !=null) {
mLockdownTracker.onNetworkInfoChanged(info);
}
break;
.......
}
}
}
(23.)最後一步操作了,根據NetworkInfo對應狀態變化執行 handleConnect(進行Connected 處理)或者 handleDisconnect(Disconnected)處理。併發出 CONNECTIVITY_ACTION 的廣播,該廣播是一個公共廣播,應用程序或者框架中都可以進行廣播的接收。消息傳遞給應用程序,知曉了網絡的連接狀況已經改變,並且可以獲取到具體當前的網絡狀態
private void handleDisconnect(NetworkInfo info) {
………
/*
* If the disconnected network is not the active one, then don't report
* this as a loss of connectivity. What probably happened is that we're
* getting the disconnect for a network that we explicitly disabled
* in accordance with network preference policies.
*/
if(!mNetConfigs [prevNetType].isDefault()) {
List pids = mNetRequestersPids[prevNetType];
for(int i = 0; i<pids.size(); i++) {
Integer pid = (Integer)pids.get(i);
// will remove them because the net's no longer connected
// need to do this now as only now do we know the pids and
// can properly null things that are no longer referenced.
reassessPidDns(pid.intValue(), false);
}
}
Intent intent =new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
intent.putExtra(ConnectivityManager. EXTRA_NETWORK_INFO, new NetworkInfo(info));
intent.putExtra(ConnectivityManager. EXTRA_NETWORK_TYPE, info.getType());
if(info.isFailover()) {
intent.putExtra(ConnectivityManager. EXTRA_IS_FAILOVER,true );
info.setFailover( false);
}
if(info.getReason() !=null) {
intent.putExtra(ConnectivityManager. EXTRA_REASON, info.getReason());
}
if(info.getExtraInfo() !=null) {
intent.putExtra(ConnectivityManager. EXTRA_EXTRA_INFO,
info.getExtraInfo());
}
if(mNetConfigs [prevNetType].isDefault()) {
/** M: Support CMCC Wi-Fi to Mobile @{ */
booleanmobileData = getMobileDataEnabled();
log("mobileData="+ mobileData + ", prevNetType="+ prevNetType +",mActiveDefaultNetwork "+mActiveDefaultNetwork);
if(mIcsExt ==null){
log("Null in mIcsExt");
return;
}
if(mIcsExt .isDefaultFailover(prevNetType,mActiveDefaultNetwork)){
isFailover = tryFailover(prevNetType);
if(mActiveDefaultNetwork != -1) {
NetworkInfo switchTo = mNetTrackers[mActiveDefaultNetwork ].getNetworkInfo();
intent.putExtra(ConnectivityManager. EXTRA_OTHER_NETWORK_INFO, switchTo);
} else{
mDefaultInetConditionPublished= 0;// we're not connected anymore
intent.putExtra(ConnectivityManager. EXTRA_NO_CONNECTIVITY,true );
}
} else{
mDefaultInetConditionPublished= 0;// we're not connected anymore
intent.putExtra(ConnectivityManager. EXTRA_NO_CONNECTIVITY,true );
mActiveDefaultNetwork= -1;
if(!mIcsExt .isUserPrompt()) {
isFailover = tryFailover(prevNetType);
if(mActiveDefaultNetwork != -1) {
NetworkInfo switchTo = mNetTrackers[mActiveDefaultNetwork ].getNetworkInfo();
intent.putExtra(ConnectivityManager. EXTRA_OTHER_NETWORK_INFO, switchTo);
} else{
mDefaultInetConditionPublished= 0;// we're not connected anymore
intent.putExtra(ConnectivityManager. EXTRA_NO_CONNECTIVITY,true );
}
}
}
/** M: @} */
}
intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION, mDefaultInetConditionPublished);
if(FeatureOption.MTK_GEMINI_SUPPORT) {
if(ConnectivityManager.isRadioNumValid(simId)) {
intent.putExtra(ConnectivityManager. EXTRA_SIM_ID,simId);
}
}
// Reset interface if no other connections are using the same interface
booleandoReset =true;
LinkProperties linkProperties = mNetTrackers[prevNetType].getLinkProperties();
if(linkProperties !=null) {
String oldIface = linkProperties.getInterfaceName();
if(TextUtils.isEmpty(oldIface) == false) {
for(NetworkStateTracker networkStateTracker :mNetTrackers) {
if(networkStateTracker ==null)continue;
NetworkInfo networkInfo = networkStateTracker.getNetworkInfo();
if(networkInfo.isConnected() && networkInfo.getType() != prevNetType) {
LinkProperties l = networkStateTracker.getLinkProperties();
if(l ==null)continue;
if(oldIface.equals(l.getInterfaceName())) {
doReset = false;
break;
}
}
}
}
}
// do this before we broadcast the change
handleConnectivityChange(prevNetType, doReset);
finalIntent immediateIntent =newIntent(intent);
immediateIntent.setAction( CONNECTIVITY_ACTION_IMMEDIATE );
sendStickyBroadcast(immediateIntent);
sendStickyBroadcastDelayed(intent, getConnectivityChangeDelay());
/*
* If the failover network is already connected, then immediately send
* out a followup broadcast indicating successful failover
*/
if(mActiveDefaultNetwork != -1) {
sendConnectedBroadcastDelayed( mNetTrackers[mActiveDefaultNetwork ].getNetworkInfo(),
getConnectivityChangeDelay());
}
/* whenever any interface is down, try to restore default */
if(FeatureOption.MTK_DT_SUPPORT !=true)
{
if(!isFailover) {
tryRestoreDefault();
}
}
}
pivate void handleConnect(NetworkInfo info) {
finalintnewNetType = info.getType();
setupDataActivityTracking(newNetType);
// snapshot isFailover, because sendConnectedBroadcast() resets it
booleanisFailover = info.isFailover();
finalNetworkStateTracker thisNet =mNetTrackers [newNetType];
finalString thisIface = thisNet.getLinkProperties().getInterfaceName();
// if this is a default net and other default is running
// kill the one not preferred
if(mNetConfigs [newNetType].isDefault()) {//是否是默認網絡
if(mActiveDefaultNetwork != -1 &&mActiveDefaultNetwork!= newNetType) {
if(isNewNetTypePreferredOverCurrentNetType(newNetType)) {//網絡優先級判斷,wifi開啓的情況下,數據網絡會在這裏通過 tearDown關掉
// tear down the other
NetworkStateTracker otherNet =
mNetTrackers[mActiveDefaultNetwork ];
if(DBG ) {
log("Policy requires "+ otherNet.getNetworkInfo().getTypeName() +
" teardown");
}
if(!teardown(otherNet)) {
loge("Network declined teardown request");
//MTK mark
Xlog.e(MTK_TAG, "Since we may teardown it by other way, just go on" );
//teardown (thisNet);
//return;
}
} else{
// don't accept this one
if(VDBG ) {
log("Not broadcasting CONNECT_ACTION "+
"to torn down network " + info.getTypeName());
}
teardown(thisNet);
return;
}
}
synchronized(ConnectivityService.this) {
// have a new default network, release the transition wakelock in a second
// if it's held. The second pause is to allow apps to reconnect over the
// new network
if(mNetTransitionWakeLock .isHeld()) {
mHandler.sendMessageDelayed(mHandler .obtainMessage(
EVENT_CLEAR_NET_TRANSITION_WAKELOCK ,
mNetTransitionWakeLockSerialNumber , 0),
1000);
}
}
mActiveDefaultNetwork= newNetType;
// this will cause us to come up initially as unconnected and switching
// to connected after our normal pause unless somebody reports us as reall
// disconnected
mDefaultInetConditionPublished= 0;
mDefaultConnectionSequence++;
mInetConditionChangeInFlight=false ;
// Don't do this - if we never sign in stay, grey
//reportNetworkCondition(mActiveDefaultNetwork, 100);
}
thisNet.setTeardownRequested( false);
updateNetworkSettings(thisNet);
handleConnectivityChange(newNetType, false);
sendConnectedBroadcastDelayed(info, getConnectivityChangeDelay());
// notify battery stats service about this network
finalString iface = thisNet.getLinkProperties().getInterfaceName();
if(iface !=null) {
try{
BatteryStatsService. getService().noteNetworkInterfaceType(iface, newNetType);
} catch(RemoteException e) {
// ignored; service lives in system_server
}
}
}
--------------------------------------------------------------
private void sendConnectedBroadcastDelayed(NetworkInfo info, intdelayMs) {
sendGeneralBroadcast(info, CONNECTIVITY_ACTION_IMMEDIATE );
sendGeneralBroadcastDelayed(info, CONNECTIVITY_ACTION, delayMs);
}