Framework中的Ril源码分析

来源:互联网 发布:sql嵌套循环语句 编辑:程序博客网 时间:2024/05/17 09:04
为了区别RIL层中的RIL.cpp,我们约定framework中的RIL.java文件描述为RILJ,而hardware中的RIL.cpp描述为RILC。

一、概述


        所有上层对RIL层的请求,最终都需要在RILJ中转换为RIL层可识别的命令,然后通过Socket通道传输下去;同时,RIL层数据的上报,也要通过RILJ进行解码,还原为上层容易识别的格式。因此,RILJ是framework与RIL层交互的通道。
        RILJ有两个主要特点:
        1、开启了两个独立线程分别负责发送数据和接收数据
        2、数据的发送和接收是异步的

        第一个特点很容易理解,而对于第二个特点有衍生出来了另一个问题:既然是异步的消息机制,也就是说发送者在发送完数据后就可以返回,那么当得到回应后,就要有一种方法去找到当初的请求者,并把结果返回给他。那么这个方法是什么呢?
        这个方法就是令牌系统,我们将在第二节介绍。
        有了以上简要的认识,我们从RILJ的入口开始展开我们的分析,也就是RILJ的构造函数:
[java] view plain copy
  1. @RIL.java  
  2. public RIL(Context context, int preferredNetworkType, int cdmaSubscription) {  
  3.     super(context);  
  4.     //发送子线程  
  5.     mSenderThread = new HandlerThread("RILSender");  
  6.     mSenderThread.start();  
  7.     Looper looper = mSenderThread.getLooper();  
  8.     //mSender是发送子线程的Handler,通过他可以发送数据  
  9.     mSender = new RILSender(looper);  
  10.   
  11.     //接收子线程  
  12.     mReceiver = new RILReceiver();  
  13.     mReceiverThread = new Thread(mReceiver, "RILReceiver");  
  14.     mReceiverThread.start();  
  15. }  
        可以看到,在构造函数中开启了两个独立的子线程:mSenderThread用于给RILC发送数据,而mReceiverThread用于接收RILC上报的数据

        下面我们分析两个子线程的处理流程。而在介绍他们之前,我们先来介绍以下在RIL层中传递的消息的格式:RILRequest。

二、RILRequest


        我们单独拿出一小节来介绍RILRequest对象,是因为里面包含了我们在文章开始地方介绍的“消息异步传输”的秘密。
        首先来看他的属性:class RILRequest {},说明这是一个独立的类,没有继承任何的父类或接口。他也在RIL.java中,同时也是RIL.java中除了RIL外唯一的类。
        我们再来看一下RILRequest的构成:
[java] view plain copy
  1. class RILRequest {  
  2.     //令牌  
  3.     int mSerial;  
  4.     int mRequest;  
  5.     Message mResult;  
  6.     Parcel mp;  
  7.     RILRequest mNext;  
  8.   
  9.     //生成一个RILRequest的消息对象  
  10.     static RILRequest obtain(int request, Message result) {  
  11.     }  
  12.     //释放资源  
  13.     void release() {  
  14.     }  
  15.     //构造函数,内容为空  
  16.     private RILRequest() {  
  17.     }  
  18.     //重置令牌  
  19.     static void resetSerial() {  
  20.         sNextSerial.set(sRandom.nextInt());  
  21.     }  
  22.     //用于调试  
  23.     String serialString() {  
  24.     }  
  25.     //异常处理  
  26.     void onError(int error, Object ret) {  
  27.     }  
  28. }  
        这个类并不复杂,主要的包括一些成员变量和两个重要的成员函数:用于生成消息对象的obtain方法和用于释放对象的release方法。我们先来介绍他的成员变量:
            mSerial:这个变量就是一个令牌,每生成(obtain)一个新的请求,都将产生一个递增的、唯一的mSerial,当从RILC得到一个数据后,我们将通过mSerial找到当初发送这个请求的对象。这也就是异步通信最关键的联系点。我们在RIL层谈到的令牌(token)就是这个东西。
            mRequest:请求码,需要和RIL层中的ril_commands.h文件内定义的请求码一致。
            mResult:生成当前请求的请求者。从RILC中得到数据后,需要把数据处理后返回给mResult指向的对象。
            mp:附加数据。
            mNext:链表结构,代表下一个RILRequest。
        然后我们介绍RILRequest中主要的方法,先来看看obtain:
[java] view plain copy
  1. static RILRequest obtain(int request, Message result) {  
  2.     RILRequest rr = null;  
  3.     synchronized(sPoolSync) {  
  4.         //释放的RILRequest可以循环利用  
  5.         if (sPool != null) {  
  6.             rr = sPool;  
  7.             sPool = rr.mNext;  
  8.             rr.mNext = null;  
  9.             sPoolSize--;  
  10.         }  
  11.     }  
  12.   
  13.     if (rr == null) {  
  14.         rr = new RILRequest();  
  15.     }  
  16.     rr.mSerial = sNextSerial.getAndIncrement();  
  17.     rr.mRequest = request;  
  18.     rr.mResult = result;  
  19.     rr.mp = Parcel.obtain();  
  20.     rr.mp.writeInt(request);  
  21.     rr.mp.writeInt(rr.mSerial);  
  22.     return rr;  
  23. }  
        在obtain过程中,涉及到了sPool的用法,这个对象代表了一个RILRequest对象的链表,作用就是对RILRequest的循环利用,具体原理就是:当一个RILRequest被销毁(release)的时候,可以把当前的RILRequest保存在sPool中,等下次需要申请RILRequest时就去sPool中取出用即可,而无需重复的申请(new)RILRequest。同时sPool的最大个数为4(MAX_POOL_SIZE)。
        然后在obtain中对RILRequest对象的mSerial进行初始化,前面说过,这个值是当前请求的令牌,是唯一的。他是通过getAndIncrement的方式生成的。这里补充以下AtomicInteger的知识:AtomicInteger是一个提供原子操作的Integer的类。在Java语言中,++i和i++操作并不是线程安全的,在使用的时候,不可避免的会用到synchronized关键字。而AtomicInteger则通过一种线程安全的加减操作接口。可以简单的理解为,getAndIncrement的方法可以得到一个绝对递增的整数。
        接下来就是mResult的初始化,他的初始化过程很简单,就是简单的赋值。
        然后是mp,他的初始化过程中把上面的request和mSerial都写了进去。
        这就是生成RILRequest的过程。
        接下来看看release方法:
[java] view plain copy
  1. void release() {  
  2.     synchronized (sPoolSync) {  
  3.         if (sPoolSize < MAX_POOL_SIZE) {  
  4.             this.mNext = sPool;  
  5.             sPool = this;  
  6.             sPoolSize++;  
  7.             mResult = null;  
  8.         }  
  9.     }  
  10. }  
        这个方法中就是把即将要释放的RILRequest存入sPool中循环利用。
        其他的方法比较简单,只做简要介绍:
        resetSerial:重置mSerial
        serialString:生成包含mSerial的字串,主要用于打印Log
        onError:异常处理

        至此,RILRequest的结构介绍完了,我们下面去分析发送和接收子线程的流程。

三、发送子线程


        发送子线程需要关注两点:1、如何把数据发送到mSenderThread中;2、mSenderThread是如何把请求发送给RILC的

3.1、如何把数据发送到mSenderThread    


        我们在创建mSenderThread线程的时候,先是通过getLooper得到子线程的Looper,然后用这个Looper去创建了Handler对象,因此得到的这个Handler对象就是子线程的Handler,也就是mSender对象。
        假设我们此时需要通过RIL发送短消息,那么我们就可以通过mSender对象把数据发送到mSenderThread线程中:
[java] view plain copy
  1. public void sendSMS (String smscPDU, String pdu, Message result) {  
  2.     //构建一个请求  
  3.     RILRequest rr = RILRequest.obtain(RIL_REQUEST_SEND_SMS, result);  
  4.     rr.mp.writeInt(2);  
  5.     rr.mp.writeString(smscPDU);  
  6.     rr.mp.writeString(pdu);  
  7.     //发送  
  8.     send(rr);  
  9. }  
        继续看send的过程:
[java] view plain copy
  1. private void send(RILRequest rr) {  
  2.     Message msg;  
  3.     //如果Socket通道还没有打开  
  4.     if (mSocket == null) {  
  5.         rr.onError(RADIO_NOT_AVAILABLE, null);  
  6.         rr.release();  
  7.         return;  
  8.     }  
  9.     //生成mSender的消息  
  10.     msg = mSender.obtainMessage(EVENT_SEND, rr);  
  11.     acquireWakeLock();  
  12.     //发送给子线程  
  13.     msg.sendToTarget();  
  14. }  

        上面的步骤证实了我们确实是通过mSender对象生成(obtainMessage)了一个EVENT_SEND的消息,同时把请求包装为RILRequest对象,连同EVENT_SEND消息一同发送(sendToTarget)给了子线程

3.2、mSenderThread子线程把请求发送给RILC


        我们直接来看mSenderThread子线程的Handler:
[java] view plain copy
  1. public void handleMessage(Message msg) {  
  2.     RILRequest rr = (RILRequest)(msg.obj);  
  3.     RILRequest req = null;  
  4.     switch (msg.what) {  
  5.         case EVENT_SEND:  
  6.             try {  
  7.                 LocalSocket s;  
  8.                 //得到Socket通道  
  9.                 s = mSocket;  
  10.                 synchronized (mRequestList) {  
  11.                     mRequestList.append(rr.mSerial, rr);  
  12.                 }  
  13.                 byte[] data;  
  14.                 data = rr.mp.marshall();  
  15.                 rr.mp.recycle();  
  16.                 rr.mp = null;  
  17.   
  18.                 dataLength[0] = dataLength[1] = 0;  
  19.                 dataLength[2] = (byte)((data.length >> 8) & 0xff);  
  20.                 dataLength[3] = (byte)((data.length) & 0xff);  
  21.                 //通过Socket通道发送数据  
  22.                 s.getOutputStream().write(dataLength);  
  23.                 s.getOutputStream().write(data);  
  24.             } catch (IOException ex) {  
  25.             } catch (RuntimeException exc) {  
  26.             }  
  27.             break;  
  28.         case EVENT_WAKE_LOCK_TIMEOUT:  
  29.             break;  
  30.     }  
  31. }  

        发送的过程比较直观,就是通过Socket通道把数据长度(dataLength)和数据(data)发送出去

四、接收子线程   


        接收子线程要完成的就是对接收数据的处理操作。我们还分为两步去分析:1、如何接收的消息;2、消息的处理流程

4.1、接收数据的过程    


        我们直接来看接收子线程的run方法:
[java] view plain copy
  1. public void run() {  
  2.     for (;;) {  
  3.         //开启Socket通道  
  4.         s = new LocalSocket();  
  5.         l = new LocalSocketAddress(SOCKET_NAME_RIL, LocalSocketAddress.Namespace.RESERVED);  
  6.         s.connect(l);  
  7.         mSocket = s;  
  8.         InputStream is = mSocket.getInputStream();  
  9.         //接收数据过程  
  10.         for (;;) {  
  11.             Parcel p;  
  12.             //从通道读取数据  
  13.             length = readRilMessage(is, buffer);  
  14.             if (length < 0) {  
  15.                 break;  
  16.             }  
  17.             p = Parcel.obtain();  
  18.             p.unmarshall(buffer, 0, length);  
  19.             p.setDataPosition(0);  
  20.             //处理数据  
  21.             processResponse(p);  
  22.             p.recycle();  
  23.         }  
  24.         //关闭Socket,然后重新打开  
  25.         mSocket.close();  
  26.         mSocket = null;  
  27.         //清除令牌  
  28.         RILRequest.resetSerial();  
  29.         //清除所有请求  
  30.         clearRequestList(RADIO_NOT_AVAILABLE, false);  
  31.     }  
  32.     //通知ril连接状态改变  
  33.     notifyRegistrantsRilConnectionChanged(-1);  
  34. }  
        从上面的过程中我们看到,我们是在接收的子线程中打开了Socket的通道,并且在通道内通过for死循环不断检测(readRilMessage)数据,然后通过processResponse去处理。如果读取的过程出现异常,将会关闭Socket通道,并且清除令牌和请求列表,重新打开Socket。

        我们现在看一下readRilMessage的过程,至于处理的流程放到下一节介绍。

[java] view plain copy
  1. private static int readRilMessage(InputStream is, byte[] buffer) throws IOException {  
  2.     //先读取数据的长度  
  3.     do {  
  4.         //从InputStream中读取数据  
  5.         countRead = is.read(buffer, offset, remaining);  
  6.         if (countRead < 0 ) {  
  7.             return -1;  
  8.         }  
  9.         offset += countRead;  
  10.         remaining -= countRead;  
  11.     } while (remaining > 0);  
  12.     //计算数据长度  
  13.     messageLength = ((buffer[0] & 0xff) << 24)  
  14.             | ((buffer[1] & 0xff) << 16)  
  15.             | ((buffer[2] & 0xff) << 8)  
  16.             | (buffer[3] & 0xff);  
  17.   
  18.     remaining = messageLength;  
  19.     //再读取有效数据  
  20.     do {  
  21.         countRead = is.read(buffer, offset, remaining);  
  22.         if (countRead < 0 ) {  
  23.             return -1;  
  24.         }  
  25.         offset += countRead;  
  26.         remaining -= countRead;  
  27.     } while (remaining > 0);  
  28.     return messageLength;  
  29. }  
        可以看出,返回的数据分为两部分,数据长度+数据内容,我们通过对InputStream的连续读取得到了完整的数据,并把数据的长度返回出来,而数据的内容通过buffer带出来。

        下面介绍数据的处理流程(processResponse)。

4.2、数据的处理流程


        在《RIL层源码分析》文档中我们介绍过,RIL层收到的消息分为两部分:

        一部分是Modem主动上报的消息,比如新短信的提醒、Modem状态的改变等,这类消息称为URC消息;

        另一部分是由终端发送给Modem后,Modem给出的回应,属于非URC消息

        对于URC消息来说,只需要调用相应的通知机制即可;而对于非URC消息,我们还需要把相应的数据返回给当初发送请求的单位。

    既然RIL层对消息的处理方式不同,那么对应的在RILJ中也要分开处理:
[java] view plain copy
  1. private void processResponse (Parcel p) {  
  2.     int type;  
  3.     //得到数据的类型,是URC消息还是非URC消息  
  4.     type = p.readInt();  
  5.     if (type == RESPONSE_UNSOLICITED) {  
  6.         //URC消息的处理  
  7.         processUnsolicited (p);  
  8.     } else if (type == RESPONSE_SOLICITED) {  
  9.         //非URC消息的处理  
  10.         RILRequest rr = processSolicited (p);  
  11.         if (rr != null) {  
  12.             rr.release();  
  13.             decrementWakeLock();  
  14.         }  
  15.     }  
  16. }  

        上面看到,URC消息是通过processUnsolicited处理的,而非URC消息是由processSolicited处理的,我们分别介绍两种处理流程。

4.2.1、URC消息处理流程


        URC消息是由processUnsolicited处理的:
[java] view plain copy
  1. private void processUnsolicited (Parcel p) {  
  2.     int response;  
  3.     Object ret;  
  4.     //读取当前消息的消息码  
  5.     response = p.readInt();  
  6.     //先处理  
  7.     switch(response) {  
  8.         case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED: ret =  responseVoid(p); break;  
  9.         case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: ret =  responseVoid(p); break;  
  10.         case RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED: ret =  responseVoid(p); break;  
  11.         case RIL_UNSOL_RESPONSE_NEW_SMS: ret =  responseString(p); break;  
  12.         ........  
  13.     }  
  14.     //再次处理  
  15.     switch(response) {  
  16.         case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED:  
  17.             //Radio状态改变  
  18.             switchToRadioState(newState);  
  19.         break;  
  20.         case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED:  
  21.             //通知call状态改变  
  22.             mCallStateRegistrants.notifyRegistrants(new AsyncResult(nullnullnull));  
  23.         break;  
  24.         case RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED:  
  25.             //通知网络状态改变  
  26.             mVoiceNetworkStateRegistrants.notifyRegistrants(new AsyncResult(nullnullnull));  
  27.         break;  
  28.         case RIL_UNSOL_RESPONSE_NEW_SMS: {  
  29.             //新短信  
  30.             SmsMessage sms;  
  31.             sms = SmsMessage.newFromCMT(a);  
  32.             if (mGsmSmsRegistrant != null) {  
  33.                 //发送短信通知  
  34.                 mGsmSmsRegistrant.notifyRegistrant(new AsyncResult(null, sms, null));  
  35.             }  
  36.         break;  
  37.         }  
  38.         case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT:  
  39.             //短信报告  
  40.             mSmsStatusRegistrant.notifyRegistrant( new AsyncResult(null, ret, null));  
  41.         break;  
  42.         }  
  43.     }  
  44. }  
        上面代码只挑出部分消息的处理代码,从中可以看到,URC消息的处理要经过两个switch语句的处理:
        A、第一个switch:
            根据不同的消息码对数据进行初步解析,得到上报的有效数据
            responseVoid:当前消息的附带数据为空。
            responseString:当前消息的附带数据为String型,需要得到String。
            responseInts:当前消息的附带数据为int型的数组,需要得到这个数组。
        B、第二个switch

            在这个switch语句中针对不同的消息码用刚刚得到的有效数据进行不同的处理,主要就是调用相应的管理者去做相应的通知。

4.2.2、非URC消息处理流程


        非URC消息是在processSolicited中处理的:
[java] view plain copy
  1. private RILRequest processSolicited (Parcel p) {  
  2.     RILRequest rr;  
  3.   
  4.     //得到当前的RILRequest对象,然后从mRequestList中将其删除  
  5.     rr = findAndRemoveRequestFromList(serial);  
  6.   
  7.     //得到RIL上报的数据  
  8.     switch (rr.mRequest) {  
  9.         case RIL_REQUEST_GET_SIM_STATUS: ret =  responseIccCardStatus(p); break;  
  10.         case RIL_REQUEST_ENTER_SIM_PIN: ret =  responseInts(p); break;  
  11.         case RIL_REQUEST_ENTER_SIM_PUK: ret =  responseInts(p); break;  
  12.         case RIL_REQUEST_ENTER_SIM_PIN2: ret =  responseInts(p); break;  
  13.         case RIL_REQUEST_ENTER_SIM_PUK2: ret =  responseInts(p); break;  
  14.         case RIL_REQUEST_CHANGE_SIM_PIN: ret =  responseInts(p); break;  
  15.         case RIL_REQUEST_CHANGE_SIM_PIN2: ret =  responseInts(p); break;  
  16.         case RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION: ret =  responseInts(p); break;  
  17.         case RIL_REQUEST_GET_CURRENT_CALLS: ret =  responseCallList(p); break;  
  18.         case RIL_REQUEST_DIAL: ret =  responseVoid(p); break;  
  19.         case RIL_REQUEST_GET_IMSI: ret =  responseString(p); break;  
  20.         case RIL_REQUEST_HANGUP: ret =  responseVoid(p); break;  
  21.         case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND: ret =  responseVoid(p); break;  
  22.         ........  
  23.         default:  
  24.             throw new RuntimeException("Unrecognized solicited response: " + rr.mRequest);  
  25.     }  
  26.   
  27.     //对PUK的两个消息需要单独的通知其管理者  
  28.     switch (rr.mRequest) {  
  29.         case RIL_REQUEST_ENTER_SIM_PUK:  
  30.         case RIL_REQUEST_ENTER_SIM_PUK2:  
  31.             if (mIccStatusChangedRegistrants != null) {  
  32.                 mIccStatusChangedRegistrants.notifyRegistrants();  
  33.             }  
  34.             break;  
  35.     }  
  36.   
  37.     //将返回值返回给当初的请求者,由请求者去决定如何处理数据  
  38.     if (rr.mResult != null) {  
  39.         AsyncResult.forMessage(rr.mResult, ret, null);  
  40.         rr.mResult.sendToTarget();  
  41.     }  
  42.     return rr;  
  43. }  
        在非URC消息的处理流程中,同样要先得到RIL层上报的有效数据,但是得到数据之后并不像URC消息那样主动的去通知相应的管理者去做通知,而是需要根据当前消息的mResult把有效数据发送给当初的发送者,由发送者自己去处理消息。

        以上就是Framework中的RILJ消息处理流程。

        下面贴出流程图:

0 0