- 浏览: 68033 次
- 性别:
- 来自: 长沙
文章分类
最新评论
-
sys1121:
sys1121 写道为什么我这样获取,img图片没变呢..调试 ...
Android WebView调用Js设置byte[]给Img src -
sys1121:
为什么我这样获取,img图片没变呢..调试发现已经调用JS方法 ...
Android WebView调用Js设置byte[]给Img src -
luciferdevil:
iwangxiaodong 写道这样会不会更简单(更多WebV ...
Android WebView调用Js设置byte[]给Img src -
iwangxiaodong:
这样会不会更简单(更多WebView技巧):webview.l ...
Android WebView调用Js设置byte[]给Img src
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函数:
public void
dial (String address, int clirMode, Message result)
{
RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);
rr.mp.writeString(address);
rr.mp.writeInt(clirMode);
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
send(rr);
}
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的形式存在.
typedef struct RequestInfo {
int32_t token; //this is not RIL_Token
CommandInfo *pCI;
struct RequestInfo *p_next;
char cancelled;
char local; // responses to local commands do not go back to command process
} RequestInfo;
这里的pRI就是一个RequestInfo结构指针, 从socket过来的数据流, 前面提到是Parcel处理过的序列化字节流, 这里会通过反序列化的方法提取出来. 最前面的是request号, 以及token域(request的递增序列号). 我们更关注这个request号, 前面提到, 上层和rild之间, 这个号是统一的. 它的定义是一个包含ril_commands.h的枚举, 在ril.cpp中
static CommandInfo s_commands[] = {
#include "ril_commands.h"
};
pRI直接访问这个数组, 来获取自己的pCI.
这是一个CommandInfo结构:
typedef struct {
int requestNumber;
void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);
int(*responseFunction) (Parcel &p, void *response, size_t responselen);
} 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的详细流程,就分析完毕了.
上文说到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函数:
public void
dial (String address, int clirMode, Message result)
{
RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);
rr.mp.writeString(address);
rr.mp.writeInt(clirMode);
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
send(rr);
}
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的形式存在.
typedef struct RequestInfo {
int32_t token; //this is not RIL_Token
CommandInfo *pCI;
struct RequestInfo *p_next;
char cancelled;
char local; // responses to local commands do not go back to command process
} RequestInfo;
这里的pRI就是一个RequestInfo结构指针, 从socket过来的数据流, 前面提到是Parcel处理过的序列化字节流, 这里会通过反序列化的方法提取出来. 最前面的是request号, 以及token域(request的递增序列号). 我们更关注这个request号, 前面提到, 上层和rild之间, 这个号是统一的. 它的定义是一个包含ril_commands.h的枚举, 在ril.cpp中
static CommandInfo s_commands[] = {
#include "ril_commands.h"
};
pRI直接访问这个数组, 来获取自己的pCI.
这是一个CommandInfo结构:
typedef struct {
int requestNumber;
void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);
int(*responseFunction) (Parcel &p, void *response, size_t responselen);
} 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的详细流程,就分析完毕了.
发表评论
-
Ext.field.DatePicker汉化
2012-12-08 17:55 1936代码片段: // 放到Ext.application的la ... -
公司Augreal项目构架设计
2012-11-17 12:10 1023最近,公司接了一个移动应用方面的项目Augreal,经 ... -
Android WebView调用Js设置byte[]给Img src
2012-09-16 21:18 3463WebView与JS的相互调用就不在这里罗嗦了, 这里只说 ... -
Android Paint类介绍
2012-09-04 17:25 995/** * Paint类介绍 ... -
Android系统搜索对话框(浮动搜索框)的使用
2012-08-05 11:46 1240当您需要在您的应用程序中提供搜索服务时,您第一个想到的是您的搜 ... -
简述Android触摸屏手势识别
2012-07-18 22:59 1103简述Android触摸屏手势识别 很多时候,利用触摸屏的Fl ... -
Android 利用缓存机制实现文件下载
2012-06-21 13:54 1654在下载文件或者在线浏览文件时,或者为了保证文件下载的正确性,需 ... -
Android 下网络抓包方法 使用tcpdump
2012-04-23 16:13 1156抓包需要tcpdump以及Root权限,tcpdump在本文后 ... -
使用ProGuard遇到“conversion to Dalvik format failed with error 1”错误的解决办法
2011-12-28 10:20 941ProGuard 是 Android 代码混淆工具,对于程序员 ... -
Microlog4Android使用
2011-11-07 19:00 21591. Add the following static var ... -
LinearLayout上onFling事件失效问题
2011-10-11 09:56 40631. 写一个类,实现OnGestureListener, O ... -
实时文件夹
2011-10-03 23:06 707实时文件夹 实时文件夹是一种用来显示由某个ContentP ... -
Android Supporting Multiple Screens
2011-05-14 00:03 979Android被设计为能运行在不同尺寸、不同像素的多种设备的系 ... -
Android处理多种屏幕尺寸
2011-05-13 14:17 15121 默认设置 如果应用程序针对android1.5或更低版本 ... -
Android各种屏幕尺寸
2011-05-13 03:12 1466多分辨率支持 在 ... -
Android处适应布局
2011-05-13 00:48 8931、使用高分辨率[high density display ( ... -
Eclipse将so文件打包到APK中
2011-05-09 16:13 3088使用Eclipse build APK文件,只要将so文件放在 ... -
Android NDK 编程环境搭建
2011-04-28 00:17 7561. 下载Android 1.5 NDK, Release 1 ... -
Android GSM驱动模块-response流程
2011-04-26 15:00 1239前文对request的分析, 终止在了at_send_comm ... -
Android GSM驱动模块-基本架构及初始化
2011-04-26 14:58 1023Android的RIL驱动模块, ...
相关推荐
首次使用Yapi的时候,需要安装cross-request插件,请求是通过本地的cross-request插件发送出去的。解压该压缩包-->Google Chrome-->扩展程序-->加载已解压的扩展程序-->选中cross-request文件夹就可以啦
最新cross-request3.0插件下载,下载完成后解压,在Chrome中选择加载已解压的插件进行安装。
cross-request 3.1
【标题】"cross-request3.0谷歌插件.zip"是一个包含最新版本的cross-request3.0插件的压缩包,该插件专为谷歌浏览器(Chrome)设计。它旨在提升开发人员在API测试和调试过程中的效率,尤其与YApi接口管理工具配合...
Yapi3.3 Chrome 插件 cross-request3.3
cross-request3.0.0插件,支持chrome、火狐等浏览器,下载解压后,通过加载已解压扩张程序添加即可
instrumented-restful-fast-request-pro-2023.1.7.1 可用版,亲测,可用。
cross-request(YApi跨域请求),用于可视化接口管理平台YApi测试后台接口的,用Chrome浏览器在YApi上测后台接口必须先安装这个扩展程序才行
YAPI插件,用于实现跨域,浏览器共享cookies
使用YApi,在线调取接口时,需要在谷歌浏览器安装插件。解压之后,将cross-request.crx文件拖到谷歌浏览器的扩展程序页面,确认即可。
cross-request YApi 跨域请求
jmeter压测grpc接口需要的jar包
最新cross-request3.0插件下载,下载完成后解压,在Chrome中选择加载已解压的插件进行安装。
### GSM-08-移动被叫流程详解 #### 8.1 概述 移动被叫流程是指在GSM网络中,当一个移动终端(MS)接收到呼叫请求时所经历的一系列步骤。这一过程涉及多个网络实体之间的交互,包括移动交换中心(MSC)、基站控制器(BSC...
find-pull-request Intellij plugin that jumps to the pull request page (or GitHub commits page) How to use Select/Choose a line Right click Click "Find Pull Request" Open the pull request page ...
谷歌插件,前端开发神器
cross-request3.1.zip
cross-request插件
`laravel-middleware-request-id` 中间件的工作流程如下: 1. **生成 Request ID**:在请求进入应用时,中间件会生成一个唯一的 ID(通常是一个 UUID),并将其存储在一个全局可访问的地方,如 `request` 对象的...
在Laravel框架中,`laravel-request-id`是一个非常实用的中间件,它主要用于跟踪和记录请求ID。这个中间件帮助开发者在分布式系统或者微服务环境中更好地追踪和调试请求,尤其是在处理日志和监控时,可以更有效地...