转载自http://blog.csdn.net/hellolinshoujie/article/details/7790566
Moto XT800(双卡手机)短信和电话服务的总结
分类: Android2011-06-15 20:50 777人阅读 评论(3) 收藏 举报

Moto XT800 是Moto为中国电信定制的一款天翼双卡双待手机, 采用的是深度定制的android系统. 原生的android系统是不支持双卡的, 所以Moto对android系统进行了大量的修改, 其中变化最大的当属短信和电话服务. 这篇文章主要是讲述XT800的短信和电话服务的机制, 以及发送短信和打电话的方法.

原生的android系统的电话和短信服务

原生的android系统只有一对电话和短信服务. 这两个服务的名称分别是phone和isms. Phone服务用来打电话和接电话, 所有的电话操作都是通过这个服务来进行的. isms服务用来发送和接受短信, 所有的短信操作都是通过这个服务来进行的. API SmsManager.sendTextMessage 实际上是对这个服务进行了一定的封装. 两个服务都是通过Aidl进行类似IPC的跨进程调用的.

Ps1: 获取当前系统服务的方法

>adb shell

$service list

PS2: 在程序中获取系统服务的方法

需要用到系统隐藏api,

IBinder iBinder = ServiceManager.getService(String ServiceName); //获取系统相应的服务

如果调用的话, 需要使用aidl

ISms isms = ISms.Stub.asInterface(iBinder);

Xt800的电话和短信服务

首先需要说明, XT800有两个SIM卡卡槽, 卡槽1是CDMA, 卡槽2是GSM. 也就是说,双卡双待是有条件的, 必须一个是CDMA,一个是GSM , 而且要放置在正确的位置上.

XT800的电话和短信服务有两对, 以对应两个卡CDMA 和GSM.

服务列表如下:

对于双卡手机, 可能只插了一张卡, 即使有两张卡, 也要判断哪个是GSM, 哪个CDMA的. 下表对三种情况: 双卡, 单GSM卡, 单CDMA卡 分别测试.

PS:

1.       判断网络类型的方法:

iTelephony.getActivePhoneType(); //2 CDMA 1 GSM

2. 判断是否服务是否有效的方法

ITelephony iTelephony = ITelephony.Stub.asInterface(phone);

iTelephony.isRadioOn();

  1. 1)public static SmsMessage createFromPdu(byte[] pdu)
  2. 功能:为原始的PDU创建一个SmsMessage对象
  3. 参数:pdu:原始的pdu
  4. 返回值:SmsMessage对象
  5. 2) public static SmsMessage createFromEfRecord(int index, byte[] data)
  6. 功能:创建一个SMS EF记录的SmsMessage
  7. 参数:index:短信记录索引。应该是由SmsManager.getAllMessagesFromSim+1
  8. data:记录数据
  9. 返回值:表示记录的SmsMessage对象
  10. 3) public static int getTPLayerLengthForPDU(String pdu)
  11. 功能:为SMS-SUBMIT PDU获取以字节为单位的TP层长度
  12. 返回值:长度
  13. 4) public static int[] calculateLength(CharSequence msgBody, boolean use7bitOnly)
  14. 功能:计算消息正文和号码所需要的字符数
  15. 参数: msgBody:编码的信息
  16. use7bitOnly:如果是true的话,不属于无线电专用7位编码的一部分的字符作为一个空格字符。如果为false,并且messageBody包含非7位encodable字符,长度为计算使用的是16位编码。
  17. 返回值:int[1]编码代号;int[2]直到下一条消息的编码数;int[3]编码单位大小的指标;int[4]和int[0]表示SMS需要的字符数。
  18. 5) public static ArrayList<String> fragmentText(String text)
  19. 功能:将消息分成几个片断
  20. 参数:text:消息正文
  21. 返回值:包含原信息的ArrayList
  22. 6) public static int[] calculateLength(String messageBody, boolean use7bitOnly)
  23. 功能:为SMS-SUBMIT PDU获取以字节为单位的TP层长度
  24. 返回值:长度
  25. 7) public static SubmitPdu getDeliverPdu(long Timestamp,String OriginationAddress, String message,boolean statusReportRequested, byte[] header)
  26. 功能:为原始的地址和号码得到SMS-DELIVER PDU
  27. 8) public static SubmitPdu getSubmitPdu(String scAddress,String destinationAddress, String message,boolean statusReportRequested, byte[] header)
  28. 功能:为目的地址和信息得到SMS-SUBMIT PDU
  29. 参数:scAddress:服务中心地址,默认为空
  30. destinationAddress:信息的目的地址
  31. message:信息数据
  32. 返回值:如果适用,返回编码的SC地址和编码的信息,如果编码错误则返回空。
  33. 9) public static SubmitPdu getSubmitPdu(String scAddress,String destinationAddress, String message, boolean statusReportRequested)
  34. 功能:为目的地址和信息得到SMS-SUBMIT PDU
  35. 参数:scAddress:服务中心地址,默认为空
  36. destinationAddress:信息的目的地址
  37. message:信息数据
  38. 返回值:如果适用,返回编码的SC地址和编码的信息,如果编码错误则返回空。
  39. 10) public static SubmitPdu getSubmitPdu(String scAddress,String destinationAddress, short destinationPort, byte[] data,boolean statusReportRequested)
  40. 功能:为目的地址和信息得到SMS-SUBMIT PDU
  41. 参数:scAddress:服务中心地址,默认为空
  42. destinationAddress:信息的目的地址
  43. destinationPort:信息目的地址的端口
  44. message:信息数据
  45. 返回值:如果适用,返回编码的SC地址和编码的信息,如果编码错误则返回空。
  46. 11) public String getServiceCenterAddress()
  47. 功能:获取短信服务中心的地址
  48. 返回值:短信服务中心的地址或空
  49. 12) public String getOriginatingAddress()
  50. 功能:获取信息发出的地址
  51. 返回值:信息发出的地址或空
  52. 13) public String getMessageBody()
  53. 功能:获取消息正文
  54. 返回值:以string的形式返回消息正文或空
  55. 14) public MessageClass getMessageClass()
  56. 功能:获取消息的类
  57. 返回值:消息类
  58. 15) public long getTimestampMillis()
  59. 功能:以currentTimeMillis()格式获取短信服务中心的时间戳
  60. 返回值:短信服务中心的时间戳
  1. package net.sunniwell.app;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.telephony.CellLocation;
  5. import android.telephony.PhoneStateListener;
  6. import android.telephony.TelephonyManager;
  7. public class TelManager extends Activity {
  8. @Override
  9. protected void onCreate(Bundle savedInstanceState) {
  10. super.onCreate(savedInstanceState);
  11. TelephonyManager tm = (TelephonyManager) this.getSystemService(TELEPHONY_SERVICE);
  12. /*
  13. * 电话状态:
  14. * 1.tm.CALL_STATE_IDLE=0          无活动
  15. * 2.tm.CALL_STATE_RINGING=1  响铃
  16. * 3.tm.CALL_STATE_OFFHOOK=2  摘机
  17. */
  18. tm.getCallState();//int
  19. /*
  20. * 电话方位:
  21. *
  22. */
  23. tm.getCellLocation();//CellLocation
  24. /*
  25. * 唯一的设备ID:
  26. * GSM手机的 IMEI 和 CDMA手机的 MEID.
  27. * Return null if device ID is not available.
  28. */
  29. tm.getDeviceId();//String
  30. /*
  31. * 设备的软件版本号:
  32. * 例如:the IMEI/SV(software version) for GSM phones.
  33. * Return null if the software version is not available.
  34. */
  35. tm.getDeviceSoftwareVersion();//String
  36. /*
  37. * 手机号:
  38. * GSM手机的 MSISDN.
  39. * Return null if it is unavailable.
  40. */
  41. tm.getLine1Number();//String
  42. /*
  43. * 附近的电话的信息:
  44. * 类型:List<NeighboringCellInfo>
  45. * 需要权限:android.Manifest.permission#ACCESS_COARSE_UPDATES
  46. */
  47. tm.getNeighboringCellInfo();//List<NeighboringCellInfo>
  48. /*
  49. * 获取ISO标准的国家码,即国际长途区号。
  50. * 注意:仅当用户已在网络注册后有效。
  51. *       在CDMA网络中结果也许不可靠。
  52. */
  53. tm.getNetworkCountryIso();//String
  54. /*
  55. * MCC+MNC(mobile country code + mobile network code)
  56. * 注意:仅当用户已在网络注册时有效。
  57. *    在CDMA网络中结果也许不可靠。
  58. */
  59. tm.getNetworkOperator();//String
  60. /*
  61. * 按照字母次序的current registered operator(当前已注册的用户)的名字
  62. * 注意:仅当用户已在网络注册时有效。
  63. *    在CDMA网络中结果也许不可靠。
  64. */
  65. tm.getNetworkOperatorName();//String
  66. /*
  67. * 当前使用的网络类型:
  68. * 例如: NETWORK_TYPE_UNKNOWN  网络类型未知  0
  69. NETWORK_TYPE_GPRS     GPRS网络  1
  70. NETWORK_TYPE_EDGE     EDGE网络  2
  71. NETWORK_TYPE_UMTS     UMTS网络  3
  72. NETWORK_TYPE_HSDPA    HSDPA网络  8
  73. NETWORK_TYPE_HSUPA    HSUPA网络  9
  74. NETWORK_TYPE_HSPA     HSPA网络  10
  75. NETWORK_TYPE_CDMA     CDMA网络,IS95A 或 IS95B.  4
  76. NETWORK_TYPE_EVDO_0   EVDO网络, revision 0.  5
  77. NETWORK_TYPE_EVDO_A   EVDO网络, revision A.  6
  78. NETWORK_TYPE_1xRTT    1xRTT网络  7
  79. */
  80. tm.getNetworkType();//int
  81. /*
  82. * 手机类型:
  83. * 例如: PHONE_TYPE_NONE  无信号
  84. PHONE_TYPE_GSM   GSM信号
  85. PHONE_TYPE_CDMA  CDMA信号
  86. */
  87. tm.getPhoneType();//int
  88. /*
  89. * Returns the ISO country code equivalent for the SIM provider's country code.
  90. * 获取ISO国家码,相当于提供SIM卡的国家码。
  91. *
  92. */
  93. tm.getSimCountryIso();//String
  94. /*
  95. * Returns the MCC+MNC (mobile country code + mobile network code) of the provider of the SIM. 5 or 6 decimal digits.
  96. * 获取SIM卡提供的移动国家码和移动网络码.5或6位的十进制数字.
  97. * SIM卡的状态必须是 SIM_STATE_READY(使用getSimState()判断).
  98. */
  99. tm.getSimOperator();//String
  100. /*
  101. * 服务商名称:
  102. * 例如:中国移动、联通
  103. * SIM卡的状态必须是 SIM_STATE_READY(使用getSimState()判断).
  104. */
  105. tm.getSimOperatorName();//String
  106. /*
  107. * SIM卡的序列号:
  108. * 需要权限:READ_PHONE_STATE
  109. */
  110. tm.getSimSerialNumber();//String
  111. /*
  112. * SIM的状态信息:
  113. *  SIM_STATE_UNKNOWN          未知状态 0
  114. SIM_STATE_ABSENT           没插卡 1
  115. SIM_STATE_PIN_REQUIRED     锁定状态,需要用户的PIN码解锁 2
  116. SIM_STATE_PUK_REQUIRED     锁定状态,需要用户的PUK码解锁 3
  117. SIM_STATE_NETWORK_LOCKED   锁定状态,需要网络的PIN码解锁 4
  118. SIM_STATE_READY            就绪状态 5
  119. */
  120. tm.getSimState();//int
  121. /*
  122. * 唯一的用户ID:
  123. * 例如:IMSI(国际移动用户识别码) for a GSM phone.
  124. * 需要权限:READ_PHONE_STATE
  125. */
  126. tm.getSubscriberId();//String
  127. /*
  128. * 取得和语音邮件相关的标签,即为识别符
  129. * 需要权限:READ_PHONE_STATE
  130. */
  131. tm.getVoiceMailAlphaTag();//String
  132. /*
  133. * 获取语音邮件号码:
  134. * 需要权限:READ_PHONE_STATE
  135. */
  136. tm.getVoiceMailNumber();//String
  137. /*
  138. * ICC卡是否存在
  139. */
  140. tm.hasIccCard();//boolean
  141. /*
  142. * 是否漫游:
  143. * (在GSM用途下)
  144. */
  145. tm.isNetworkRoaming();//
  146. }
  147. }
  148. 本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/gnuser/archive/2010/07/06/5715599.aspx
  149. 非常感谢yili_xie兄!!
  150. 编译so库还没试,刚刚找到了Android重启代码(frameworks/policies/base/phone/com/android/internal/policy/impl/ShutdownThread.java)。看了一下,它里面主要调用了一个Power.hutdown()(import android.os.Power),但我在代码里面添加后运行该句不起作用,一点反应也没有,把整个ShoutdownThread添加到我的工程里,按照它的方法ShutdownThread.shutdownAfterDisablingRadio(getContext(), true)也是一样。还有Power.reboot(null)也是,一点反应都没有。
  151. 在Android.mk中已添加过LOCAL_CERTIFICATE := platform,在AndroidManifest.xml也添加了android:sharedUserId="android.uid.system",<uses-permission android:name="android.permission.REBOOT"></uses-permission>。
  152. 不知道还需要什么设置。有些地方说某些功能需要root权限,这个权限怎么获得啊?
  1. /**
  2. * 获取各个数据项的值
  3. */
  4. public void initListValues(){
  5. listValues.add(tm.getDeviceId());//获取设备编号
  6. listValues.add(tm.getSimCountryIso());//获取SIM卡国别
  7. listValues.add(tm.getSimSerialNumber());//获取SIM卡序列号
  8. listValues.add(simState[tm.getSimState()]);//获取SIM卡状态
  9. listValues.add((tm.getDeviceSoftwareVersion()!=null?tm.getDeviceSoftwareVersion():"未知"));    //获取软件版本
  10. listValues.add(tm.getNetworkOperator());//获取网络运营商代号
  11. listValues.add(tm.getNetworkOperatorName());//获取网络运营商名称
  12. listValues.add(phoneType[tm.getPhoneType()]);//获取手机制式
  13. listValues.add(tm.getCellLocation().toString());//获取设备当前位置
  14. }
  15. }

Android的RIL驱动模块,在hardware/ril目录下,一共分rild,libril.so以及librefrence_ril.so三个部分,另有一 radiooptions可供自动或手动调试使用。都依赖于include目录中ril.h头文件。目前cupcake分支上带的是gsm的支持,另有一 cdma分支,这里分析的是gsm驱动。

GSM模块,由于Modem的历史原因,AP一直是通过基于串口的AT命令与BB交互。包括到了目前的一些edge或3g模块,或像omap这类ap,bp集成的芯片,已经使用了USB或其他等高速总线通信,但大多仍然使用模拟串口机制来使用AT命令。这里的RIL(Radio Interface Layer)层,主要也就是基于AT命令的操作,如发命令,response解析等。(gprs等传输会用到的MUX协议等在这里并没有包含,也暂不作介绍。)

  以下是详细分析,本文主要涉及基本架构和初始化的内容:

  首先介绍一下rild与libril.so以及librefrence_ril.so的关系:

  1. rild:

  仅实现一main函数作为整个ril层的入口点,负责完成初始化。

  2. libril.so:

  与rild结合相当紧密,是其共享库,编译时就已经建立了这一关系。组成部分为ril.cpp,ril_event.cpp。libril.so驻留在 rild这一守护进程中,主要完成同上层通信的工作,接受ril请求并传递给librefrence_ril.so,同时把来自librefrence_ril.so的反馈回传给调用进程。

  3. librefrence_ril.so:

  rild通过手动的dlopen方式加载,结合稍微松散,这也是因为librefrence.so主要负责跟Modem硬件通信的缘故。这样做更方便替换或修改以适配更多的Modem种类。它转换来自libril.so的请求为AT命令,同时监控Modem的反馈信息,并传递回libril.so。在初始化时, rild通过符号RIL_Init获取一组函数指针并以此与之建立联系。

4. radiooptions:

  radiooptiongs通过获取启动参数, 利用socket与rild通信,可供调试时配置Modem参数。

  接下来分析初始化流程,主入口是rild.c中的main函数,主要完成三个任务:

  1. 开启libril.so中的event机制, 在RIL_startEventLoop中,是最核心的由多路I/O驱动的消息循环。

  2. 初始化librefrence_ril.so,也就是跟硬件或模拟硬件modem通信的部分(后面统一称硬件), 通过RIL_Init函数完成。

  3. 通过RIL_Init获取一组函数指针RIL_RadioFunctions, 并通过RIL_register完成注册,并打开接受上层命令的socket通道。

  首先看第一个任务,也就是RIL_startEventLoop函数。RIL_startEventLoop在ril.cpp中实现,它的主要目的是通过pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL)建立一个dispatch线程,入口点在eventLoop. 而eventLoop中,会调ril_event.cpp中的ril_event_loop()函数,建立起消息(event)队列机制。

  我们来仔细看看这一消息队列的机制,这些代码都在ril_event.cpp中。

[cpp] view plaincopy
  1. void ril_event_init();
  2. void ril_event_set(struct ril_event * ev, int fd, bool persist, ril_event_cb func, void * param);
  3. void ril_event_add(struct ril_event * ev);
  4. void ril_timer_add(struct ril_event * ev, struct timeval * tv);
  5. void ril_event_del(struct ril_event * ev);
  6. void ril_event_loop();
  7. struct ril_event {
  8. struct ril_event *next;
  9. struct ril_event *prev;
  10. int fd;
  11. int index;
  12. bool persist;
  13. struct timeval timeout;
  14. ril_event_cb func;
  15. void *param;
  16. };

  每个ril_event结构,与一个fd句柄绑定(可以是文件,socket,管道等),并且带一个func指针去执行指定的操作。

  具体流程是: ril_event_init完成后,通过ril_event_set来配置一新ril_event,并通过ril_event_add加入队列之中(实际通常用rilEventAddWakeup来添加),add会把队列里所有ril_event的fd,放入一个fd集合readFds中。这样 ril_event_loop能通过一个多路复用I/O的机制(select)来等待这些fd,如果任何一个fd有数据写入,则进入分析流程processTimeouts(),processReadReadies(&rfds, n),firePending()。 后文会详细分析这些流程。

  另外我们可以看到, 在进入ril_event_loop之前,已经挂入了一s_wakeupfd_event, 通过pipe的机制实现的,这个event的目的是可以在一些情况下,能内部唤醒ril_event_loop的多路复用阻塞,比如一些带timeout的命令timeout到期的时候。

  至此第一个任务分析完毕,这样便建立起了基于event队列的消息循环,稍后便可以接受上层发来的的请求了(上层请求的event对象建立,在第三个任务中)。

  接下来看第二个任务,这个任务的入口是RIL_Init, RIL_Init首先通过参数获取硬件接口的设备文件或模拟硬件接口的socket. 接下来便新开一个线程继续初始化, 即mainLoop。

  mainLoop的主要任务是建立起与硬件的通信,然后通过read方法阻塞等待硬件的主动上报或响应。在注册一些基础回调(timeout,readerclose)后,mainLoop首先打开硬件设备文件,建立起与硬件的通信,s_device_path和s_port 是前面获取的设备路径参数,将其打开(两者可以同时打开并拥有各自的reader,这里也很容易添加双卡双待等支持)。

  接下来通过 at_open函数建立起这一设备文件上的reader等待循环,这也是通过新建一个线程完成, ret = pthread_create(&s_tid_reader, &attr, readerLoop, &attr),入口点readerLoop。

  AT命令都是以rn或nr的换行符来作为分隔符的,所以readerLoop是line驱动的,除非出错,超时等,否则会读到一行完整的响应或主动上报,才会返回。这个循环跑起来以后,我们基本的AT响应机制已经建立了起来。它的具体分析,包括at_open中挂接的ATUnsolHandler, 我们都放到后面分析response的连载文章里去。

  有了响应的机制(当然,能与硬件通信也已经可以发请求了),通过 RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0),跑到initializeCallback中,执行一些Modem的初始化命令,主要都是AT命令的方式。发AT命令的流程,我们放到后面分析request的连载文章里。这里可以看到,主要是一些参数配置,以及网络状态的检查等。至此第二个任务分析完毕,硬件已经可以访问了。

  最后是第三个任务。第三个任务是由RIL_Init的返回值开始的,这是一个RIL_RadioFunctions结构的指针。

[cpp] view plaincopy
  1. typedef struct {
  2. int version;        /* set to RIL_VERSION */
  3. RIL_RequestFunc onRequest;
  4. RIL_RadioStateRequest onStateRequest;
  5. RIL_Supports supports;
  6. RIL_Cancel onCancel;
  7. RIL_GetVersion getVersion;
  8. } RIL_RadioFunctions;

  其中最重要的是onRequest域,上层来的请求都由这个函数进行映射后转换成对应的AT命令发给硬件。

  rild通过RIL_register注册这一指针。

  RIL_register中要完成的另外一个任务,就是打开前面提到的跟上层通信的socket接口(s_fdListen是主接口,s_fdDebug供调试时使用)。

  然后将这两个socket接口使用任务一中实现的机制进行注册(仅列出s_fdListen)

[cpp] view plaincopy
  1. ril_event_set (&s_listen_event, s_fdListen, false,
  2. listenCallback, NULL);
  3. rilEventAddWakeup (&s_listen_event);

  这样将两个socket加到任务一中建立起来多路复用I/O的检查句柄集合中,一旦有上层来的(调试)请求,event机制便能响应处理了。到这里启动流程已经分析完毕。

request流程

1. 多路复用I/O机制的运转
上文说到request是接收,是通过ril_event_loop中的多路复用I/O,也对初始化做了分析.现在我们来仔细看看这个机制如何运转.
ril_event_set负责配置一个event,主要有两种event:
ril_event_add添加使用多路I/O的event,它负责将其挂到队列,同时将event的通道句柄fd加入到watch_table,然后通过select等待.
ril_timer_add添加timer event,它将其挂在队列,同时重新计算最短超时时间.
无论哪种add,最后都会调用triggerEvLoop来刷新队列,更新超时值或等待对象.
刷新之后, ril_event_loop从阻塞的位置,select返回,只有两种可能,一是超时,二是等待到了某I/O操作.
超时的处理在processTimeouts中,摘下超时的event,加入pending_list.
检查有I/O操作的通道的处理在processReadReadies中,将超时的event加入pending_list.
最后在firePending中,检索pending_list的event并依次执行event->func.
这些操作完之后,计算新超时时间,并重新select阻塞于多路I/O.
前面的初始化流程已分析得知,初始化完成以后,队列上挂了3个event对象,分别是:
s_listen_event: 名为rild的socket,主要requeset & response通道
s_debug_event: 名为rild-debug的socket,调试用requeset & response通道(流程与s_listen_event基本相同,后面仅分析s_listen_event)
s_wakeupfd_event: 无名管道,用于队列主动唤醒(前面提到的队列刷新,就用它来实现,请参考使用它的相关地方)
2. request的传入和dispatch
明白了event队列的基本运行流程,我们可以来看看request是怎么传入和dispatch的了.
上层的部分,核心代码在frameworks/base/telephony/java/com/android/internal/telephony /gsm/RIL.java,这是android java框架处理radio(gsm)的核心组件.本文因为主要关注rild,也就是驱动部分,所以这里只作简单介绍.
我们看一个具体的例子,RIL.java中的dial函数:

[cpp] view plaincopy
  1. public void
  2. dial (String address, int clirMode, Message result)
  3. {
  4. RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);
  5. rr.mp.writeString(address);
  6. rr.mp.writeInt(clirMode);
  7. if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
  8. send(rr);
  9. }

rr是以RIL_REQUEST_DIAL为request号而申请的一个RILRequest对象.这个request号在java框架和rild库中共享(参考RILConstants.java中这些值的由来:))
RILRequest初始化的时候,会连接名为rild的socket(也就是rild中s_listen_event绑定的socket),初始化数据传输的通道. 
rr.mp 是Parcel对象,Parcel是一套简单的序列化协议,用于将对象(或对象的成员)序列化成字节流,以供传递参数之用.这里可以看到String address和int clirMode都是将依次序列化的成员.在这之前,rr初始化的时候,request号跟request的序列号(自动生成的递增数),已经成为头两个将被序列化的成员.这为后面的request解析打下了基础.
接下来是send到handleMessage的流程,send将rr直接传递给另一个线程的handleMessage,handleMessage执行data = rr.mp.marshall()执行序列化操作, 并将data字节流写入到rild socket.
接下来回到我们的rild,select发现rild socket有了请求链接的信号,导致s_listen_event被挂入pending_list,执行event->func,即
static void listenCallback (int fd, short flags, void *param);
接下来,s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen),获取传入的socket描述符,也就是上层的java RIL传入的连接.
然后,通过record_stream_new建立起一个record_stream, 将其与s_fdCommand绑定, 这里我们不关注record_stream 的具体流程, 我们来关注command event的回调, processCommandsCallback函数, 从前面的event机制分析, 一旦s_fdCommand上有数据, 此回调函数就会被调用. (略过onNewCommandConnect的分析)
processCommandsCallback通过 record_stream_get_next阻塞读取s_fdCommand上发来的 数据, 直到收到一完整的request(request包的完整性由record_stream的机制保证), 然后将其送达processCommandBuffer.
进入processCommandBuffer以后,我们就正式进入了命令的解析部分. 每个命令将以RequestInfo的形式存在.

[cpp] view plaincopy
  1. typedef struct RequestInfo {
  2. int32_t token; //this is not RIL_Token
  3. CommandInfo *pCI;
  4. struct RequestInfo *p_next;
  5. char cancelled;
  6. char local; // responses to local commands do not go back to command process
  7. } RequestInfo;

这里的pRI就是一个RequestInfo结构指针, 从socket过来的数据流, 前面提到是Parcel处理过的序列化字节流, 这里会通过反序列化的方法提取出来. 最前面的是request号, 以及token域(request的递增序列号). 我们更关注这个request号, 前面提到, 上层和rild之间, 这个号是统一的. 它的定义是一个包含ril_commands.h的枚举, 在ril.cpp中

[cpp] view plaincopy
  1. static CommandInfo s_commands[] = {
  2. #include "ril_commands.h"
  3. };

pRI直接访问这个数组, 来获取自己的pCI.
这是一个CommandInfo结构:

[cpp] view plaincopy
  1. typedef struct {
  2. int requestNumber;
  3. void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);
  4. int(*responseFunction) (Parcel &p, void *response, size_t responselen);
  5. } CommandInfo;

基本解析到这里就完成了, 接下来, pRI被挂入pending的request队列, 执行具体的pCI->dispatchFunction, 进行详细解析.
3. request的详细解析
对dial而言, CommandInfo结构是这样初始化的:
{RIL_REQUEST_DIAL, dispatchDial, responseVoid},
这里执行dispatchFunction, 也就是dispatchDial这一函数.我们可以看到其实有很多种类的dispatch function, 比如dispatchVoid, dispatchStrings, dispatchSIM_IO等等, 这些函数的区别, 在于Parcel传入的参数形式,Void就是不带参数的,Strings是以string[]做参数,又如Dial等,有自己的参数解析方式,以此类推.
request号和参数现在都有了,那么可以进行具体的request函数调用了.
s_callbacks.onRequest(pRI->pCI->requestNumber, xxx, len, pRI)完成这一操作.
s_callbacks 是上篇文章中提到的获取自libreference-ril的RIL_RadioFunctions结构指针,request请求在这里转入底层的 libreference-ril处理,handler是reference-ril.c中的onRequest.
onRequest进行一个简单的switch分发,我们依然来看RIL_REQUEST_DIAL
流程是 onRequest-->requestDial-->at_send_command-->at_send_command_full-->at_send_command_full_nolock-->writeline
requestDial中将命令和参数转换成对应的AT命令,调用公共send command接口at_send_command.
除了这个接口之外,还有 at_send_command_singleline,at_send_command_sms,at_send_command_multiline 等,这是根据at返回值,以及发命令流程的类型来区别的.比如at+csq这类,需要at_send_command_singleline,而发送短信,因为有prompt提示符">",传裸数据,结束符等一系列操作,需要专门用at_send_command_sms来实现.
然后执行at_send_command_full,前面几个接口都会最终到这里,再通过一个互斥的at_send_command_full_nolock调用,然后完成最终的写出操作,在writeline中,写出到初始化时打开的设备中.
writeline返回之后,还有一些操作,如保存type等信息,供response回来时候使用,以及一些超时处理. 不再详述.
到这里,request的详细流程,就分析完毕了.
response流程
前文对request的分析, 终止在了at_send_command_full_nolock里的writeline操作,因为这里完成命令写出到硬件设备的操作,接下来就是等待硬件响应,也就是response的过程了。我们的分析也是从这里开始。
response信息的获取,是在第一篇初始化分析中,提到的readerLoop中。由readline函数以‘行’为单位接收上来。
AT的response有两种,一是主动上报的,比如网络状态,短信,来电等都不需要经过请求,有一unsolicited词语专门描述。另一种才是真正意义上的response,也就是命令的响应。
这里我们可以看到,所有的行,首先经过sms的自动上报筛选,因为短信的AT处理通常比较麻烦,无论收发都单独列出。这里是因为要即时处理这条短信消息(两行,标志+pdu),而不能拆开处理。处理函数为onUnsolicited(由s_unsolHandler指向),我们等下介绍。
除开sms的特例,所有的line都要经过processLine,我们来看看这个流程:
processLine
|----no cmd--->handleUnsolicited //主动上报
|----isFinalResponseSuccess--->handleFinalResponse //成功,标准响应
|----isFinalResponseError--->handleFinalResponse //失败,标准响应
|----get '>'--->send sms pdu //收到>符号,发送sms数据再继续等待响应
|----switch s_type--->具体响应  //命令有具体的响应信息需要对应分析
我们这里主要关注handleUnsolicited自动上报(会调用到前面smsUnsolicite也调用的onUnsolicite),以及 switch s_type具体响应信息,另外具体响应需要handleFinalResponse这样的标准响应来最终完成。
1. onUnsolicite(主动上报响应)
static void onUnsolicited (const char *s, const char *sms_pdu);
短信的AT设计真是麻烦的主,以致这个函数的第二个参数完全就是为它准备的。
response 的主要的解析过程,由at_tok.c中的函数完成,其实就是字符串按块解析,具体的解析方式由每条命令或上报信息自行决定。这里不再详述,onUnsolicited只解析出头部(一般是+XXXX的形式),然后按类型决定下一步操作,操作为 RIL_onUnsolicitedResponse和RIL_requestTimedCallback两种。
a)RIL_onUnsolicitedResponse: 
将 unsolicited的信息直接返回给上层。通过Parcel传递,将 RESPONSE_UNSOLICITED,unsolResponse(request号)写入Parcel先,然后通过 s_unsolResponses数组,查找到对应的responseFunction完成进一步的的解析,存入Parcel中。最终通过 sendResponse将其传递回原进程。流程:
sendResponse-->sendResponseRaw-->blockingWrite-->write to s_fdCommand(前面建立起来的和上层框架的socket连接)
这些步骤之后有一些唤醒系统等其他操作。不再详述。
b)RIL_requestTimedCallback:
通过event机制(参考文章二)实现的timer机制,回调对应的内部处理函数。通过internalRequestTimedCallback将回调添加到event循环,最终完成callback上挂的函数的回调。比如pollSIMState,onPDPContextListChanged等回调, 不用返回上层, 内部处理就可以。
2. switch s_type(命令的具体响应)及handleFinalResponse(标准响应)
命令的类型(s_type)在send command的时候设置(参考文章二),有NO_RESULT,NUMERIC,SINGLELINE,MULTILINE几种,供不同的AT使用。比如AT+CSQ是singleline, 返回at+csq=xx,xx,再加一行OK,比如一些设置命令,就是no_result, 只有一行OK或ERROR。
这几个类型的解析都很相仿,通过一定的判断(比较AT头标记等),如果是对应的响应,就通过 addIntermediate挂到一个临时结果sp_response->p_intermediates队列里。如果不是对应响应,那它其实应该是穿插其中的自动上报,用onUnsolicite来处理。
具体响应,只起一个获取响应信息到临时结果,等待具体分析的作用。无论有无具体响应,最终都得以标准响应handleFinalResponse来完成,也就是接受到OK,ERROR等标准response来结束,这是大多数AT命令的规范。
handleFinalResponse 会设置s_commandcond这一object,也就是at_send_command_full_nolock等待的对象。到这里,响应的完整信息已经完全获得,send command可以进一步处理返回的信息了(临时结果,以及标准返回的成功或失败,都在sp_response中)。
pp_outResponse参数将sp_response返回给调用at_send_command_full_nolock的函数。
继续我们在文章二的分析的话,这个函数其实是requestDial,不过requestDial忽略了响应,所以我们另外看个例子,如requestSignalStrength,命令其实就是前面提到的at+csq:
可以看到确实是通过at_send_command_singleline来进行的操作,response在p_response中。
p_response如果返回失败(也就是标准响应的ERROR等造成),则通过RIL_onRequestComplete发送返回数据给上层,结束命令。
如果成功,则进一步分析p_response->p_intermediates, 同样是通过at_tok.c里的函数进行分析。并同样将结果通过RIL_onRequestComplete返回。
RIL_onRequestComplete:
RIL_onRequestComplete和RIL_onUnsolicitedResponse很相仿,功能也一致。
通过Parcel来传递回上层,同样是先写入RESPONSE_SOLICITED(区别于 RESPONSE_UNSOLICITED),pRI->token(上层传下的request号),错误码(send command的错误,不是AT响应)。如果有AT响应,通过访问pRI->pCI->responseFunction来完成具体 response的解析,并写入Parcel。
然后通过同样的途径:
sendResponse-->sendResponseRaw-->blockingWrite-->write to s_fdCommand
完成最终的响应传递。
到这里,我们分析了自动上报与命令响应,其实response部分,也就告一段落了

android sim 卡双卡双待相关推荐

  1. android sim卡联系人存储格式,如何在Android设备上将联系人复制到SIM卡

    如何将联系人复制到SIM卡 以下部分是将联系人从计算机和Android手机存储卡复制到Android上的SIM卡的简单步骤.准备?让我们开始吧. 步骤1.安装并运行此Android管理器 开始时,在您 ...

  2. android+已移除sim卡,如何从Android SIM卡中删除/删除/擦除联系人

    作者:Selena Komez11月更新了20,2017 "我的兄弟是一个空白的经理,他在他的Android SIM卡上保存了数千个客户名称和电话号码.但是,现在为了保护客户的隐私,他需要将 ...

  3. android+sim卡短信,android 信息(mms)开发(七)-- sim卡短信

    这篇关于android源码是如何操作sim卡上的信息说明,sim\usim卡的信息说起来这算是比较特别的一部分,由于android源码的信息是没有开机自动导卡信息和联系人的,所以有时会忘掉它的存在应该 ...

  4. android sim卡分析,Android 判断SIM卡属于哪个移动运营商详解及实例

    Android 判断SIM卡属于哪个移动运营商 第一种方法: 获取手机的IMSI码,并判断是中国移动\中国联通\中国电信 TelephonyManager telManager = (Telephon ...

  5. Android sim卡 pin码解锁流程.

    sim卡 pin码解锁流程 1. PIN码解锁界面属于Phone Policy的一部分,代码位置在 \frameworks\base\policy\src\com\android\internal\p ...

  6. Android SIM卡识别加载流程

    文章目录 总述 代码路径 UICC框架 SIM卡识别加载流程 日志分析举例 总述 本文基于Android N(Android 7) 首先要知道SIM卡一般是挂载在CP侧(MODEM侧)的,由MODEM ...

  7. android sim卡应用程序,Android双SIM卡API

    MM们 您可以使用MultiSim库从多SIM卡设备中获取详细信息.每个SIM卡上的可用信息:IMEI,IMSI,SIM序列号,SIM状态,SIM操作员代码,SIM操作员名称,SIM国家/地区ISO, ...

  8. android sim卡槽,包教包会,十分钟让你搞定与或卡槽

    包教包会,十分钟让你搞定与或卡槽 2016-04-29 11:48:20 27点赞 83收藏 35评论 前言:很多手机厂商为了节省成本都会选择与或卡槽,美其名曰节省内部空间,让你的手机更薄,也有华为m ...

  9. android sim卡槽,一加5怎么装卡/插卡 一加手机5 SIM卡安装图文教程

    一加5发布之后今日迎来首销,通过消息得知目前四个平台基本上售罄,第二轮抢购将于6月28日发售.对于已经买到这款旗舰机的朋友来说,拿到真机之后我们需要安装手机卡方可正常使用哦.对于首次使用一加手机的朋友 ...

最新文章

  1. Day03 - Ruby比一比:Module的include与extend
  2. Python是世界上最好的语言,你不服不行!(附资料)
  3. 问题.NET--win7 IIS唯一密钥属性“VALUE”设置为“DEFAULT.ASPX”时,无法添加类型为“add”的重复集合...
  4. 使用Lombok优化代码
  5. apache工作模式
  6. 四十六、You-Get爬取B站热门排名的小视频
  7. ubuntu mysql混合开发_mysql5.7主从同步 ubuntu
  8. Git使用手册--超级详细,自己都看哭了
  9. 绕过AppLocker系列之MSBuild的利用
  10. Qt保留小数点后两位
  11. 使用RMAN迁移文件系统数据库到ASM
  12. C#记事本的简单开发
  13. 利用ggseqlogo绘制seqlogo图
  14. Atitit office Ooxml excel标准的主要内容 目录 1.1. 物理存储 zip+文件夹包+xml 1 1.2. Package Structure 1 1.3. 内容部分 1
  15. 利用MDTools和Hydraw设计专业的液压阀块
  16. 三分钟带你快速了解网站开发的整个流程
  17. java 序列化理解_Java序列化的相关认知
  18. Mysql——DQL(查询语句语法、格式、举例)以及全部数据库源码,复制就可实现全部功能
  19. TomExam开源在线考试系统
  20. Elasticsearch:在华为大数据集群 从c80升级至651版本后,执行es命令修改配置失败

热门文章

  1. 2022上海最新建筑八大员(机械员)机考真题题库及答案
  2. C语言---会思考的三子棋
  3. 每日技巧分享:怎么用AI绘画?
  4. React Router 之 browserHistory
  5. Vj数据结构实验十二
  6. 大学,男生因为孤独而优秀,女生因为优秀而孤独……
  7. 颗粒化处理图片,科技感十足(当下最流行的图片处理方式之一)
  8. 2023最新SSM计算机毕业设计选题大全(附源码+LW)之java招生管理系统2ij21
  9. C#基础系列2:反射以及Attri…
  10. 关于奶茶店小程序开发价值和相关功能