【Android架構Telephony篇】資料業務(2)RILJ
Android: 6.0
Desktop: Ubuntu 15.04
更新日期:2017-04-28
http://blog.csdn.net/u013686019/article/details/49719897Telephony的Overview見:
一、Telephony整體流程
Telephony執行的完整流程如下:
下面自上而下,只關注主幹,分層看下程式碼走向。
二、Telephony資料業務的RILJ層
1、App層
使用者點選系統【設定】進行開啟/關閉資料業務,呼叫:
DataUsageSummary.java (packages\apps\settings\src\com\android\settings) private void setMobileDataEnabled(int subId, boolean enabled) { mTelephonyManager.setDataEnabled(subId, enabled); }
TelephonyManager作為"phone"系統服務的管理類,其獲取方式可以:
DataUsageSummary#onCreate() -->mTelephonyManager = TelephonyManager.from(context); TelephonyManager.java (frameworks\base\telephony\java\android\telephony) public static TelephonyManager from(Context context) { return (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); }
在上圖中,把"phone"系統服務獲取放在了frameworks層,這是因為在frameworks中可以越過Manager類直接獲取服務,如:
TelephonyManager.java (frameworks\base\telephony\java\android\telephony) private ITelephony getITelephony() { return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE)); } public void setDataEnabled(int subId, boolean enable) { ITelephony telephony = getITelephony(); telephony.setDataEnabled(subId, enable); }
Telephony提供的操作無線模組的方法可以通過ITelephony.aidl檔案檢視:
frameworks/base/telephony/java/com/android/internal/telephony/ITelephony.aidl
2、Framework層
(1)"phone"系統服務註冊
系統服務絕大部分都在frameworks/base/services/java/com/android/server/SystemServer.java檔案中統一註冊,但TELEPHONY_SERVICE("phone")服務很是另類,其註冊流程:
PhoneApp.java (packages\services\telephony\src\com\android\phone)
public class PhoneApp extends Application {}
PhoneApp#onCreate()
-->PhoneGlobals#onCreate()
---->PhoneInterfaceManager#init()
------>PhoneInterfaceManager#publish()
-------->PhoneInterfaceManager#ServiceManager.addService("phone", this);
adb shell進去系統,通過service命令可以檢視系統註冊的所有服務:
# service list
Found 102 services:
1 phone: [com.android.internal.telephony.ITelephony]
(2) 繼續資料業務流程
PhoneInterfaceManager是"phone"服務實現方,對於資料開啟/關閉:
PhoneInterfaceManager.java (packages\services\telephony\src\com\android\phone)
/**
* Set mobile data enabled
* Used by the user through settings etc to turn on/off mobile data
*/
@Override
public void setDataEnabled(int subId, boolean enable) {
enforceModifyPermission(); -->a
int phoneId = mSubscriptionController.getPhoneId(subId); -->b
Phone phone = PhoneFactory.getPhone(phoneId);
phone.setDataEnabled(enable);
}
這裡的Phone是一個interface。在【Android架構Telephony篇】資料業務(1)總覽提到,無線通訊有TD-LTE/FDD-LTE/TD-SCDMA/WCDMA/EVDO/CDMA1X/GSM等等很多的網路制式,不同模組可以支援其中的一種或多種。它們之間的區別最簡單的例子,比如常說的電信卡、聯通卡等等。從軟體層面進行抽象就是:
Phone的建立:
PhoneFactory.java (frameworks\opt\telephony\src\java\com\android\internal\telephony)
public static void makeDefaultPhone(Context context) {
PhoneBase phone = null;
int phoneType = TelephonyManager.getPhoneType(networkModes[i]);
if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
phone = new GSMPhone(context,
sCommandsInterfaces[i], sPhoneNotifier, i);
} else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
phone = new CDMALTEPhone(context,
sCommandsInterfaces[i], sPhoneNotifier, i);
}
}
這裡就根據type建立不同的Phone,我們以GSMPhone為例,setDataEnabled()呼叫的就是:
GSMPhone.java (frameworks\opt\telephony\src\java\com\android\internal\telephony\gsm)
public void setDataEnabled(boolean enable) {
mDcTracker.setDataEnabled(enable);
}
mDcTracker是一個Handler,
DcTrackerBase.java (frameworks\opt\telephony\src\java\com\android\internal\telephony\dataconnection)
public void setDataEnabled(boolean enable) {
Message msg = obtainMessage(DctConstants.CMD_SET_USER_DATA_ENABLE);
msg.arg1 = enable ? 1 : 0;
sendMessage(msg);
}
public void handleMessage(Message msg) {
switch (msg.what) {
case DctConstants.CMD_SET_USER_DATA_ENABLE: {
final boolean enabled = (msg.arg1 == DctConstants.ENABLED) ? true : false;
onSetUserDataEnabled(enabled);
break;
}
}
DcTrackerBase#onSetUserDataEnabled()
-->DcTrackerBase#onTrySetupData()
DcTrackerBase傳送CMD_SET_USER_DATA_ENABLE資訊,並自己處理,最終呼叫到onTrySetupData(),實現onTrySetupData()的地方:
DcTracker.java (opt\telephony\src\java\com\android\internal\telephony\dataconnection)
protected boolean onTrySetupData(String reason) {
setupDataOnConnectableApns(reason);
return true;
}
setupDataOnConnectableApns()顧名思義就是使用可用的APN建立資料連線,之後:
DcTracker.java (opt\telephony\src\java\com\android\internal\telephony\dataconnection)
setupDataOnConnectableApns(reason, RetryFailures.ALWAYS);
-->trySetupData(apnContext, waitingApns);
---->setupData(apnContext, radioTech);
private boolean setupData(ApnContext apnContext, int radioTech) {
// 用於連線DcTracker和DataConnection
DcAsyncChannel dcac = null;
if (dcac == null) {
// 獲取/建立DcAsyncChannel物件
}
// 更新apnContext
apnContext.setDataConnectionAc(dcac);
apnContext.setApnSetting(apnSetting);
apnContext.setState(DctConstants.State.CONNECTING);
mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
// 撥號成功後傳送EVENT_DATA_SETUP_COMPLETE資訊
Message msg = obtainMessage();
msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
msg.obj = new Pair<ApnContext, Integer>(apnContext, generation);
dcac.bringUp(apnContext, getInitialMaxRetry(), profileId, radioTech,
mAutoAttachOnCreation.get(), msg, generation);
return true;
}
呼叫DcAsyncChannel的bringUp(),向DataConnection傳送建立連線訊息EVENT_CONNECT:
DcAsyncChannel.java (opt\telephony\src\java\com\android\internal\telephony\dataconnection)
/**
* 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.
*/
public void bringUp(ApnContext apnContext, int initialMaxRetry, int profileId,
int rilRadioTechnology, boolean retryWhenSSChange, Message onCompletedMsg,
int connectionGeneration) {
sendMessage(DataConnection.EVENT_CONNECT,
new ConnectionParams(apnContext, initialMaxRetry, profileId,
rilRadioTechnology, retryWhenSSChange, onCompletedMsg,
connectionGeneration));
}
DataConnection處理EVENT_CONNECT:
DataConnection.java (opt\telephony\src\java\com\android\internal\telephony\dataconnection)
private class DcInactiveState extends State { -->a
public boolean processMessage(Message msg) {
case EVENT_CONNECT:
ConnectionParams cp = (ConnectionParams) msg.obj;
if (initConnection(cp)) { -->b
onConnect(mConnectionParams); -->c
transitionTo(mActivatingState);
} else {
log("DcInactiveState: msg.what=EVENT_CONNECT initConnection failed");
notifyConnectCompleted(cp, DcFailCause.UNACCEPTABLE_NETWORK_PARAMETER,
false);
}
retVal = HANDLED;
break;
}
}
a, DataConnection是一個狀態機,其初始狀態:
private DcInactiveState mInactiveState = new DcInactiveState();
private DataConnection() {
setInitialState(mInactiveState);
}
所以這裡呼叫DcInactiveState類的processMessage()處理EVENT_CONNECT資訊。b,
檢測引數合法性
c, 呼叫onConnect()啟動連線建立
/**
* Begin setting up a data connection, calls setupDataCall
* and the ConnectionParams will be returned with the
* EVENT_SETUP_DATA_CONNECTION_DONE AsyncResul.userObj.
*/
private void onConnect(ConnectionParams cp) {
// msg.obj will be returned in AsyncResult.userObj;
Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
msg.obj = cp;
mPhone.mCi.setupDataCall(
Integer.toString(cp.mRilRat + 2),
Integer.toString(cp.mProfileId),
mApnSetting.apn, mApnSetting.user, mApnSetting.password,
Integer.toString(authType),
protocol, msg);
}
mCi是一個CommandsInterface,實現它的是RIL:
RIL.java (opt\telephony\src\java\com\android\internal\telephony)
public final class RIL extends BaseCommands implements CommandsInterface {
}
這裡,就來到了開篇流程圖中的“RILJ”,在繼續下去之前,稍微提下Android中本地socket通訊。
(3)socket程序間通訊
Linux中的socket除了可以用於不同機器之間的網路通訊,還可以用於同一臺機器的程序間通訊。以Telephony為例,RILJ的java程序和RILC的c守護程序之間就是通過"/dev/socket/rild"這個socket進行通訊的。
a, socket建立
RIL.java (opt\telephony\src\java\com\android\internal\telephony)
LocalSocket mSocket;
String rilSocket = "rild";
s = new LocalSocket();
l = new LocalSocketAddress(rilSocket, LocalSocketAddress.Namespace.RESERVED);
s.connect(l);
mSocket = s;
b, 資料傳送
RIL.java (opt\telephony\src\java\com\android\internal\telephony)
class RILSender extends Handler implements Runnable {
@Override public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_SEND:
try {
LocalSocket s;
s = mSocket;
synchronized (mRequestList) {
mRequestList.append(rr.mSerial, rr);
}
byte[] data;
data = rr.mParcel.marshall();
rr.mParcel.recycle();
rr.mParcel = null;
// parcel length in big endian
dataLength[0] = dataLength[1] = 0;
dataLength[2] = (byte)((data.length >> 8) & 0xff);
dataLength[3] = (byte)((data.length) & 0xff);
s.getOutputStream().write(dataLength);
s.getOutputStream().write(data);
}
}
}
c, 資料接收
RIL.java (opt\telephony\src\java\com\android\internal\telephony)
class RILReceiver implements Runnable {
try {
InputStream is = mSocket.getInputStream();
for (;;) {
Parcel p;
length = readRilMessage(is, buffer);
if (length < 0) {
// End-of-stream reached
break;
}
p = Parcel.obtain();
p.unmarshall(buffer, 0, length);
p.setDataPosition(0);
processResponse(p); // 資料處理
p.recycle();
}
}
}
有了這個知識,RILJ就容易理解了。
(4)RILJ
RIL.java (opt\telephony\src\java\com\android\internal\telephony)
@Override
public void setupDataCall(String radioTechnology, String profile, String apn,
String user, String password, String authType, String protocol,
Message result) {
RILRequest rr
= RILRequest.obtain(RIL_REQUEST_SETUP_DATA_CALL, result);
rr.mParcel.writeInt(7);
rr.mParcel.writeString(radioTechnology);
rr.mParcel.writeString(profile);
rr.mParcel.writeString(apn);
rr.mParcel.writeString(user);
rr.mParcel.writeString(password);
rr.mParcel.writeString(authType);
rr.mParcel.writeString(protocol);
send(rr);
}
private void send(RILRequest rr) {
Message msg;
if (mSocket == null) {
rr.onError(RADIO_NOT_AVAILABLE, null);
rr.release();
return;
}
msg = mSender.obtainMessage(EVENT_SEND, rr);
acquireWakeLock();
msg.sendToTarget();
}
無需解釋。