您的位置:首页 > 移动开发

android_apps_frameworks_通话处理流程

2012-11-16 14:13 369 查看

一、拨打电话功能处理流程

1、拨打电话是从拨号后按下拨号键开始,此步的代码在/android/packages/apps/Contacts/src/com/android/contacts/目录的DialpadFragment.java文件中,相关代码如下:

dialButtonPressed(){

.........

finalString number = mDigits.getText().toString();

startActivity(newDialNumberIntent(number));

mDigits.getText().clear();

finish();

}

代码中newDialNumberIntent()方法定义如下:

privateIntent newDialNumberIntent(String number) {

finalIntent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, Uri.fromParts("tel", number, null));

.............

}

从newDialNumberIntent的定义可以看出,当拨号键按下以后,DialpadFragment会启动一个特定的组件,该组件的ACTION为:ACTION_CALL_PRIVILEGED,经过查找,该ACTION启动的组件是目下:/androidsourcecode/packeges/Phone/的一个文件,在该文件下的AndroidMenifest.xml中可以查到:“ACTION_CALL_PRIVILEGED”启动的Activity的名字是:PrivilegedOutgoingCallBroadcast,但是我们到/android/sourcecode/packeges/Phone/src/....目下并找不到该文件,因为该文件在AndroidMenifest.xml中标记有点特殊:
<activity-alias/>,这个标签的意思是这个Activity是另一个Activity的别名,真实的Activity在标签中用“android:targetActivity
=OutgoingCallBroadcast”标出,所以“ACTION_CALL_PRIVILEGED”启动的PrivilegedOutgoingCallBroadcast所对应的真实“身份”是“OutgoingCallBroadcast”。

2、这个时候电话的数据已经流到OutgoingCallBroadcast.java中了。

在OutgoingCallBroadcast.java的onCreate()方法中有:

protectedvoid onCreate(Bundle icicle) {

.......

Intentintent = getIntent();

........

Stringaction = intent.getAction();

.......

finalboolean emergencyNum = (number != null) && PhoneNumUtils.isEmergencyNumbernumber);//判断号码是否是紧急号码

.......

if(Intent.ACTION_CALL_PRIVILEGED.equals(action))


action= emergencyNum ? Intent.ACTION_CALL_EMERGENCY Intent.ACTION_CALL;

intent.setAction(action);

}

.......

intent.setClass(this,InCallScreen.class);

startActivity(intent);

}

在这个方法中,判断如果所收到的ACTION是“ACTION_CALL_PRIVILEGED”,那么根据所输入的号码是否是紧急号码进行转换,如果是紧急号码,则ACTION=Intent.ACTION_CALL_EMERGENCY,否则ACTION=Intent.ACTION_CALL,并启动转换Activity:InCallScreen.java

3、InCallScreen.java依然在目录/packeges/Phone/src/com/android/phone下。

InCallScreen的onCreate中调用initInCallScreen初始化打电话界面,并调用registerForPhoneStates注册电话状态监听.

在onNewIntent()方法中有:

protectedvoid onNewIntent(Intent intent) {

..........

Stringaction= intent.getAction();

..........

elseif (action.equals(Intent.ACTION_CALL) || action.equals(Intent.ACTION_CALL_EMERGENCY)) {

..........

InCallInitStatusstatus = placeCall(intent);

}

}

//placeCall

privateInCallInitStatus placeCall(Intent intent) {

..............

intcallStatus = PhoneUtils.placeCall(........);

}

InCallScreen.java中的placeCall方法调用PhoneUtils.java文件的placeCall方法。

4、PhoneUtils.java依然在目录/packeges/Phone/src/com/android/phone下。

publicstatic int placeCall(...) {

Connectionconnection;

connection= PhoneApp.getInstance().mCM.dial(phone, numberToDial) ;

}

继续追踪,在PhoneApp.java中发现,mCM是CallManager.java类的一个对象,而CallManager.java是属于frameworks层的,所以,这个时候数据流已经进入frameworks.

5、进入/frameworks/base/telephony/java/com/android/internal/telephony目录。

在CallManager.java的dial()方法中,有:

publicConnection dial(Phone phone, String dialNumber) throws CallState Exception {

PhonebasePhone = getPhoneBase(phone);

Connectionresult;

result= basePhone.dial(dialString);

........

}

privatestatic Phone getPhoneBase(Phone phone) {

if(phone instanceof PhoneProxy) {

returnphone.getForegroundCall().getPhone();

}

returnphone;

}

PhoneBase.java抽象类实现了接口Phone.java,而GSMPhone.java又实现了抽象类PhoneBase,所以上述代码中:phone.getForegroundCall()实际相当于GSMPhone对象执行了getForegroundCall()方法。

6、继续追踪GSMPhone.java,该类位于/frameworks/base/telephony/java/com/android/internal/telephony/gsm/下。

GSMPhone.java:

GsmCallTrackermCT;

publicGsmCall getForegroundCall() {

returnmCT.foregroundCall;

}

可以看出getForegroundCall()函数继续调用GsmCallTracker.java的foregroundCall属性。

7、GsmCallTracker.java位于/frameworks/base/telephony/java/com/android/internal/telephony/gsm/下.

GsmCallTracker.java:

GSMCallforegroundCall = new GSMCall(this);

GsmCallTracker.java:GSMCall foregroundCall = new GSMCall(this);

打开GSMCall.java,找到getPhone()方法,发现:

GSMCallTrackerowner;

publicPhone getPhone() {

returnowner.phone;

}

而在GSMCallTracker.java中有如下声明:

GSMPhonephone;

到此,我们得出一下结论:第5部分代码所返回的就是GSMPhone的对象,进一步可以得出,第5部分的代码即是调用了GSMPhone对象的dial方法。

8、在GSMPhone.java中

GSMCallTrackermCT;

publicConnection dial(String dialString) throws CallStateException {

returndial(dialString, null);

}

publicConnection dial(String dialString, UUSInfo uusInfo) throws CallStaeException {

.......

mCT.dial(.......);

}

继续调用GSMCallTracker.java中的dial()方法:

GSMCallTracker.java:

GSMCallTracker(GSMPhonephone) {

cm= phone.mCM;

}

Connectiondial(String dialString, int clirMode, UUSInfo uusInfo) {

cm.dial(........);

}

追踪mCM,发现:

publicCommandsInterface mCM;

所以GSMCallTracker持有CommandsInterface对象,即RIL.Java类的对象,所以"cm.dial(....)"即是调用RIL类对象的dial()方法。

9、RIL.java

publicvoid

dial(Stringaddress,intclirMode,Messageresult)

{

RILRequestrr=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是一套简单的序列化协议,用于将对象(或对象的成员)序列化成字节流,以供传递参数之用.这里可以看到Stringaddress和intclirMode都是将依次序列化的成员.在这之前,rr初始化的时候,request号跟request的序列号(自动生成的递增数),已经成为头两个将被序列化的成员.这为后面的request解析打下了基础.

接下来是send到handleMessage的流程,send将rr直接传递给另一个线程的handleMessage,handleMessage执行data=rr.mp.marshall()执行序列化操作,并将data字节流写入到rildsocket.

RIL对象负责把客户端的通话请求按照一定的格式发送给"rild"socket,至此,请求过程完毕。

二、来电处理流程

1、PhoneApp.java在初始化时会实例CallNotifier对象,Callnotifier主要是对电话状态的监听,通知事件

PhoneApp创建一个CallNotifier:

notifier= CallNotifier.init(this, phone, ringer, mBtHandsfree, new CallLogAsync());

2、Callnotifier实现对电话状态的监听

privateCallNotifier(PhoneApp app, Phone phone, Ringer ringer,BluetoothHandsfree btMgr, CallLogAsync callLog) {

mApplication= app;

mCM= app.mCM;

mCallLog= callLog;

mAudioManager= (AudioManager)mApplication.getSystemService(Context.AUDIO_SERVICE);

registerForNotifications();//状态监听

}

3、registerForNotifications方法注册电话状态监听

privatevoid registerForNotifications() {

mCM.registerForNewRingingConnection(this,PHONE_NEW_RINGING_CONNECTION, null);

mCM.registerForPreciseCallStateChanged(this,PHONE_STATE_CHANGED, null);

mCM.registerForDisconnect(this,PHONE_DISCONNECT, null);

mCM.registerForUnknownConnection(this,PHONE_UNKNOWN_CONNECTION_APPEARED, null);

mCM.registerForIncomingRing(this,PHONE_INCOMING_RING, null);

mCM.registerForCdmaOtaStatusChange(this,EVENT_OTA_PROVISION_CHANGE, null);

mCM.registerForCallWaiting(this,PHONE_CDMA_CALL_WAITING, null);

mCM.registerForDisplayInfo(this,PHONE_STATE_DISPLAYINFO, null);

mCM.registerForSignalInfo(this,PHONE_STATE_SIGNALINFO, null);

mCM.registerForInCallVoicePrivacyOn(this,PHONE_ENHANCED_VP_ON, null);

mCM.registerForInCallVoicePrivacyOff(this,PHONE_ENHANCED_VP_OFF, null);

mCM.registerForRingbackTone(this,PHONE_RINGBACK_TONE, null);

mCM.registerForResendIncallMute(this,PHONE_RESEND_MUTE, null);

}

4、mCM.registerForNewRingingConnection(this,PHONE_NEW_RINGING_CONNECTION,
null); 监听来电,在handleMessage函数内处理

publicvoid handleMessage(Message msg) {

switch(msg.what) {

casePHONE_NEW_RINGING_CONNECTION:

log("RINGING...(new)");

onNewRingingConnection((AsyncResult)msg.obj);

mSilentRingerRequested= false;

break;

}

5、转到onNewRingingConnection处理来电,最后转到showIncomingCall函数启动InCallScreen界面,当然还有处理一些状态更新,响铃之类的。比如挂断流程如下。

三、挂断电话处理流程

有电话打入是RIL会通知CallNotifier,CallNotifier会调用InCallScreen,上面有讨论,简单提一下。CallNotifier会调用InCallScreen代码

1、InCallScreen会显示InCallTouchUI,
InCallTouchUI 就是可以拖动接听和挂断的那个界面,这个页面持有一个SlidingTab控件,这个就是可以左右拖动的控件了。

InCallTouchUI实现了SlidingTab.OnTriggerListener,在该监听者的OnTrigge方法里面处理接听和挂断的动作,代码如下

onTrigger():

switch(whichHandle) {

caseANSWER_CALL_ID:

if(DBG) log("ANSWER_CALL_ID: answer!");

mInCallScreen.handleOnscreenButtonClick(R.id.incomingCallAnswer);

break;

…......

}

2、进入InCallScreen.java:

handleOnscreenButtonClick():

caseR.id.incomingCallReject:

hangupRingingCall();

break;

3、进入PhoneUtils.java:

staticboolean hangup(Call call) {

try{

CallManagercm = PhoneApp.getInstance().mCM;

if(call.getState() == Call.State.ACTIVE &&cm.hasActiveBgCall()) {

//handle foreground call hangup while there is background call

log("-hangup(Call): hangupForegroundResumeBackground...");

cm.hangupForegroundResumeBackground(cm.getFirstActiveBgCall());

}else {

log("-hangup(Call): regular hangup()...");

call.hangup();

}

returntrue;

}catch (CallStateException ex) {

Log.e(LOG_TAG,"Call hangup: caught " + ex, ex);

}

returnfalse;

}

4、进入GsmCall.java:

publicvoid hangup() throws CallStateException {

owner.hangup(this);

}

5、进入GsmCallTracker.java:

//*****Called from GsmCall

/*package */ void

hangup(GsmCall call) throws CallStateException {

if(call.getConnections().size() == 0) {

thrownew CallStateException("no connections in call");

}

if(call == ringingCall) {

if(Phone.DEBUG_PHONE) log("(ringing) hangup waiting orbackground");

cm.hangupWaitingOrBackground(obtainCompleteMessage());

}else if (call == foregroundCall) {

if(call.isDialingOrAlerting()) {

if(Phone.DEBUG_PHONE) {

log("(foregnd)hangup dialing or alerting...");

}

hangup((GsmConnection)(call.getConnections().get(0)));

}else {

hangupForegroundResumeBackground();

}

}else if (call == backgroundCall) {

if(ringingCall.isRinging()) {

if(Phone.DEBUG_PHONE) {

log("hangupall conns in background call");

}

hangupAllConnections(call);

}else {

hangupWaitingOrBackground();

}

}else {

thrownew RuntimeException ("GsmCall " + call +

"doesnot belong to GsmCallTracker " + this);

}

call.onHangupLocal();

phone.notifyPreciseCallStateChanged();

}

6、下面进入RIL.java,向rild发请求了,比如:

publicvoid hangupForegroundResumeBackground (Message result) {

RILRequestrr

=RILRequest.obtain(

RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND,

result);

if(RILJ_LOGD) riljLog(rr.serialString() + "> " +requestToString(rr.mRequest));

send(rr);

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: