- Android: 6.0
- Desktop: Ubuntu 15.04
- 更新:2017-04-27
Android设备中完整的移动通讯系统构成:

(1)Hardware
这里提到的无线通信模组是一种支持TD-LTE/FDD-LTE/TD-SCDMA/WCDMA/EVDO/CDMA1X/GSM等等网络制式的通信模块,能够为用户提供高速的无线数据、互联网接入等业务,具备语音、分组数据、短信功能,彩信等功能。提供这些功能使用服务的就是联通/移动/电信等运营商,通过SIM卡注册到各自的网络中,付费使用。
(2)Kernel
内核中实现访问无线通信模组的数据通道,比如对于串口类模组就可以为/dev/ttyS0(/dev/ttyS1...),USB接口的为/dev/ttyUSB0(/dev/ttyUSB1...)等。
而要使用模组的网络功能,还必须实现网络功能的通信协议,比如常见的PPP(Point-to-Point Protocol):
- PPP is the protocol used for establishing internet links over dial-up modems, DSL connections, and many other types of point-to-point links. The
为直观故,我们看下通过PPP建立网络连接的log:
最后一行返回status为0,说明网络建立成功。
同时,它也显示了本地IP、远程IP、DNS等网络信息。在Android拨号过程中,用到的两个重要文件是/data/connect和/etc/ppp/ip-up。
这时可以adb shell进入系统,看下网络:
或:

(3)Framework
该层的Telephony子系统完成无线通信模块所有功能的具体实现,同时为应用开发人员提供使用接口。
(4)App
作为App开发人员,调用Telephony的API,实现具体功能的App工用户使用。
Android的Telephony子系统也是非常复杂的,涉及很多方面。我们以数据业务为分析入口,从上到下走一遍框图中的流程。
http://blog.youkuaiyun.com/u013686019/article/details/49719897
Telephony的Overview见:
【Android架构Telephony篇】数据业务(1)总览
一、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)
-
-
-
-
- @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) {
-
- DcAsyncChannel dcac = null;
-
- if (dcac == null) {
-
- }
-
-
- apnContext.setDataConnectionAc(dcac);
- apnContext.setApnSetting(apnSetting);
- apnContext.setState(DctConstants.State.CONNECTING);
- mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
-
-
- 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)
-
-
-
-
-
- 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)) {<span style="font-family:Courier New;"> --></span>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()启动连接建立
-
-
-
-
-
- private void onConnect(ConnectionParams cp) {
-
-
- 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;
-
-
- 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) {
-
- 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();
- }
无需解释。
【Android架构Telephony篇】数据业务(3)RILC
http://blog.youkuaiyun.com/u013686019/article/details/53580878
三、Telephony数据业务的RILC层
现在,建立移动数据业务的任务通过socket传递给C/C++的RIL进行处理了:
- hardware\ril\rild\rild.c:
- int main(int argc, char **argv)
- {
- char libPath[PROPERTY_VALUE_MAX];
-
-
-
-
- for (i = 1; i < argc ;) {
- if (0 == strcmp(argv[i], "-l") && (argc - i > 1)) {
- rilLibPath = argv[i + 1];
- i += 2;
- } else if (0 == strcmp(argv[i], "--")) {
- i++;
- hasLibArgs = 1;
- break;
- } else {
- usage(argv[0]);
- }
- }
-
-
- startmux(bpID);
-
-
- dlHandle = dlopen(rilLibPath, RTLD_NOW);
-
- if (dlHandle == NULL) {
- RLOGE("dlopen failed: %s", dlerror());
- exit(-1);
- }
-
-
- RIL_startEventLoop();
-
- rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init");
-
- if (rilInit == NULL) {
- RLOGE("RIL_Init not defined or exported in %s\n", rilLibPath);
- exit(-1);
- }
-
-
-
-
-
-
-
-
- funcs = rilInit(&s_rilEnv, argc, rilArgv);
-
- RIL_register(funcs);
- done:
- while(1) {
- sleep(0x00ffffff);
- }
- }
rild用来对RIL进行管理。对于不同的Modem,尤其不同无线通信技术(GSM、CDMA)之间,其差别是很大的;就算是同种技术的Modem,不同厂商也有区别。所以管理部分处理它们共通的业务逻辑,而把具体的实现交给xxxril.so。管理部分涉及文件:hardware\ril\libril\ril.cpp、hardware\ril\libril\ril_event.cpp,本文不对该部分进行分析。
/system/lib/libreference-ril.so库处理AT指令相关操作,/dev/mux2用作数据通道。我们知道,Modem和CPU之间是通过串口连接的,为了使AT指令和数据分开管理,系统对串口进行了复用。原理和实现参见:hardware/ril/gsm0710muxd/src/gsm0710muxd.c。
回到数据业务建立上面。在RIL的具体实现上,每一个操作,比如短信、电话、查询信号强度等都有独有的Request,且RILC/C++和RILJava定义一致。建立数据连接的Request是RIL_REQUEST_SETUP_DATA_CALL:
- frameworks\opt\telephony\src\java\com\android\internal\telephony\RIL.java
- RILRequest rr = RILRequest.obtain(RIL_REQUEST_SETUP_DATA_CALL, result);
响应该request的地方是:
- hardware\ril\reference-ril\reference-ril.c
-
-
-
- static void onRequest (int request, void *data, size_t datalen, RIL_Token t)
- {
- switch (request) {
- case RIL_REQUEST_SETUP_DATA_CALL:
- requestSetupDataCall(data, datalen, t);
- break;
- }
- }
-
- static void requestSetupDataCall(void *data, size_t datalen, RIL_Token t)
- {
- char value[PROPERTY_VALUE_MAX] = "";
- char dns1[64]="";
- char dns2[64]="";
- char *cmd;
- int err,i,fd;
-
- char *response[3] = { "ppp0", "IP", ip_address };
- RIL_Data_Call_Response_v6 response_v6;
- int mypppstatus;
- char strTemp[32];
- ATResponse *p_response = NULL;
- char *dialup = NULL;
- int cid = 1;
- int channel = 0;
- char dev_path[32];
- char gateway[PROPERTY_VALUE_MAX] = "";
-
- const char* radioTechnology = ((const char**)data)[0];
- const char* profile = ((const char**)data)[1];
- const char* apn = ((const char**)data)[2];
- const char* user = ((const char**)data)[3];
- const char* password = ((const char**)data)[4];
- const char* authType = ((const char**)data)[5];
- char *auth_option;
- memset(ip_address,0,32);
- LOGD("radioTechnology:%s", radioTechnology);
- LOGD("profile:%s", profile);
- LOGD("apn:%s", apn);
- LOGD("user:%s", user);
- LOGD("password:%s", password);
- LOGD("authType:%s", authType);
-
-
-
-
- asprintf(&cmd, "/etc/ppp/call-pppd "
- "\"%s 115200\" "
- "\"novj novjccomp noccp ipcp-accept-local ipcp-accept-remote\" "
- "\"%s\" \"%s\" "
- "\"%s\" \"%s\" &",
- s_pppChannel, user, password, conn_script, disconn_script);
- ret = system(cmd);
- RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
- at_response_free(p_response);
- }
据业务建立的方式,依据不同Modem而有所不同,对于PPP方式,Android下的一个实现是通过/etc/ppp/call-pppd脚本进行:
- /etc/ppp/call-pppd
-
-
-
-
-
- PPPD_EXIT=""
-
- /system/bin/setprop "net.gprs.ppp-exit" ""
-
- /system/bin/pppd $1 debug defaultroute noauth nodetach nocrtscts $2 noipdefault usepeerdns user "$3" password "$4" connect "$5" disconnect "$6"
-
- PPPD_EXIT=$?
-
-
- /system/bin/setprop "net.gprs.ppp-exit" "$PPPD_EXIT"
call-pppd脚本需要我们传进来6个参数,比如:
- asprintf(&cmd, "/etc/ppp/call-pppd "
- "\"%s 115200\" "
- "\"novj novjccomp noccp ipcp-accept-local ipcp-accept-remote\" "
- "\"%s\" \"%s\" "
- "\"%s\" \"%s\" &",
- s_pppChannel, user, password, conn_script, disconn_script);
- ret = system(cmd);
system(const char *command)函数用来执行call-pppd脚本,log:

至此,数据业务建立完成,通过ifconfig命令查看,生成由ppp设备节点。
转自 http://blog.youkuaiyun.com/u013686019/article/details/53580878