Android RIL 本地代码(c/c++) 和 Java代码解析
2012-11-20 23:19
288 查看
Android系统源代码目录里面: hardware/ril 目录包含了所有有关于telephony的底层代码.
1.目录架构(20101215的git版本):
ril
|-- CleanSpec.mk
|-- include
| |-- telephony
| |-- ril_cdma_sms.h
//CDMA SMS
| |-- ril.h
//Android RIL 框架的一些接口和数据结构
|-- libril
| |-- Android.mk
| |-- MODULE_LICENSE_APACHE2
| |-- NOTICE
| |-- ril_commands.h
//RIL命令列表
| |-- ril.cpp
| |-- ril_event.cpp
| |-- ril_event.h
| |-- ril_unsol_commands.h
//RIL 主动上报信息列表
|-- reference-cdma-sms
| |-- Android.mk
| |-- reference-cdma-sms.c
| |-- reference-cdma-sms.h
|-- reference-ril
| |-- Android.mk
| |-- atchannel.h
//负责向modem读写数据
| |-- atchannel.c
| |-- at_tok.h
| |-- at_tok.c
| |-- misc.h
| |-- misc.c
| |-- MODULE_LICENSE_APACHE2
| |-- NOTICE
| |-- reference-ril.c
//主要负责与modem进行交互
|-- rild
|-- Android.mk
|-- MODULE_LICENSE_APACHE2
|-- NOTICE
|-- radiooptions.c
//调试时配置Modem参数
|-- rild.c
//RIL守护进程
其中include/telephony目录下面的ril.h文件,定义了104个如下的宏:
RIL_REQUEST_XXXX
这些宏代表客户进程向Android
telephony发送的命令,包括SIM卡相关的功能,打电话,发短信,网络信号查询等等。
2.目录hardware/ril/libril
本目录下代码负责与客户进程进行交互。在接收客户进程命令后,调用相应函数进行处理,然后将命令响应结果传回客户进程。在收到来自网络端的事件后,也传给客户进程。
v 文件ril_commands.h:列出了telephony可以接收的命令;每个命令对应的处理函数;以及命令响应的处理函数。
v 文件ril_unsol_commands.h:列出了telephony可以接收的事件类型;对每个事件的处理函数;以及WAKEType???
v 文件ril_event.h/cpp:处理与事件源(端口,modem等)相关的功能。ril_event_loop监视所有注册的事件源,当某事件源有数据到来时,相应事件源的回调函数被触发(firePending->
ev->func())
v 文件ril.cpp:
? RIL_register函数:打开监听端口,接收来自客户进程的命令请求(s_fdListen
=android_get_control_socket(SOCKET_NAME_RIL);),当与某客户进程连接建立时,调用listenCallback函数;创建一单独线程监视并处理所有事件源(通过ril_event_loop)
? listenCallback函数:当与客户进程连接建立时,此函数被调用。此函数接着调用 processCommandsCallback处理来自客户进程的命令请求
? processCommandsCallback函数:具体处理来自客户进程的命令请求。对每一个命令,ril_commands.h中都规定了对应的命令处理函数(dispatchXXX),processCommandsCallback会调用这个命令处理函数进行处理。
? dispatch系列函数:此函数接收来自客户进程的命令己相应参数,并调用onRequest进行处理。
? RIL_onUnsolicitedResponse函数:将来自网络端的事件封装(通过调用responseXXX)后传给客户进程。
? RIL_onRequestComplete函数:将命令的最终响应结构封装(通过调用responseXXX)后传给客户进程。
? response系列函数:对每一个命令,都规定了一个对应的response函数来处理命令的最终响应;对每一个网络端的事件,也规定了一个对应的response函数来处理此事件。response函数可被onUnsolicitedResponse或者onRequestComplete调用。
3. 目录hardware/ril/reference-ril分析:
本目录下代码主要负责与modem进行交互。
v 文件reference-ril.c:此文件核心是两个函数:onRequest和onUnsolicited
? onRequest
函数:在这个函数里,对每一个RIL_REQUEST_XXX请求,都转化成相应的ATcommand,发送给modem,然后睡眠等待。当收到此ATcommand的最终响应后,线程被唤醒,将响应传给客户进程(RIL_onRequestComplete-> sendResponse)。
? onUnsolicited函数:这个函数处理modem从网络端收到的各种事件,如网络信号变化,拨入的电话,收到短信等。然后将时间传给客户进程(RIL_onUnsolicitedResponse
-> sendResponse)
v 文件atchannel.c:负责向modem读写数据。其中,写数据(主要是ATcommand)功能运行在主线程中,读数据功能运行在一个单独的读线程中。
? at_send_command_full_nolock函数:运行在主线程里面。将一个ATcommand命令写入modem后进入睡眠状态(使用pthread_cond_wait或类似函数),直到modem读线程将其唤醒。唤醒后此函数获得了ATcommand的最终响应并返回。
? readerLoop函数:
运行在一个单独的读线程里面,负责从modem中读取数据。读到的数据可分为三种类型:网络端传入的事件;modem对当前ATcommand的部分响应;modem对当前AT command的全部响应。对第三种类型的数据(ATcommand的全部响应),读线程唤醒(pthread_cond_signal)睡眠状态的主线程。
第二部分,
Java代码
1.package简介:
Android中,telephony相关的java代码主要在下列目录中:
v frameworks/base/telephony/java/android/telephony
提供Android
telephony的公开接口,任何具有权限的第三方应用都可使用,如接口类TelephonyManager、SMSManager。
v frameworks/base/telephony/java/com/android/internal/telephony
v frameworks/base/services/java/com/android/server
提供一系列内部接口,目前第三方应用还不能使用。当前似乎只packages/apps/Phone能够使用.
v packages/apps/Phone
目录packages/apps/Phone是一个特殊应用,或者理解为一个平台内部进程。其他应用通过intent方式调用这个进程的服务。
2.详细介绍
v TelephonyManager(telephony/java/android/telephony/TelephonyManager.java)主要使用两个IBinder接口(AIDL接口)来访问telephony功能:
? ITelephony,
提供与telephony进行操作,交互的接口,在packages/apps/Phone中由PhoneInterfaceManager.java实现。
? ITelephonyRegistry,其提供一个通知机制,将一些底层状态或变更通知给电话服务的用户,如网络状态、信号强度等。它的服务实现在框架代码中,即:frameworks/base/services/java/com/android/server/TelephonyRegistry.java。
底层通知的来源是GSMPhone/CDMAPhone通过PhoneNotifier接口的实现者DefaultPhoneNotifier将具体的事件转化为函数调用,通知到TelephonyRegistry。TelephonyRegistry再通过两种方式通知用户:
§ Broadcast机制广播事件;
§ 通过服务用户在TelephonyRegistry.java中注册的IPhoneStateListener接口,实现回调机制。注册函数是用户可见的:
TelephonyManager.listen();
->TelephonyRegistry.listen(,IPhoneStateListener
callback,,);
v 接口PhoneNotifier定义电话事件的通知方法
v 类DefaultPhoneNotifier从PhoneNotifier派生而来。在其方法实现中,通过调用service
ITelephonyRegistry来发布电话事件。
v 接口Phone描述了对电话的所有操作接口。
PhoneBase直接从Phone派生而来。而另外两个类,CDMAPhone和GSMPhone,又从PhoneBase派生而来,分别代表对CDMA和GSM的操作。
v PhoneProxy也从Phone直接派生而来。当当前不需要区分具体是CDMA
Phone还是GSMPhone时,可使用PhoneProxy。
v PhoneFactory.java:
获取电话实例,如GSMPhone/CDMAPhone.
PhoneFactory.makeDefaultPhones();
->PhoneFactory.makeDefaultPhone();//注册相应的Phone实例
->Phone
phone = (Phone)PhoneFactory.getDefaultPhone();
v 接口IPhoneStateListener:
手机状态监听接口(AIDL),其服务实现在/frameworks/base/telephony/java/android/telephony/PhoneStateListener.java
v 接口CommandsInterface
描述了对电话的所有操作接口,如命令, 查询状态,以及电话事件监听等。
v 类BaseCommands是CommandsInterface的直接派生类,实现了电话事件的处理(发送message给对应的handler)。
v 类RIL又派生自BaseCommands。RIL负责实际实现CommandsInterface中的接口方法。RIL通过Socket和rild守护进程进行通讯。对于每一个命令接口方法,如acceptCall,或者状态查询,将它转换成对应的RIL_REQUEST_XXX,发送给rild。RIL中的几个类如下:
? RILRequest:代表一个电话服务命令请求;
? RIL.RILSender:负责处理命令的发送;
? RIL.RELReceiver:负责处理命令相应以及主动上报信息的接受;线程RILReceiver监听socket,当有数据上报时,读取该数据并处理。读取的数据有两种。
§ 电话事件,RIL_UNSOL_xxx,
RIL读取相应数据后,发送message给对应的handler(详见函数processUnsolicited);
§ 命令的异步响应。(详见函数processSolicited)
v RILConstants.java
: 定义了电话服务的具体命令。
v 抽象类Call代表一个call,有两个派生类CdmaCall和GsmCall。
第三部分,
流程分析
1.Outgoing Call流程。
v TwelveKeyDialer.java
|_onKeyUp()
|_placeCall()
v OutgoingCallBroadcaster.java,
|_onCreate()
|_sendOrderedBroadcast(broadcastIntent, PERMISSION,
new OutgoingCallReceiver(), null, Activity.RESULT_OK,
number,null);
|_ OutgoingCallReceiver
|_onReceive()
|_ doReceive()
|_context.startActivity(newIntent);
v InCallScreen.java
|_ onCreate()
|_onNewIntent()
|_placeCall()
v PhoneUtils.java
|_placeCall()
v GSMPhone.java,
|_dial()
v GsmCallTracker.java,
|_dial()
v RIL.java
|_dial()
|_ RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL,
result);
|...
|send(rr);
2. Incoming Call的流程:
v 创建GsmPhone时,mCT
= new GsmCallTracker(this);
v 创建GsmCallTracker时:
? cm.registerForCallStateChanged(this,
EVENT_CALL_STATE_CHANGE,null);
? mCallStateRegistrants.add(r);
v RIL中的RILReceiver线程首先读取从rild中传来的数据:
? processResponse()
? processUnsolicited()
v 对应于incoming
call,RIL收到RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED消息,触发mCallStateRegistrants中的所有记录。
v GsmCallTracker处理EVENT_CALL_STATE_CHANGE,调用pollCallsWhenSafe
v 函数pllCallsWhenSafe
处理:
§ lastRelevantPoll
= obtainMessage(EVENT_POLL_CALLS_RESULT);
§ cm.getCurrentCalls(lastRelevantPoll);
v RIL::getCurrentCalls
§ RILRequestrr
= RILRequest.obtain(RIL_REQUEST_GET_CURRENT_CALLS,result);
§ ...
§ send(rr);
v 接着RIL调用processSolicited处理RIL_REQUEST_GET_CURRENT_CALLS的返回结果
v GsmCallTracker的handleMessage被触发,处理事件EVENT_POLL_CALLS_RESULT,调用函数
handlePollCalls
v handlPollCalls
调用phone.notifyNewRingingConnection(newRinging);
v PhoneApp中创建CallNotifier
v CallNotifier注册:
§ registerForNewRingingConnection
->mNewRingingConnectionRegistrants.addUnique(h, what, obj);
第四部分,
参考文档
1.《Android系统原理及开发详解》
2. http:/www.meegozu.com/thread-391-1-1.html
3. http://www.netmite.com/android/mydroid/development/pdk/docs/telephony.html
4. http://wenku.baidu.com/view/8d57336aaf1ffc4ffe47ac75.html
5. http:/wenku.baidu.com/view/ca78fabef121dd36a32d8258.html
Android消息系统构成元素如下:
1. 消息队列
2. 发送消息
3. 读取消息
4. 消息分发
5. 消息循环线程
Android消息系统设计的类:
1. Looper:负责管理线程的消息队列和消息循环。
1) 构造一个MessageQueue实例;
2) 维护一个线程实例;
3) Looper一旦调用loop(),则会一直执行,直到遇到一个target=null的Message;
2. MessageQueue:以执行时间为序列的一个Message队列。以IdleHandler接口的函数queueIdle()表征MessageQueue状态是否为Idle.
3. Message:消息
1) 执行时间;
2) Handler(既:target);
3) Runnable;
4. Handler
:
1) 设置Message.target
(默认为 this);
2) 将Message实例加入MessageQueue中;
3) 处理消息
4) 发送消息
调用层次结构:
1. Handler通过snedMessage()等发送Message消息到MessageQueue;
2. MessageQueue通过enqueueMessage()将接收到的Message放入队列,相反则通过removeMessage将接收到的Message从队列中移除;
3. Looper通过loop()在MessageQueue队头取出Message,直到遇到target为null的Message
4.
Looper 将读取到的Message发送到Handler进行处理;
2.1.
标准Java接口
java.net.*下面提供了访问 HTTP 服务的基本功能。使用这部分接口的基本操作主要包括:
· 创建 URL 以及 URLConnection / HttpURLConnection 对象
· 设置连接参数
· 连接到服务器
· 向服务器写数据
· 从服务器读取数据
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.net.HttpURLConnection;
try {
// 创建一个 URL 对象
URL url = new URL(your_url);
// 创建一个 URL 连接,如果有代理的话可以指定一个代理。
URLConnection connection = url.openConnection(Proxy_yours);
// 对于 HTTP 连接可以直接转换成 HttpURLConnection 这样就可以使用一些 HTTP 连接特定的方法,
// 如 setRequestMethod() 等
//HttpURLConnection connection = (HttpURLConnection)url.openConnection(Proxy_yours);
// 在开始和服务器连接之前,可能需要设置一些网络参数
connection.setConnectTimeout(10000);
connection.addRequestProperty("User-Agent", "J2me/MIDP2.0");
// 连接到服务器
connection.connect();
// 往服务器写数据,数据会暂时被放到内存缓存区中
// 如果仅是一个简单的 HTTP GET,这一部分则可以省略
OutputStream outStream = connection.getOutputStream();
ObjectOutputStream objOutput = new ObjectOutputStream(outStream);
objOutput.writeObject(new String("this is a string..."));
objOutput.flush();
// 向服务器发送数据并获取应答
InputStream in = connection.getInputStream();
// 处理数据
...
} catch (Exception e) {
// 网络读写操作往往会产生一些异常,所以在具体编写网络应用时
// 最好捕捉每一个具体以采取相应措施
}
2.2. Apache接口
Apache HttpClient 是一个开源项目,弥补了 java.net.* 灵活性不足的缺点,为客户端的HTTP编程提供高效、最新、功能丰富的工具包支持。Android
平台引入了 Apache HttpClient 的同时还提供了对它的一些封装和扩展,例如设置缺省的HTTP超时和缓存大小等。早期的 Android 曾同时包括 Commons HttpClient (org.apache.commons.httpclient.*) 和 HttpComponents (org.apache.http.client.* ),不过当前版本 (1.5) 中开发者只能使用后者,也就是说类似以下的一些类:
使用这部分接口的基本操作与 java.net.* 基本类似,主要包括:
· 创建 HttpClient 以及 GetMethod / PostMethod, HttpRequest 等对象
· 设置连接参数
· 执行 HTTP 操作
· 处理服务器返回结果
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.params. HttpConnectionParams;
import org.apache.http.client.params. HttpClientParams;
try {
// 创建 HttpParams 以用来设置 HTTP 参数(这一部分不是必需的)
HttpParams params = new BasicHttpParams();
// 设置连接超时和 Socket 超时,以及 Socket 缓存大小
HttpConnectionParams.setConnectionTimeout(params, 20 * 1000);
HttpConnectionParams.setSoTimeout(params, 20 * 1000);
HttpConnectionParams.setSocketBufferSize(params, 8192);
// 设置重定向,缺省为 true
HttpClientParams.setRedirecting(params, true);
// 设置 user agent
HttpProtocolParams.setUserAgent(params, userAgent);
// 创建一个 HttpClient 实例 ,注意 HttpClient httpClient = new HttpClient();
是Commons HttpClient
// 中的用法,在 Android 1.5 中我们需要使用 Apache 的缺省实现 DefaultHttpClient
HttpClient httpClient = new DefaultHttpClient(params);
// 创建 HttpGet 方法,该方法会自动处理 URL 地址的重定向
HttpGet httpGet = new HttpGet ("http://www.test_test.com/");
HttpResponse response = client.execute(httpGet);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK)
{
// 错误处理,例如可以在该请求正常结束前将其中断
httpGet.abort();
}
// 读取更多信息
Header[] headers = response.getHeaders();
HttpEntity entity = response.getEntity();
Header header = response.getFirstHeader("Content-Type");
} catch (Exception ee) {
//
} finally {
// 释放连接
client.getConnectionManager().shutdown();
}
2.3.
Android接口
android.net.* 实际上是通过对 Apache 的 HttpClient 的封装来实现的一个 HTTP 编程接口,同时还提供了 HTTP
请求队列管理, 以及 HTTP 连接池管理,以提高并发请求情况下(如转载网页时)的处理效率,除此之外还有网络状态监视等接口。
以下是一个通过 AndroidHttpClient 访问服务器的最简例子:
import import android.net.http.AndroidHttpClient;
try {
AndroidHttpClient client = AndroidHttpClient.newInstance(“your_user_agent”);
// 创建 HttpGet 方法,该方法会自动处理 URL 地址的重定向
HttpGet httpGet = new HttpGet ("http://www.test_test.com/");
HttpResponse response = client.execute(httpGet);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK)
{
// 错误处理
}
// 关闭连接
client.close();
} catch (Exception ee) {
//
}
另外当我们的应用需要同时从不同的主机获取数目不等的数据,并且仅关心数据的完整性而不关心其先后顺序时,也可以使用这部分的接口。典型用例就是
android.webkit 在转载网页和下载网页资源时,具体可参考 android.webkit.* 中的相关实现。
1.目录架构(20101215的git版本):
ril
|-- CleanSpec.mk
|-- include
| |-- telephony
| |-- ril_cdma_sms.h
//CDMA SMS
| |-- ril.h
//Android RIL 框架的一些接口和数据结构
|-- libril
| |-- Android.mk
| |-- MODULE_LICENSE_APACHE2
| |-- NOTICE
| |-- ril_commands.h
//RIL命令列表
| |-- ril.cpp
| |-- ril_event.cpp
| |-- ril_event.h
| |-- ril_unsol_commands.h
//RIL 主动上报信息列表
|-- reference-cdma-sms
| |-- Android.mk
| |-- reference-cdma-sms.c
| |-- reference-cdma-sms.h
|-- reference-ril
| |-- Android.mk
| |-- atchannel.h
//负责向modem读写数据
| |-- atchannel.c
| |-- at_tok.h
| |-- at_tok.c
| |-- misc.h
| |-- misc.c
| |-- MODULE_LICENSE_APACHE2
| |-- NOTICE
| |-- reference-ril.c
//主要负责与modem进行交互
|-- rild
|-- Android.mk
|-- MODULE_LICENSE_APACHE2
|-- NOTICE
|-- radiooptions.c
//调试时配置Modem参数
|-- rild.c
//RIL守护进程
其中include/telephony目录下面的ril.h文件,定义了104个如下的宏:
RIL_REQUEST_XXXX
这些宏代表客户进程向Android
telephony发送的命令,包括SIM卡相关的功能,打电话,发短信,网络信号查询等等。
2.目录hardware/ril/libril
本目录下代码负责与客户进程进行交互。在接收客户进程命令后,调用相应函数进行处理,然后将命令响应结果传回客户进程。在收到来自网络端的事件后,也传给客户进程。
v 文件ril_commands.h:列出了telephony可以接收的命令;每个命令对应的处理函数;以及命令响应的处理函数。
v 文件ril_unsol_commands.h:列出了telephony可以接收的事件类型;对每个事件的处理函数;以及WAKEType???
v 文件ril_event.h/cpp:处理与事件源(端口,modem等)相关的功能。ril_event_loop监视所有注册的事件源,当某事件源有数据到来时,相应事件源的回调函数被触发(firePending->
ev->func())
v 文件ril.cpp:
? RIL_register函数:打开监听端口,接收来自客户进程的命令请求(s_fdListen
=android_get_control_socket(SOCKET_NAME_RIL);),当与某客户进程连接建立时,调用listenCallback函数;创建一单独线程监视并处理所有事件源(通过ril_event_loop)
? listenCallback函数:当与客户进程连接建立时,此函数被调用。此函数接着调用 processCommandsCallback处理来自客户进程的命令请求
? processCommandsCallback函数:具体处理来自客户进程的命令请求。对每一个命令,ril_commands.h中都规定了对应的命令处理函数(dispatchXXX),processCommandsCallback会调用这个命令处理函数进行处理。
? dispatch系列函数:此函数接收来自客户进程的命令己相应参数,并调用onRequest进行处理。
? RIL_onUnsolicitedResponse函数:将来自网络端的事件封装(通过调用responseXXX)后传给客户进程。
? RIL_onRequestComplete函数:将命令的最终响应结构封装(通过调用responseXXX)后传给客户进程。
? response系列函数:对每一个命令,都规定了一个对应的response函数来处理命令的最终响应;对每一个网络端的事件,也规定了一个对应的response函数来处理此事件。response函数可被onUnsolicitedResponse或者onRequestComplete调用。
3. 目录hardware/ril/reference-ril分析:
本目录下代码主要负责与modem进行交互。
v 文件reference-ril.c:此文件核心是两个函数:onRequest和onUnsolicited
? onRequest
函数:在这个函数里,对每一个RIL_REQUEST_XXX请求,都转化成相应的ATcommand,发送给modem,然后睡眠等待。当收到此ATcommand的最终响应后,线程被唤醒,将响应传给客户进程(RIL_onRequestComplete-> sendResponse)。
? onUnsolicited函数:这个函数处理modem从网络端收到的各种事件,如网络信号变化,拨入的电话,收到短信等。然后将时间传给客户进程(RIL_onUnsolicitedResponse
-> sendResponse)
v 文件atchannel.c:负责向modem读写数据。其中,写数据(主要是ATcommand)功能运行在主线程中,读数据功能运行在一个单独的读线程中。
? at_send_command_full_nolock函数:运行在主线程里面。将一个ATcommand命令写入modem后进入睡眠状态(使用pthread_cond_wait或类似函数),直到modem读线程将其唤醒。唤醒后此函数获得了ATcommand的最终响应并返回。
? readerLoop函数:
运行在一个单独的读线程里面,负责从modem中读取数据。读到的数据可分为三种类型:网络端传入的事件;modem对当前ATcommand的部分响应;modem对当前AT command的全部响应。对第三种类型的数据(ATcommand的全部响应),读线程唤醒(pthread_cond_signal)睡眠状态的主线程。
第二部分,
Java代码
1.package简介:
Android中,telephony相关的java代码主要在下列目录中:
v frameworks/base/telephony/java/android/telephony
提供Android
telephony的公开接口,任何具有权限的第三方应用都可使用,如接口类TelephonyManager、SMSManager。
v frameworks/base/telephony/java/com/android/internal/telephony
v frameworks/base/services/java/com/android/server
提供一系列内部接口,目前第三方应用还不能使用。当前似乎只packages/apps/Phone能够使用.
v packages/apps/Phone
目录packages/apps/Phone是一个特殊应用,或者理解为一个平台内部进程。其他应用通过intent方式调用这个进程的服务。
2.详细介绍
v TelephonyManager(telephony/java/android/telephony/TelephonyManager.java)主要使用两个IBinder接口(AIDL接口)来访问telephony功能:
? ITelephony,
提供与telephony进行操作,交互的接口,在packages/apps/Phone中由PhoneInterfaceManager.java实现。
? ITelephonyRegistry,其提供一个通知机制,将一些底层状态或变更通知给电话服务的用户,如网络状态、信号强度等。它的服务实现在框架代码中,即:frameworks/base/services/java/com/android/server/TelephonyRegistry.java。
底层通知的来源是GSMPhone/CDMAPhone通过PhoneNotifier接口的实现者DefaultPhoneNotifier将具体的事件转化为函数调用,通知到TelephonyRegistry。TelephonyRegistry再通过两种方式通知用户:
§ Broadcast机制广播事件;
§ 通过服务用户在TelephonyRegistry.java中注册的IPhoneStateListener接口,实现回调机制。注册函数是用户可见的:
TelephonyManager.listen();
->TelephonyRegistry.listen(,IPhoneStateListener
callback,,);
v 接口PhoneNotifier定义电话事件的通知方法
v 类DefaultPhoneNotifier从PhoneNotifier派生而来。在其方法实现中,通过调用service
ITelephonyRegistry来发布电话事件。
v 接口Phone描述了对电话的所有操作接口。
PhoneBase直接从Phone派生而来。而另外两个类,CDMAPhone和GSMPhone,又从PhoneBase派生而来,分别代表对CDMA和GSM的操作。
v PhoneProxy也从Phone直接派生而来。当当前不需要区分具体是CDMA
Phone还是GSMPhone时,可使用PhoneProxy。
备注: |
在PhoneApp创建时, sPhoneNotifier = new DefaultPhoneNotifier(); ... sCommandsInterface = newRIL(context, networkMode, cdmaSubscription); 然后根据当前phone是cdma还是gsm,创建对应的phone,如 sProxyPhone = newPhoneProxy(new GSMPhone(context,sCommandsInterface, sPhoneNotifier)); |
获取电话实例,如GSMPhone/CDMAPhone.
PhoneFactory.makeDefaultPhones();
->PhoneFactory.makeDefaultPhone();//注册相应的Phone实例
->Phone
phone = (Phone)PhoneFactory.getDefaultPhone();
v 接口IPhoneStateListener:
手机状态监听接口(AIDL),其服务实现在/frameworks/base/telephony/java/android/telephony/PhoneStateListener.java
v 接口CommandsInterface
描述了对电话的所有操作接口,如命令, 查询状态,以及电话事件监听等。
v 类BaseCommands是CommandsInterface的直接派生类,实现了电话事件的处理(发送message给对应的handler)。
v 类RIL又派生自BaseCommands。RIL负责实际实现CommandsInterface中的接口方法。RIL通过Socket和rild守护进程进行通讯。对于每一个命令接口方法,如acceptCall,或者状态查询,将它转换成对应的RIL_REQUEST_XXX,发送给rild。RIL中的几个类如下:
? RILRequest:代表一个电话服务命令请求;
? RIL.RILSender:负责处理命令的发送;
? RIL.RELReceiver:负责处理命令相应以及主动上报信息的接受;线程RILReceiver监听socket,当有数据上报时,读取该数据并处理。读取的数据有两种。
§ 电话事件,RIL_UNSOL_xxx,
RIL读取相应数据后,发送message给对应的handler(详见函数processUnsolicited);
§ 命令的异步响应。(详见函数processSolicited)
v RILConstants.java
: 定义了电话服务的具体命令。
v 抽象类Call代表一个call,有两个派生类CdmaCall和GsmCall。
第三部分,
流程分析
1.Outgoing Call流程。
v TwelveKeyDialer.java
|_onKeyUp()
|_placeCall()
v OutgoingCallBroadcaster.java,
|_onCreate()
|_sendOrderedBroadcast(broadcastIntent, PERMISSION,
new OutgoingCallReceiver(), null, Activity.RESULT_OK,
number,null);
|_ OutgoingCallReceiver
|_onReceive()
|_ doReceive()
|_context.startActivity(newIntent);
v InCallScreen.java
|_ onCreate()
|_onNewIntent()
|_placeCall()
v PhoneUtils.java
|_placeCall()
v GSMPhone.java,
|_dial()
v GsmCallTracker.java,
|_dial()
v RIL.java
|_dial()
|_ RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL,
result);
|...
|send(rr);
2. Incoming Call的流程:
v 创建GsmPhone时,mCT
= new GsmCallTracker(this);
v 创建GsmCallTracker时:
? cm.registerForCallStateChanged(this,
EVENT_CALL_STATE_CHANGE,null);
? mCallStateRegistrants.add(r);
v RIL中的RILReceiver线程首先读取从rild中传来的数据:
? processResponse()
? processUnsolicited()
v 对应于incoming
call,RIL收到RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED消息,触发mCallStateRegistrants中的所有记录。
v GsmCallTracker处理EVENT_CALL_STATE_CHANGE,调用pollCallsWhenSafe
v 函数pllCallsWhenSafe
处理:
§ lastRelevantPoll
= obtainMessage(EVENT_POLL_CALLS_RESULT);
§ cm.getCurrentCalls(lastRelevantPoll);
v RIL::getCurrentCalls
§ RILRequestrr
= RILRequest.obtain(RIL_REQUEST_GET_CURRENT_CALLS,result);
§ ...
§ send(rr);
v 接着RIL调用processSolicited处理RIL_REQUEST_GET_CURRENT_CALLS的返回结果
v GsmCallTracker的handleMessage被触发,处理事件EVENT_POLL_CALLS_RESULT,调用函数
handlePollCalls
v handlPollCalls
调用phone.notifyNewRingingConnection(newRinging);
v PhoneApp中创建CallNotifier
v CallNotifier注册:
§ registerForNewRingingConnection
->mNewRingingConnectionRegistrants.addUnique(h, what, obj);
第四部分,
参考文档
1.《Android系统原理及开发详解》
2. http:/www.meegozu.com/thread-391-1-1.html
3. http://www.netmite.com/android/mydroid/development/pdk/docs/telephony.html
4. http://wenku.baidu.com/view/8d57336aaf1ffc4ffe47ac75.html
5. http:/wenku.baidu.com/view/ca78fabef121dd36a32d8258.html
Android消息系统构成元素如下:
1. 消息队列
2. 发送消息
3. 读取消息
4. 消息分发
5. 消息循环线程
Android消息系统设计的类:
1. Looper:负责管理线程的消息队列和消息循环。
1) 构造一个MessageQueue实例;
2) 维护一个线程实例;
3) Looper一旦调用loop(),则会一直执行,直到遇到一个target=null的Message;
2. MessageQueue:以执行时间为序列的一个Message队列。以IdleHandler接口的函数queueIdle()表征MessageQueue状态是否为Idle.
3. Message:消息
1) 执行时间;
2) Handler(既:target);
3) Runnable;
4. Handler
:
1) 设置Message.target
(默认为 this);
2) 将Message实例加入MessageQueue中;
3) 处理消息
4) 发送消息
调用层次结构:
1. Handler通过snedMessage()等发送Message消息到MessageQueue;
2. MessageQueue通过enqueueMessage()将接收到的Message放入队列,相反则通过removeMessage将接收到的Message从队列中移除;
3. Looper通过loop()在MessageQueue队头取出Message,直到遇到target为null的Message
4.
Looper 将读取到的Message发送到Handler进行处理;
2.1.
标准Java接口
java.net.*下面提供了访问 HTTP 服务的基本功能。使用这部分接口的基本操作主要包括:
· 创建 URL 以及 URLConnection / HttpURLConnection 对象
· 设置连接参数
· 连接到服务器
· 向服务器写数据
· 从服务器读取数据
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.net.HttpURLConnection;
try {
// 创建一个 URL 对象
URL url = new URL(your_url);
// 创建一个 URL 连接,如果有代理的话可以指定一个代理。
URLConnection connection = url.openConnection(Proxy_yours);
// 对于 HTTP 连接可以直接转换成 HttpURLConnection 这样就可以使用一些 HTTP 连接特定的方法,
// 如 setRequestMethod() 等
//HttpURLConnection connection = (HttpURLConnection)url.openConnection(Proxy_yours);
// 在开始和服务器连接之前,可能需要设置一些网络参数
connection.setConnectTimeout(10000);
connection.addRequestProperty("User-Agent", "J2me/MIDP2.0");
// 连接到服务器
connection.connect();
// 往服务器写数据,数据会暂时被放到内存缓存区中
// 如果仅是一个简单的 HTTP GET,这一部分则可以省略
OutputStream outStream = connection.getOutputStream();
ObjectOutputStream objOutput = new ObjectOutputStream(outStream);
objOutput.writeObject(new String("this is a string..."));
objOutput.flush();
// 向服务器发送数据并获取应答
InputStream in = connection.getInputStream();
// 处理数据
...
} catch (Exception e) {
// 网络读写操作往往会产生一些异常,所以在具体编写网络应用时
// 最好捕捉每一个具体以采取相应措施
}
2.2. Apache接口
Apache HttpClient 是一个开源项目,弥补了 java.net.* 灵活性不足的缺点,为客户端的HTTP编程提供高效、最新、功能丰富的工具包支持。Android
平台引入了 Apache HttpClient 的同时还提供了对它的一些封装和扩展,例如设置缺省的HTTP超时和缓存大小等。早期的 Android 曾同时包括 Commons HttpClient (org.apache.commons.httpclient.*) 和 HttpComponents (org.apache.http.client.* ),不过当前版本 (1.5) 中开发者只能使用后者,也就是说类似以下的一些类:
使用这部分接口的基本操作与 java.net.* 基本类似,主要包括:
· 创建 HttpClient 以及 GetMethod / PostMethod, HttpRequest 等对象
· 设置连接参数
· 执行 HTTP 操作
· 处理服务器返回结果
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.params. HttpConnectionParams;
import org.apache.http.client.params. HttpClientParams;
try {
// 创建 HttpParams 以用来设置 HTTP 参数(这一部分不是必需的)
HttpParams params = new BasicHttpParams();
// 设置连接超时和 Socket 超时,以及 Socket 缓存大小
HttpConnectionParams.setConnectionTimeout(params, 20 * 1000);
HttpConnectionParams.setSoTimeout(params, 20 * 1000);
HttpConnectionParams.setSocketBufferSize(params, 8192);
// 设置重定向,缺省为 true
HttpClientParams.setRedirecting(params, true);
// 设置 user agent
HttpProtocolParams.setUserAgent(params, userAgent);
// 创建一个 HttpClient 实例 ,注意 HttpClient httpClient = new HttpClient();
是Commons HttpClient
// 中的用法,在 Android 1.5 中我们需要使用 Apache 的缺省实现 DefaultHttpClient
HttpClient httpClient = new DefaultHttpClient(params);
// 创建 HttpGet 方法,该方法会自动处理 URL 地址的重定向
HttpGet httpGet = new HttpGet ("http://www.test_test.com/");
HttpResponse response = client.execute(httpGet);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK)
{
// 错误处理,例如可以在该请求正常结束前将其中断
httpGet.abort();
}
// 读取更多信息
Header[] headers = response.getHeaders();
HttpEntity entity = response.getEntity();
Header header = response.getFirstHeader("Content-Type");
} catch (Exception ee) {
//
} finally {
// 释放连接
client.getConnectionManager().shutdown();
}
2.3.
Android接口
android.net.* 实际上是通过对 Apache 的 HttpClient 的封装来实现的一个 HTTP 编程接口,同时还提供了 HTTP
请求队列管理, 以及 HTTP 连接池管理,以提高并发请求情况下(如转载网页时)的处理效率,除此之外还有网络状态监视等接口。
以下是一个通过 AndroidHttpClient 访问服务器的最简例子:
import import android.net.http.AndroidHttpClient;
try {
AndroidHttpClient client = AndroidHttpClient.newInstance(“your_user_agent”);
// 创建 HttpGet 方法,该方法会自动处理 URL 地址的重定向
HttpGet httpGet = new HttpGet ("http://www.test_test.com/");
HttpResponse response = client.execute(httpGet);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK)
{
// 错误处理
}
// 关闭连接
client.close();
} catch (Exception ee) {
//
}
另外当我们的应用需要同时从不同的主机获取数目不等的数据,并且仅关心数据的完整性而不关心其先后顺序时,也可以使用这部分的接口。典型用例就是
android.webkit 在转载网页和下载网页资源时,具体可参考 android.webkit.* 中的相关实现。
相关文章推荐
- Android中JNI高级应用 - 本地C代码中创建Java对象及本地JNI对象的保存
- 不依赖第三方jar包的通过java sax解析本地xml文件的实例代码
- Android中JNI高级应用 - 本地C代码中创建Java对象及本地JNI对象的保存
- Android中JNI高级应用 - 本地C代码中创建Java对象及本地JNI对象的保存
- 不依赖第三方jar包的通过java sax解析本地xml文件的实例代码
- Android ril原生代码(C/C++)和java代码部分分析
- android webview 调用本地java代码失败解决方法
- Android中JNI高级应用 - 本地C代码中创建Java对象及本地JNI对象的保存
- 深入了解android平台的jni---本地多线程调用java代码
- [置顶] Android开发之下载服务器上的一张图片到本地java代码实现HttpURLConnection
- Android中WebView载入本地HTML代码并实现Java与JavaScript交互
- 深入了解android平台的jni---本地多线程调用java代码
- Android中JNI高级应用 - 本地C代码中创建Java对象及本地JNI对象的保存 .
- Android中WebView载入本地HTML代码并实现Java与JavaScript交互的例子
- xml读取.sax解析方式 使用于Android 和java中 ,代码是java的, 在Android中只需要 区别监听类中的参数(localName,qName),相反的,java中使用qname 为节点的名称;
- 深入了解android平台的jni---本地多线程调用java代码
- Android RIL 原生代码(c/c++) 和 java代码部分分析
- 深入了解android平台的jni的原理以及本地多线程调用java代码
- Android RIL 原生代码(c/c++) 和 java代码部分分析
- 深入了解android平台的jni---本地多线程调用java代码