原文:http://www.2cto.com/kf/201201/116969.html 作者 liujian885
Android 的 call setting 是用来设定与 simcard 相关的一些内容的应用程序,如网络,PIN等等,算是AP层。这里就选择其中一个项从源代码读下去直到底层,看看大概的结构和流程。
在 Android 主菜单中选择 setting->call setting->additional call setting->caller ID,会弹出来一个对话框来选择,这个项是用来设定在用电话本拨打电话的时候是否显示对方的电话号码。这里就以这个设定为例来一步步读下去。
AP层:
call setting 的代码和资源都是放在/packages/apps/Phone 的目录下。其实 Android 的 setting AP 相关的资源都是放在/packages/apps/setting 下的。但是看 /packages/apps/Settings/res/xml/settings.xml 中:
<PreferenceScreen android:key="call_settings" android:title="@string/call_settings_title" android:summary="@string/call_settings_summary">
<intent android:action="android.intent.action.MAIN" android:targetPackage="com.android.phone" android:targetClass="com.android.phone.CallFeaturesSetting" />
</PreferenceScreen>
从这里就可以知道,android 的call setting 的包指向了 phone,并且知道了入口是 CallFeaturesSetting 类。
在/packages/apps/Phone/res/call_featrue_setting.xml 中有call setting 的界面布局:
<PreferenceScreen android:key="button_more_expand_key" android:title="@string/labelMore" android:persistent="false">
...
<ListPreference android:key="button_clir_key" ... />
这个button_clir_key就是对应的 caller ID 的那个按钮,从这里可以看出是一个ListPreference,点击会出现一个列表框。里面出现的内容布局都在这里定义。相关的xml档都在/packages/apps/Phone/res 下。
接着就是AP的代码了,在/packages/apps/Phone/src 下。前面说了入口是 CallFeaturesSetting:
public class CallFeaturesSetting extends PreferenceActivity {
…
private PreferenceScreen mSubMenuFDNSettings;
private ListPreference mButtonCLIR;
private CheckBoxPreference mButtonCW;
...
protected void onCreate(…) {
…
PreferenceScreen prefSet = getPreferenceScreen();
mButtonCLIR = (ListPreference) prefSet.findPreference(BUTTON_CLIR_KEY);
...
定义的 mButtonCLIR 就是对应caller ID 的变量。在onCreate中初始化,用findPreference来找到XML中对应的资源。
然后是对这个按钮的响应代码:
public boolean onPreferenceChange(Preference preference, Object objValue){
if (preference == mButtonCLIR) {
handleCLIRClickRequest(mButtonCLIR.findIndexOfValue((String) objValue));
...
}
private void handleCLIRClickRequest(int i) {
…
mPhone.setOutgoingCallerIdDisplay(i,Message.obtain(mSetOptionComplete, EVENT_CLIR_EXECUTED));
}
可以看到最终是调用 mPhone.setOutgoingCallerIdDisplay 来完成真正的设定。这个 mPhone 是
com.android.internal.telephony.Phone,mPhone 的调用就进入 framework 层了。
Framework层:
相关的代码在/frameworks/base/telephony/java 中。先从 Phone 开始,这是一个 interface,真正实现是在
com.android.internal.telephony.gsm.GSMPhone 中:
public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, Message onComplete) {
mCM.setCLIR(commandInterfaceCLIRMode,
h.obtainMessage(EVENT_SET_CLIR_COMPLETE, commandInterfaceCLIRMode, 0, onComplete));
}
这里是调用 mCM.setCLIR。mCM 是一个 CommandsInterface,真正实现是在 RIL.java 文件中。这下到了framework中
真正办实事的地方。同样是在com.android.internal.telephony.gsm 下,RIL类中:
public void setCLIR(int clirMode, Message result)
{
RILRequest rr = RILRequest.obtain(RIL_REQUEST_SET_CLIR, result);
rr.mp.writeInt(1);
rr.mp.writeInt(clirMode);
...
send(rr);
}
这里顺便说一下,RIL与底层是用socket来通信。所以send(rr)最终是把RIL_REQUEST_SET_CLIR发给底层,底层会有一个守护
进程来接收framework层的信息,并且把结果传达上去。继续看下send的代码:
private void send(RILRequest rr)
{
Message msg;
msg = mSender.obtainMessage(EVENT_SEND, rr);
acquireWakeLock();
msg.sendToTarget();
}
是发一个EVENT_SEND的message给系统。那么找这个 handle 这个 message 的地方,还是同一个文件里,RIL.RILSender类
里面的handleMessage:
public void handleMessage(Message msg)
{
RILRequest rr = (RILRequest)(msg.obj);
switch (msg.what) {
case EVENT_SEND:
LocalSocket s;
s = mSocket;
...
byte[] data;
data = rr.mp.marshall();
rr.mp.recycle();
rr.mp = null;
// parcel length in big endian
dataLength[0] = dataLength[1] = 0;
dataLength[2] = (byte)((data.length >> 8) & 0xff);
dataLength[3] = (byte)((data.length) & 0xff);
s.getOutputStream().write(dataLength);
s.getOutputStream().write(data);
...
这里的mSocket 在就是与底层沟通的socket,有兴趣可以看看相关代码。
到这里 framework 层也算告一段落,在下去就是底层的C部分了。放在下一篇文章在写吧。
---------------------------------------------
看下篇之前,请先读这三篇文章:
Android GSM驱动模块(rild)详细分析(一)基本架构及初始化http://www.2cto.com/kf/201201/116966.html
Android GSM驱动模块(rild)详细分析(二)request流程http://www.2cto.com/kf/201201/116967.html
Android GSM驱动模块(rild)详细分析(三)response流程http://www.2cto.com/kf/201201/116968.html
这几篇分析了android 的 GSM 也就是 RIL 部分的驱动。我这篇文章也就是建立在这个基础上的。
上篇http://www.2cto.com/kf/201201/116969.html最后说到 framework 最后通过 socket 给底层发信息来沟通,有一个守护进程来接收信息,这个进程就是 rild。具体的代码在/hardware/ril 下面。代码结构和流程的介绍上面3篇文章都已说明,这里只是通过call setting 中 caller ID 的设置来追踪代码的完整流程。
request:
framework 发送信息后, rild 进程先select 到,然后进入 ril.cpp中的 listenCallback,有数据就进入 processCommandsCallback 回调函数,再调用 processCommandBuffer,这里调用pRI->pCI->dispatchFunction 来最终处理数据。
pRI是一个CommandInfo结构,初始化的内容在 /hardware/ril/libril/ril_commands.h 中。上篇说到要处理的是 RIL_REQUEST_SET_CLIR,从{RIL_REQUEST_SET_CLIR, dispatchInts, responseVoid} 可知要调用的dispatchFunction 就是dispatchInts。在ril.cpp 中的 dispatchInts 函数最终是调用 s_callbacks.onRequest 来处理请求。s_callbacks 是一个RIL_RadioFunctions,它的初始化是在/hardware/ril/reference-ril/reference-ril.c:
static const RIL_RadioFunctions s_callbacks = {
RIL_VERSION,
onRequest,
currentState,
onSupports,
onCancel,
getVersion
};
同一文件中的 onRequest 处理上层发来的信息,简单点的指令就直接调用at_send_command 发指令给 simcard,复杂点的就分发
到requestXXX函数来处理,不过最终还是调用at_send_command来写入 AT 指令。
at_send_command 相关的函数都在/hardware/ril/reference-ril/atchannel.c 中。at_send_command ->
at_send_command_full -> at_send_command_full_nolock -> writeline,这个writeline 最终调用 write 来把信息写到设备中。
对了,onRequest中是如何处理RIL_REQUEST_SET_CLIR的?
case RIL_REQUEST_SET_CLIR:
//TO DO
break;
...饿滴神,怎么来个TO DO...看样子是要等咱自己来写了(我说android手机咋那么少)。
指令写进去后要把结果传出来,这就是response的流程了。开头提到的文章写的很不错,这里也不赘述了(反正我要的代码又
没写...)。