Framework中的Ril源码分析
来源:互联网 发布:sql嵌套循环语句 编辑:程序博客网 时间:2024/05/17 09:04
一、概述
RILJ有两个主要特点:
1、开启了两个独立线程分别负责发送数据和接收数据
2、数据的发送和接收是异步的
第一个特点很容易理解,而对于第二个特点有衍生出来了另一个问题:既然是异步的消息机制,也就是说发送者在发送完数据后就可以返回,那么当得到回应后,就要有一种方法去找到当初的请求者,并把结果返回给他。那么这个方法是什么呢?
这个方法就是令牌系统,我们将在第二节介绍。
有了以上简要的认识,我们从RILJ的入口开始展开我们的分析,也就是RILJ的构造函数:
- @RIL.java
- public RIL(Context context, int preferredNetworkType, int cdmaSubscription) {
- super(context);
- //发送子线程
- mSenderThread = new HandlerThread("RILSender");
- mSenderThread.start();
- Looper looper = mSenderThread.getLooper();
- //mSender是发送子线程的Handler,通过他可以发送数据
- mSender = new RILSender(looper);
- //接收子线程
- mReceiver = new RILReceiver();
- mReceiverThread = new Thread(mReceiver, "RILReceiver");
- mReceiverThread.start();
- }
下面我们分析两个子线程的处理流程。而在介绍他们之前,我们先来介绍以下在RIL层中传递的消息的格式:RILRequest。
二、RILRequest
首先来看他的属性:class RILRequest {},说明这是一个独立的类,没有继承任何的父类或接口。他也在RIL.java中,同时也是RIL.java中除了RIL外唯一的类。
我们再来看一下RILRequest的构成:
- class RILRequest {
- //令牌
- int mSerial;
- int mRequest;
- Message mResult;
- Parcel mp;
- RILRequest mNext;
- //生成一个RILRequest的消息对象
- static RILRequest obtain(int request, Message result) {
- }
- //释放资源
- void release() {
- }
- //构造函数,内容为空
- private RILRequest() {
- }
- //重置令牌
- static void resetSerial() {
- sNextSerial.set(sRandom.nextInt());
- }
- //用于调试
- String serialString() {
- }
- //异常处理
- void onError(int error, Object ret) {
- }
- }
mSerial:这个变量就是一个令牌,每生成(obtain)一个新的请求,都将产生一个递增的、唯一的mSerial,当从RILC得到一个数据后,我们将通过mSerial找到当初发送这个请求的对象。这也就是异步通信最关键的联系点。我们在RIL层谈到的令牌(token)就是这个东西。
mRequest:请求码,需要和RIL层中的ril_commands.h文件内定义的请求码一致。
mResult:生成当前请求的请求者。从RILC中得到数据后,需要把数据处理后返回给mResult指向的对象。
mp:附加数据。
mNext:链表结构,代表下一个RILRequest。
然后我们介绍RILRequest中主要的方法,先来看看obtain:
- static RILRequest obtain(int request, Message result) {
- RILRequest rr = null;
- synchronized(sPoolSync) {
- //释放的RILRequest可以循环利用
- if (sPool != null) {
- rr = sPool;
- sPool = rr.mNext;
- rr.mNext = null;
- sPoolSize--;
- }
- }
- if (rr == null) {
- rr = new RILRequest();
- }
- rr.mSerial = sNextSerial.getAndIncrement();
- rr.mRequest = request;
- rr.mResult = result;
- rr.mp = Parcel.obtain();
- rr.mp.writeInt(request);
- rr.mp.writeInt(rr.mSerial);
- return rr;
- }
然后在obtain中对RILRequest对象的mSerial进行初始化,前面说过,这个值是当前请求的令牌,是唯一的。他是通过getAndIncrement的方式生成的。这里补充以下AtomicInteger的知识:AtomicInteger是一个提供原子操作的Integer的类。在Java语言中,++i和i++操作并不是线程安全的,在使用的时候,不可避免的会用到synchronized关键字。而AtomicInteger则通过一种线程安全的加减操作接口。可以简单的理解为,getAndIncrement的方法可以得到一个绝对递增的整数。
接下来就是mResult的初始化,他的初始化过程很简单,就是简单的赋值。
然后是mp,他的初始化过程中把上面的request和mSerial都写了进去。
这就是生成RILRequest的过程。
接下来看看release方法:
- void release() {
- synchronized (sPoolSync) {
- if (sPoolSize < MAX_POOL_SIZE) {
- this.mNext = sPool;
- sPool = this;
- sPoolSize++;
- mResult = null;
- }
- }
- }
其他的方法比较简单,只做简要介绍:
resetSerial:重置mSerial
serialString:生成包含mSerial的字串,主要用于打印Log
onError:异常处理
至此,RILRequest的结构介绍完了,我们下面去分析发送和接收子线程的流程。
三、发送子线程
发送子线程需要关注两点:1、如何把数据发送到mSenderThread中;2、mSenderThread是如何把请求发送给RILC的。
3.1、如何把数据发送到mSenderThread
假设我们此时需要通过RIL发送短消息,那么我们就可以通过mSender对象把数据发送到mSenderThread线程中:
- public void sendSMS (String smscPDU, String pdu, Message result) {
- //构建一个请求
- RILRequest rr = RILRequest.obtain(RIL_REQUEST_SEND_SMS, result);
- rr.mp.writeInt(2);
- rr.mp.writeString(smscPDU);
- rr.mp.writeString(pdu);
- //发送
- send(rr);
- }
- private void send(RILRequest rr) {
- Message msg;
- //如果Socket通道还没有打开
- if (mSocket == null) {
- rr.onError(RADIO_NOT_AVAILABLE, null);
- rr.release();
- return;
- }
- //生成mSender的消息
- msg = mSender.obtainMessage(EVENT_SEND, rr);
- acquireWakeLock();
- //发送给子线程
- msg.sendToTarget();
- }
上面的步骤证实了我们确实是通过mSender对象生成(obtainMessage)了一个EVENT_SEND的消息,同时把请求包装为RILRequest对象,连同EVENT_SEND消息一同发送(sendToTarget)给了子线程。
3.2、mSenderThread子线程把请求发送给RILC
- public void handleMessage(Message msg) {
- RILRequest rr = (RILRequest)(msg.obj);
- RILRequest req = null;
- switch (msg.what) {
- case EVENT_SEND:
- try {
- LocalSocket s;
- //得到Socket通道
- s = mSocket;
- synchronized (mRequestList) {
- mRequestList.append(rr.mSerial, rr);
- }
- byte[] data;
- data = rr.mp.marshall();
- rr.mp.recycle();
- rr.mp = null;
- dataLength[0] = dataLength[1] = 0;
- dataLength[2] = (byte)((data.length >> 8) & 0xff);
- dataLength[3] = (byte)((data.length) & 0xff);
- //通过Socket通道发送数据
- s.getOutputStream().write(dataLength);
- s.getOutputStream().write(data);
- } catch (IOException ex) {
- } catch (RuntimeException exc) {
- }
- break;
- case EVENT_WAKE_LOCK_TIMEOUT:
- break;
- }
- }
发送的过程比较直观,就是通过Socket通道把数据长度(dataLength)和数据(data)发送出去。
四、接收子线程
接收子线程要完成的就是对接收数据的处理操作。我们还分为两步去分析:1、如何接收的消息;2、消息的处理流程。
4.1、接收数据的过程
- public void run() {
- for (;;) {
- //开启Socket通道
- s = new LocalSocket();
- l = new LocalSocketAddress(SOCKET_NAME_RIL, LocalSocketAddress.Namespace.RESERVED);
- s.connect(l);
- mSocket = s;
- 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();
- }
- //关闭Socket,然后重新打开
- mSocket.close();
- mSocket = null;
- //清除令牌
- RILRequest.resetSerial();
- //清除所有请求
- clearRequestList(RADIO_NOT_AVAILABLE, false);
- }
- //通知ril连接状态改变
- notifyRegistrantsRilConnectionChanged(-1);
- }
我们现在看一下readRilMessage的过程,至于处理的流程放到下一节介绍。
- private static int readRilMessage(InputStream is, byte[] buffer) throws IOException {
- //先读取数据的长度
- do {
- //从InputStream中读取数据
- countRead = is.read(buffer, offset, remaining);
- if (countRead < 0 ) {
- return -1;
- }
- offset += countRead;
- remaining -= countRead;
- } while (remaining > 0);
- //计算数据长度
- messageLength = ((buffer[0] & 0xff) << 24)
- | ((buffer[1] & 0xff) << 16)
- | ((buffer[2] & 0xff) << 8)
- | (buffer[3] & 0xff);
- remaining = messageLength;
- //再读取有效数据
- do {
- countRead = is.read(buffer, offset, remaining);
- if (countRead < 0 ) {
- return -1;
- }
- offset += countRead;
- remaining -= countRead;
- } while (remaining > 0);
- return messageLength;
- }
下面介绍数据的处理流程(processResponse)。
4.2、数据的处理流程
在《RIL层源码分析》文档中我们介绍过,RIL层收到的消息分为两部分:
一部分是Modem主动上报的消息,比如新短信的提醒、Modem状态的改变等,这类消息称为URC消息;
另一部分是由终端发送给Modem后,Modem给出的回应,属于非URC消息;
对于URC消息来说,只需要调用相应的通知机制即可;而对于非URC消息,我们还需要把相应的数据返回给当初发送请求的单位。
既然RIL层对消息的处理方式不同,那么对应的在RILJ中也要分开处理:- private void processResponse (Parcel p) {
- int type;
- //得到数据的类型,是URC消息还是非URC消息
- type = p.readInt();
- if (type == RESPONSE_UNSOLICITED) {
- //URC消息的处理
- processUnsolicited (p);
- } else if (type == RESPONSE_SOLICITED) {
- //非URC消息的处理
- RILRequest rr = processSolicited (p);
- if (rr != null) {
- rr.release();
- decrementWakeLock();
- }
- }
- }
上面看到,URC消息是通过processUnsolicited处理的,而非URC消息是由processSolicited处理的,我们分别介绍两种处理流程。
4.2.1、URC消息处理流程
- private void processUnsolicited (Parcel p) {
- int response;
- Object ret;
- //读取当前消息的消息码
- response = p.readInt();
- //先处理
- switch(response) {
- case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED: ret = responseVoid(p); break;
- case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: ret = responseVoid(p); break;
- case RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED: ret = responseVoid(p); break;
- case RIL_UNSOL_RESPONSE_NEW_SMS: ret = responseString(p); break;
- ........
- }
- //再次处理
- switch(response) {
- case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED:
- //Radio状态改变
- switchToRadioState(newState);
- break;
- case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED:
- //通知call状态改变
- mCallStateRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
- break;
- case RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED:
- //通知网络状态改变
- mVoiceNetworkStateRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
- break;
- case RIL_UNSOL_RESPONSE_NEW_SMS: {
- //新短信
- SmsMessage sms;
- sms = SmsMessage.newFromCMT(a);
- if (mGsmSmsRegistrant != null) {
- //发送短信通知
- mGsmSmsRegistrant.notifyRegistrant(new AsyncResult(null, sms, null));
- }
- break;
- }
- case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT:
- //短信报告
- mSmsStatusRegistrant.notifyRegistrant( new AsyncResult(null, ret, null));
- break;
- }
- }
- }
A、第一个switch:
根据不同的消息码对数据进行初步解析,得到上报的有效数据。
responseVoid:当前消息的附带数据为空。
responseString:当前消息的附带数据为String型,需要得到String。
responseInts:当前消息的附带数据为int型的数组,需要得到这个数组。
B、第二个switch
在这个switch语句中针对不同的消息码用刚刚得到的有效数据进行不同的处理,主要就是调用相应的管理者去做相应的通知。
4.2.2、非URC消息处理流程
- private RILRequest processSolicited (Parcel p) {
- RILRequest rr;
- //得到当前的RILRequest对象,然后从mRequestList中将其删除
- rr = findAndRemoveRequestFromList(serial);
- //得到RIL上报的数据
- switch (rr.mRequest) {
- case RIL_REQUEST_GET_SIM_STATUS: ret = responseIccCardStatus(p); break;
- case RIL_REQUEST_ENTER_SIM_PIN: ret = responseInts(p); break;
- case RIL_REQUEST_ENTER_SIM_PUK: ret = responseInts(p); break;
- case RIL_REQUEST_ENTER_SIM_PIN2: ret = responseInts(p); break;
- case RIL_REQUEST_ENTER_SIM_PUK2: ret = responseInts(p); break;
- case RIL_REQUEST_CHANGE_SIM_PIN: ret = responseInts(p); break;
- case RIL_REQUEST_CHANGE_SIM_PIN2: ret = responseInts(p); break;
- case RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION: ret = responseInts(p); break;
- case RIL_REQUEST_GET_CURRENT_CALLS: ret = responseCallList(p); break;
- case RIL_REQUEST_DIAL: ret = responseVoid(p); break;
- case RIL_REQUEST_GET_IMSI: ret = responseString(p); break;
- case RIL_REQUEST_HANGUP: ret = responseVoid(p); break;
- case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND: ret = responseVoid(p); break;
- ........
- default:
- throw new RuntimeException("Unrecognized solicited response: " + rr.mRequest);
- }
- //对PUK的两个消息需要单独的通知其管理者
- switch (rr.mRequest) {
- case RIL_REQUEST_ENTER_SIM_PUK:
- case RIL_REQUEST_ENTER_SIM_PUK2:
- if (mIccStatusChangedRegistrants != null) {
- mIccStatusChangedRegistrants.notifyRegistrants();
- }
- break;
- }
- //将返回值返回给当初的请求者,由请求者去决定如何处理数据
- if (rr.mResult != null) {
- AsyncResult.forMessage(rr.mResult, ret, null);
- rr.mResult.sendToTarget();
- }
- return rr;
- }
以上就是Framework中的RILJ消息处理流程。
下面贴出流程图:
- Framework中的Ril源码分析
- Framework中的Ril源码分析
- Framework中的Ril源码分析(原)
- Framework中的Ril源码分析(原)
- Android RIL源码分析
- Android RIL源码分析
- RIL层源码分析
- RIL层源码分析
- RIL源码分析
- RIL层源码分析
- RIL 机制源码分析
- Android RIL Driver 源码分析
- Framework层Ril控制流程分析
- Framework层Ril控制流程分析
- Android源码分析:RIL代码分析
- Android RIL源码分析(1)
- Android RIL源码分析(2)
- Android RIL源码分析(3)
- linux环境下memcache的安装
- Error:A problem was found with the configuration of task ':app:packageDebug'. > File 'F:\androidstud
- Xstream解析:xml
- android开发去掉顶部标题栏的方法
- 机器学习框架简介
- Framework中的Ril源码分析
- hadoop视频教程搜索,拿走不谢!
- eclipse加速之禁用JS、jsp等文件的语法验证
- LeetCode OJ-18-4Sum
- Spark机器学习MLlib系列1(for python)--数据类型,向量,分布式矩阵,API
- MYSQL指南
- MFC实现贪吃蛇游戏之蛇的移动
- __declspec
- 【JavaScript】富文本编辑器UEditor与代码高亮插件SyntaxHighlighter整合,实现用户贴代码功能