6572 Phone call分析
目 录
1.
概述
1.1 Phone Call
(本文在文字描述和图片引用方面,使用了其他同学的部分内容,在此感谢!本人侧重代码分析,在此将分析过程分享给大家。)
1.1.1 框架介绍
...
在Telephony层的相关类关系如下:
1.1.1 功能说明
GsmCallTracker对象中还提供有三个GsmCall对象(派生自抽象类Call):ringingCall(用来管理处于INCOMING和WAITING状态的通话)、foregroundCall(用来管理处于DAILING、ALERTING、ACTIVE状态的通话)、backgroundCall(用来管理HOLD的通话)。
GsmCallTracker是Call应用中的通话管理层,它维护了一个最多MAX_CONNECTIONS=7路GsmConnections通话链路的同时,还维护了三种通话状态(ringingCall,foregroundCall,backgroundCall),GsmCall以及GsmConnection是GsmCallTracker维护的对象,同时GsmConnection又依附于GsmCall的存在,MAX_CONNECTIONS_PER_CALL=5表明最多可以有5路通话处于某一个通话状态(foregroundCall,background,ringing)。GsmConnection继承自Connection类,该类主要是用来维护某一路的通话状态。当它们状态均为DISCONNECTED时意味着该GSMCall为IDLE状态。
在GSMCallTracker中维护着通话列表:connections。顺序记录了正连接上的通话,这些通话包括:ACTIVE,DIALING,ALERTING,HOLDING,INCOMING,WAITING等状态的连接。GSMCallTracker将这些连接分为了三类别进行管理:RingingCall: INCOMING ,WAITING ForegourndCall: ACTIVE, DIALING ,ALERTING BackgroundCall: HOLDING
另外GSMCallTracker还包含一个GsmConnection类型(派生自抽象类Connection)的数组对象Connections,用来维护所有的现行的通话的列表,GSMCallTracker对象最大可维护7路通话。
GsmConnection对象中有个成员变量:GsmCall parent,这个成员变量是用来表示该connection是属于哪个Call的,一个Call可以有多个Connection,但一个Connection只能属于一个Call。
1. 4.2呼叫相关类分析
1.1 基本知识
1.1.1 文件说明
CallDetailActivity.java 联系人详情
DialtactsActivity.java 通话记录
VoicemailContract.java 存放VM相关的一些常量、URI等,子类Status会经常被引用。
在calllogfragment.java 通话记录列表里添加了vvm的记录。
在CallDetailActivity.java 联系人详情里添加了详细信息显示。
在CallDetailHistoryAdapter.java里做详细信息数据适配器
DialpadFragment.java 拨号盘界面
CallNotifier是一个Handler,它为PhoneApp处理各个主动上报来的一些消息。它监听来自Telephony层phone状态变化和其它各种事件,从而作出反应 如各种UI行为:启动铃音播放和来电显示UI、播放正在通话时的来电提示、更新状态栏提示(通过NotificationMgr)、通话记录添加等。
1.2 DialtactsActivity
DialtactsActivity.java Dialer程序在packages/apps/Contacts中,见DialtactsActivity,它继承自TabActivity类, 是通话界面入口activity,包含3个功能项,由3个tab呈现, 通过实现接口 TabHost.OnTabChangeListener在各个tab之间切换。界面如下,
1.1.1 onCreate
onCreate主要完成了如下注释的几个工作,
protected void onCreate(Bundle icicle) {
…
final Intent intent = getIntent();
fixIntent(intent);
setContentView(R.layout.dialtacts_activity); //载入layout,该layout修改后只有一个view pager
mContactListFilterController = ContactListFilterController.getInstance(this);
mContactListFilterController.addListener(mContactListFilterListener);
findViewById(R.id.dialtacts_frame).addOnLayoutChangeListener(mFirstLayoutListener);
mViewPager = (ViewPager) findViewById(R.id.pager);//获取view的控件引用,并进行相关设置
mViewPager.setAdapter(new ViewPagerAdapter(getFragmentManager()));//getFragmentManager在activity里面,该句详细分析见后
mViewPager.setOnPageChangeListener(mPageChangeListener); // 监听器设置当前文件只改变了索引
mViewPager.setOffscreenPageLimit(2);
addDialpadScrollingThreshold(true);
…
// Setup the ActionBar tabs (the order matches the tab-index contants TAB_INDEX_*)
setupDialer();// 设置各功能项,当前只是添加了tab,实现在其他地方
setupCallLog();
setupFavorites();
getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
getActionBar().setDisplayShowTitleEnabled(false);
getActionBar().setDisplayShowHomeEnabled(false);
…
setCurrentTab(intent);
}
1)布局文件
dialtacts_activity.xml
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
2)ViewPager
根据onCreate以及后面的start等方法,我们并没有明显发现各tab项对应的界面布局和相关数据处理,而且我们的layout也仅仅是一个ViewPager类,我们需要找到tab的实现过程。
通过代码分析我们可以看出mDialpadFragment、mCallLogFragment、mPhoneFavoriteFragment是各tab对应的类的引用,他们通过onAttachFragment被赋值,通过子类ViewPagerAdapter的方法getItem来new出各自的实例。
public void onAttachFragment(Fragment fragment) {
if (fragment instanceof DialpadFragment) {
mDialpadFragment = (DialpadFragment) fragment;
} else if (fragment instanceof CallLogFragment) {
mCallLogFragment = (CallLogFragment) fragment;
} else if (fragment instanceof PhoneFavoriteFragment) {
mPhoneFavoriteFragment = (PhoneFavoriteFragment) fragment;
…
}
ViewPagerAdapter:
public Fragment getItem(int position) {
switch (position) {
case TAB_INDEX_DIALER:
return new DialpadFragment();
case TAB_INDEX_CALL_LOG:
return new CallLogFragment();
case TAB_INDEX_FAVORITES:
return new PhoneFavoriteFragment();
}
这些方法是如何关联上的,各类之间的关系是怎样的,我们可以进一步来分析。
根据上面onCreate的代码可以看出,通过layout,我们得到了一个ViewPager的引用,然后设置adapter,在setAdapter里调用populate,populate调用addNewItem,addNewItem调用mAdapter.instantiateItem,而getItem则是被instantiateItem调用的,所以我们能知道通过getItem最终将new出来的实例传递给了ViewPager。
对于其中的mAdapter,则是onCreate新建的子类ViewPagerAdapter的实例,通过它的继承关系,可以看到getItem是被父类FragmentPagerAdapter的instantiateItem调用的。
public Object instantiateItem(ViewGroup container, int position) {
…
fragment = getItem(position);
…
onAttachFragment是在new Fragment的时候被调用的,它是在Fragment.onAttach()后并在Fragment.onCreate()前被调用的。
这样,mDialpadFragment等对象实例就初始化好了,并通过一些粘合的方法关联到tab上了。
3)setupDialer
添加tab项。以setupDialer为例,说明tab是如何添加的,这里需要注意的是对mTabListener的设置,关联了ViewPager和tab。
private void setupDialer() {
…
final Tab tab = getActionBar().newTab();
tab.setContentDescription(R.string.dialerIconLabel);
tab.setTabListener(mTabListener);
tab.setIcon(R.drawable.ic_tab_dialer);
getActionBar().addTab(tab);
}
4)
如果启动了contact进程,从launcher里面进入到dailtactsactivity,不会再走onCreate,而是先走onNewIntent,再走start。
进程如何启动,为何不走onCreate?
1.1.2 其他
onNewIntent
在其他应用使用拨号,调用onNewIntent,调用栈如下,
DialtactsActivity.onNewIntent(Intent) line: 1135
Instrumentation.callActivityOnNewIntent(Activity, Intent) line: 1156
ActivityThread.deliverNewIntents(ActivityThread$ActivityClientRecord, List) line: 2493
ActivityThread.performNewIntents(IBinder, List) line: 2506
ActivityThread.handleNewIntent(ActivityThread$NewIntentData) line: 2515
ActivityThread.access$1400(ActivityThread, ActivityThread$NewIntentData) line: 162
ActivityThread$H.handleMessage(Message) line: 1435
ActivityThread$H(Handler).dispatchMessage(Message) line: 107
Looper.loop() line: 194
ActivityThread.main(String[]) line: 5400
Method.invokeNative(Object, Object[], Class, Class[], Class, int, boolean) line: not available [native method]
Method.invoke(Object, Object...) line: 525
ZygoteInit$MethodAndArgsCaller.run() line: 837
ZygoteInit.main(String[]) line: 604
NativeStart.main(String[]) line: not available [native method]
setCurrentTab
设置当前tab关联的viewPage对象。这个方法是tab和view的粘合方法。
从mainmenu进入DialpadFragment的调用栈,
DialpadFragment.onStart() line: 1043
DialpadFragment(Fragment).performStart() line: 1719
FragmentManagerImpl.moveToState(Fragment, int, int, int, boolean) line: 913
FragmentManagerImpl.moveToState(int, int, int, boolean) line: 1057
FragmentManagerImpl.moveToState(int, boolean) line: 1039
FragmentManagerImpl.dispatchStart() line: 1845
DialtactsActivity(Activity).performStart() line: 5138
DialtactsActivity(Activity).performRestart() line: 5187
DialtactsActivity(Activity).performResume() line: 5192
1.1 CallManager
1.1.1 说明
* CallManager class provides an abstract layer for PhoneApp to access
* and control calls. It implements Phone interface.
*
* CallManager provides call and connection control as well as
* channel capability.
*
* There are three categories of APIs CallManager provided
*
* 1. Call control and operation, such as dial() and hangup()
* 2. Channel capabilities, such as CanConference()
* 3. Register notification
如上描述,CallManager提供Phone应用的抽象层处理,主要提供3类接口API:呼叫控制、信道信息、注册通知。
1.1.2 初始注册
CallManager提供registerPhone来进行相关实例的初始注册。
PhoneGlobals.java在onCreate里面调用自身方法registerPhone,间接调用CallManager.registerPhone来注册相关实例。
private void registerPhone() {
mCM = CallManager.getInstance();
if (GeminiUtils.isGeminiSupport()) {
mCMGemini = MTKCallManager.getInstance();
mCMGemini.registerPhoneGemini(phone);
} else {
mCM.registerPhone(phone);
}
}
PhoneProxy.java里在重建phone对象时也会进行registerPhone注册。
deleteAndCreatePhone()
{
if(mActivePhone != null)
CallManager.getInstance().registerPhone(mActivePhone);
}
对于CallManager registerPhone的实现,如下,为每个卡槽实例提供呼叫相关的注册,
public boolean registerPhone(Phone phone) {
if ((FeatureOption.MTK_BSP_PACKAGE == true) &&
(FeatureOption.MTK_GEMINI_SUPPORT == true) &&
(!(phone instanceof SipPhone))) {
int count = (MAXIMUM_SIM_COUNT < PhoneConstants.GEMINI_SIM_NUM) ? MAXIMUM_SIM_COUNT : PhoneConstants.GEMINI_SIM_NUM;
/* registerPhone is called by Google default PhoneAPP */
for (int i = 0; i < count; i++) {
Phone p = ((GeminiPhone)phone).getPhonebyId(PhoneConstants.GEMINI_SIM_1 + i);
registerOnePhone(p);
}
int default_sim = SystemProperties.getInt(PhoneConstants.GEMINI_DEFAULT_SIM_PROP, PhoneConstants.GEMINI_SIM_1);
mDefaultPhone = getPhoneBase(((GeminiPhone)phone).getPhonebyId(default_sim));
Log.d(LOG_TAG, "[BSPPackage]default_sim = " + default_sim);
Log.d(LOG_TAG, "[BSPPackage]mDefaultPhone = " + mDefaultPhone);
registerForPhoneStates(getPhoneBase(phone));
return true;
每个卡槽实例都注册mPhones、mRingingCalls、mBackgroundCalls、mForegroundCalls。
private boolean registerOnePhone(Phone phone) {
boolean result = false;
Phone basePhone = getPhoneBase(phone);
if (basePhone != null && !mPhones.contains(basePhone)) {
mPhones.add(basePhone);
mRingingCalls.add(basePhone.getRingingCall());
mBackgroundCalls.add(basePhone.getBackgroundCall());
mForegroundCalls.add(basePhone.getForegroundCall());
result = true;
}
return result;
}
1.1.3 业务注册
1)向上提供注册服务
CallManager通过向应用的上层提供注册服务的方法,将呼叫信息和网络状态上报。
常规的注册方法是GeminiRegister做中转,向上层提供注册方法,GeminiRegister再调用CallManager的注册方法,将客户端的信息封装到RegistrantList里面,使用时再调用RegistrantList的方法notifyRegistrants通知到其客户端。
如InCallScreen里面注册呼叫事件改变,
GeminiRegister.registerForPreciseCallStateChanged(callManager, mHandler, PHONE_STATE_CHANGED);
在CallManager里,mPreciseCallStateRegistrants是对应的RegistrantList,handler和事件被封装存放起来,
public void registerForPreciseCallStateChanged(Handler h, int what, Object obj){
mPreciseCallStateRegistrants.addUnique(h, what, obj);
}
CallManager只是一个中间层,它不会自己产生状态信息,它的信息要来源于它的下层。所以它本身也会向下注册(后面会讲到)。如果他收到下层上报的EVENT_PRECISE_CALL_STATE_CHANGED事件,则在handler里面调用它的上层已经注册的RegistrantList,将状态改变通知到其客户端InCallScreen,
case EVENT_PRECISE_CALL_STATE_CHANGED:
case EVENT_PRECISE_CALL_STATE_CHANGED + NOTIFICATION_ID_OFFSET:
case EVENT_PRECISE_CALL_STATE_CHANGED + (NOTIFICATION_ID_OFFSET * 2):
case EVENT_PRECISE_CALL_STATE_CHANGED + (NOTIFICATION_ID_OFFSET * 3):
if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_PRECISE_CALL_STATE_CHANGED)");
index = (msg.what - EVENT_PRECISE_CALL_STATE_CHANGED) / NOTIFICATION_ID_OFFSET;
mPreciseCallStateRegistrantsGemini[index].notifyRegistrants((AsyncResult) msg.obj);
mPreciseCallStateRegistrants.notifyRegistrants((AsyncResult) msg.obj);
handle3GSwitchLock();
try {
checkIfExistsFollowingAction();
} catch (Exception e) {
//Do nothing.
}
break;
InCallScreen类主要负责通话界面的绘制和通话过程的控制,它也有一个handler,当CallManager通知呼叫状态改变时,它会处理自己注册的事件
case PHONE_STATE_CHANGED:
onPhoneStateChanged((AsyncResult) msg.obj);
2)向下注册获取服务
在registerPhone里,提供初始注册服务时,也调用了registerSinglePhoneStates向下注册服务,方法是调用phone提供的注册方法。例如 phone.registerForPreciseCallStateChanged(mHandler, EVENT_PRECISE_CALL_STATE_CHANGED + eventDifference*simId, null);就是用来向phone注册呼叫状态改变的。phone是GSMPhone的实例引用,registerForPreciseCallStateChanged是Phonebase的方法,GSMPhone继承自Phonebase。registerForPreciseCallStateChanged的实现是将CallManager的handler和EVENT_PRECISE_CALL_STATE_CHANGED封装起来,存放在在Phonebase的mPreciseCallStateRegistrants这个RegistrantList里面。
public void registerForPreciseCallStateChanged(Handler h, int what, Object obj) {
checkCorrectThread(h);
mPreciseCallStateRegistrants.addUnique(h, what, obj);
}
当呼叫状态改变时,需要将这个信息通知到上层应用,GSMPhone的notifyPreciseCallStateChanged会被GsmCallTracker.java调用,
phone.notifyPreciseCallStateChanged();
GSMPhone是继承自Phonebase的,所以最终是Phonebase的notifyPreciseCallStateChangedP被调用,由于之前注册过RegistrantList,所以使用RegistrantList的方法notifyRegistrants通知到其客户端CallManager。
protected void notifyPreciseCallStateChangedP() {
AsyncResult ar = new AsyncResult(null, this, null);
mPreciseCallStateRegistrants.notifyRegistrants(ar);
}
CallManager的handler会进行事件的处理。
1.2 CallNotifier
1.2.1 说明
CallNotifier
CallNotifier是一个Handler,它为PhoneApp处理各个主动上报来的一些消息。它监听来自Telephony层phone状态变化和其它各种事件,从而作出反应 如各种UI行为:启动铃音播放和来电显示UI、播放正在通话时的来电提示、更新状态栏提示(通过NotificationMgr)、通话记录添加等。
在PhoneBase中提供了一些RegistrantList,CallNotifier可以将自己作为一个感兴趣者注册进去,这样,当状态变化时,CallNotifier将得到通知,然后在线程中对其处理,作出UI方面的响应。在其构造函数中可以看出它处理的消息事件类别,下面的代码列出了部分要处理的消息种类(没有列出针对CDMA或GSM特定类型的消息):
mPhone.registerForNewRingingConnection(this, PHONE_NEW_RINGING_CONNECTION, null);
mPhone.registerForPreciseCallStateChanged(this, PHONE_STATE_CHANGED, null);
mPhone.registerForDisconnect(this, PHONE_DISCONNECT, null);
mPhone.registerForUnknownConnection(this, PHONE_UNKNOWN_CONNECTION_APPEARED, null);
mPhone.registerForIncomingRing(this, PHONE_INCOMING_RING, null);
当有PHONE_NEW_RINGING_CONNECTION类型消息到来时,意味着一个RINGING或WAITING的连接(connection)出现,此时handleMessage函数调用onNewRingingConnection来处理。后者先检查Settings里的设置是否可以接听电话;然后进行响铃(见InCallTonePlayer)和显示InCallScreen的UI,见PhoneUtils.showIncomingCallUi()和PhoneApp.displayCallScreen()两个函数。
通话过程中的铃音提示由线程类InCallTonePlayer完成。
当有PHONE_INCOMING_RING类型的消息到来时,意味着RIL层受到Ring,此处播放铃音。它使用的是Ringer.ring()函数,它会创建一个线程去播放铃音(见Ringer.makeLooper函数)。
当收到PHONE_STATE_CHANGED消息时,表明Phone的状态发生了改变,比如响铃后接通了电话,此时处理函数是onPhoneStateChanged,比如再次确认停止铃音、更新状态栏列的状态通知等。
当收到PHONE_DISCONNECT消息时,表明电话连接已挂断或RingCall断掉。其处理函数是onDisconnect。它清理现场诸如音频通道恢复、来电响铃的停止确认、对InCallScreen的UI清理、若有未接电话须在状态栏显示等。
1.3 PhoneInterfaceManager
1.3.1 说明
它继承自 Itelephony.Stub,实现了服务器侧的 ITelephony接口,其接口定义见文件:
frameworks/base/telephony/java/com/android/internal/telephony/ITelephony.aidl
TelephonyManager作为客户端通过Binder与其交互。
1.4 NotificationMgr
1.4.1 说明
NotificationMgr以静态成员函数的方式为PhoneApp用于Phone进程在状态栏中通知用户消息的功能,诸如:有未接电话、正在通话、是否静音等信息。它使用系统提供的API类NotificationManager和StatusBarManager完成通知功能。每项通知对应着通知、更新通知和取消通知的函数。当收到Message时,PhoneApp的Handler的handleMessage会使用NotificationMgr更新状态栏信息。下面的代码片段用于更新状态栏的的提示信息:
case EVENT_UPDATE_INCALL_NOTIFICATION:
NotificationMgr.getDefault().updateInCallNotification();//通话提示
break;
case EVENT_DATA_ROAMING_DISCONNECTED:
NotificationMgr.getDefault().showDataDisconnectedRoaming();//因漫游数据连接断开提示
break;
case EVENT_DATA_ROAMING_OK:
NotificationMgr.getDefault().hideDataDisconnectedRoaming();//隐藏漫游断开提示
break;
是否有未接电话的提示则是在PhoneApp创建NotificationMgr对象并调用其初始化函数时检查提示的。
1.5 InCallScreen
1.5.1 说明
它是手机正在通话时的Activity。当有来电、开始拨号或正在通话时,运行的是该Activity,UI界面是其对应的View:
// Inflate everything in incall_screen.xml and add it to the screen.
setContentView(R.layout.incall_screen);
在其OnCreate函数中将自己指定为PhoneApp的InCallScreen:
app.setInCallScreenInstance(this);
InCallScreen需要处理来电时跳过键盘锁直接可以接听电话、是否有耳机插入的情况、是否用蓝牙接听电话、需要监听并维护更新通话状态并显示给用户、需要支持通话过程中的某些功能(如发送DTMF、电话会议、分离一路通话)操作、OTA Call等。
CallCard是InCallScreen中的一个call(可能是当前的Call或保持的Call或来电Call)。
当需要接听电话或拨打电话时,上层发来intent,然后InCallScreen收到intent时它的InCallScreen.onNewIntent函数被调用,解析intent,要么调用placeCall拨打电话,要么调用internalAnswerCall接听电话。
应用程序可发出Intent进行电话呼叫,如在TwelveKeyDialer.java中进行呼叫时,去创建一个Intent:
void placeCall() {
final String number = mDigits.getText().toString();
boolean sendEmptyFlash = false;
Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
Uri.fromParts(“tel”, number, null));
if (number == null || !TextUtils.isGraphic(number)) {
// There is no number entered.
if (phoneIsCdma() && phoneIsOffhook()) {
// We only want to send this empty flash extra if we’re CDMA and the
// phone is offhook (don’t want to send if ringing or dialing)
intent.putExtra(EXTRA_SEND_EMPTY_FLASH, true);
sendEmptyFlash = true;
} else {
playTone(ToneGenerator.TONE_PROP_NACK);
return;
}
}
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
在phone程序中OutgoingCallBroadcaster(文件OutgoingCallBroadcaster.java)的包含一个内部类OutgoingCallReceiver,由它接收电话呼叫Intent,然后经过转换后再发送出去,由上述的InCallScreen接收处理,显示拨号界面并进行呼叫等。
如OutgoingCallBroadcaster所说,它接收 CALL 和CALL_PRIVILEGED 两种Intents,然后广播出ACTION_NEW_OUTGOING_CALL intent,让别的应用程序有机会去监视这些intent,最后这些呼叫intent又被自己收到转换,启动InCallScreen.
下面的代码是Intent转换,然后Intent被InCallScreen接收:
Intent newIntent = new Intent(Intent.ACTION_CALL, uri);
newIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
PhoneUtils.checkAndCopyPhoneProviderExtras(intent, newIntent);
newIntent.setClass(context, InCallScreen.class);
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (DBG) Log.v(TAG, “doReceive(): calling startActivity: ” + newIntent);
context.startActivity(newIntent);
}
这样做的目的是
InCallTouchUi:
通话过程中的按钮功能以及来电接听时的滑动接听功能。
ManageConferenceUtils:管理多方通话的工具,包括部分UI元素。借助PhoneUtils实现其功能。
DTMFTwelveKeyDialer:通话状态时的拨号盘,用于发送DTMF。
DTMFTwelveKeyDialerView:DTMF拨号视图布局类。
InCallControlState维护着一些状态信息,诸如是否Enable了Speaker声音免提、是否可以添加新的一路通话等等。它是MVC模式的数据部分。
InCallMenu是通话状态菜单,里面包含各个菜单项:
InCallMenuItemView mManageConference;
InCallMenuItemView mShowDialpad;
InCallMenuItemView mEndCall;
InCallMenuItemView mAddCall;
InCallMenuItemView mSwapCalls;
InCallMenuItemView mMergeCalls;
InCallMenuItemView mBluetooth;
InCallMenuItemView mSpeaker;
InCallMenuItemView mMute;
InCallMenuItemView mHold;
InCallMenuItemView mAnswerAndHold;
InCallMenuItemView mAnswerAndEnd;
InCallMenuItemView mAnswer;
InCallMenuItemView mIgnore;
InCallMenuItemView继承自TextView,代表着手机处在通话状态时的菜单项。每个菜单项保护一个文本,一个可选的“绿灯”,代表着打开/关闭;在文本上面的可以有可选的图标。其成员函数可以给它们赋值。当它们被点击后,在函数InCallScreen.handleOnscreenButtonClick或InCallScreen.onClick中得到调用。前者用于InCallTouchUi上的点击事件。
InCallScreen的成员函数
registerForPhoneStates:用于注册InCallScreen对哪些状态感兴趣。对于GSM手机,则注册了这些:
mPhone.registerForPreciseCallStateChanged(mHandler, PHONE_STATE_CHANGED, null);
mPhone.registerForDisconnect(mHandler, PHONE_DISCONNECT, null);
mPhone.registerForMmiInitiate(mHandler, PhoneApp.MMI_INITIATE, null); mPhone.registerForMmiComplete(mHandler, PhoneApp.MMI_COMPLETE, null);
mPhone.registerForSuppServiceFailed(mHandler, SUPP_SERVICE_FAILED, null);
当状态变化收到Message后,由mHandler来响应处理。
2. ICS Android 4.2 呼叫流程
2.1 呼叫请求
2.1.1 拨号请求(Contact)
我们从拨号盘开始分析呼叫流程。我们知道,输入一个号码,点击Call按钮,就开始触发了一个呼叫,然后将进行号码检查、判断,网络状态检查等工作,代码逐步向下调用,经过telephony、ril,再通过AT指令,让modem完成信道请求、连接和相应信令处理。
1)阶段一
在代码里,DialpadFragment的click将被执行,根据按钮id,执行拨号功能,并调用dialButtonPressed,
case R.id.dialButton: {
if (mNeedCheckSetting) {
// Retrieve the haptic feedback setting.
mHaptic.checkSystemSetting();
mNeedCheckSetting = false;
}
mHaptic.vibrate(); // Vibrate here too, just like we do for the regular keys
Profiler.trace(Profiler.DialpadFragmentEnterClick);
dialButtonPressed();
Profiler.trace(Profiler.DialpadFragmentLeaveClick);
return;
dialButtonPressed再调用dialButtonPressedInner,主要工作是创建一个intent,并调用doCallOptionHandle开始呼叫相关的处理。
protected void dialButtonPressedInner(String number, int type) {
…
final Intent intent = ContactsUtils.getCallIntent(number,
(getActivity() instanceof DialtactsActivity ?
((DialtactsActivity) getActivity()).getCallOrigin() : null), type);
mCallOptionHandler.doCallOptionHandle(intent);
mClearDigitsOnStop = true;
…
下面是函数调用栈验证分析过程:
DialpadFragment.dialButtonPressedInner(String, int) line: 2647
DialpadFragment.dialButtonPressed() line: 2634
DialpadFragment.onClick(View) line: 1531
一些重要的数据表现形式:
number "12344" (id=830022532488) //拨出的号码
cachedString "tel:12344" (id=830020302720) //uri里面的号码表述
mAction "android.intent.action.CALL_PRIVILEGED" (id=830016739648) //intent的action
2)阶段二
mCallOptionHandler 是ContactsCallOptionHandler的引用,其方法doCallOptionHandle主要是获取ITelephony服务,并调用父类CallOptionHandler的方法doCallOptionHandle,这里有2个变量非常重要,mCallOptionHandlerList和mSuccessor。
public void doCallOptionHandle(Context activityContext, Context applicationContext, Intent intent,
CallOptionBaseHandler.ICallOptionResultHandle resultHandler,
CellConnMgr cellConnMgr, ITelephony telephonyInterface,
boolean isMultipleSim, boolean is3GSwitchSupport) {
ListIterator<CallOptionBaseHandler> iterator = mCallOptionHandlerList.listIterator();
CallOptionBaseHandler previousHandler = iterator.next();
while (iterator.hasNext()) {
CallOptionBaseHandler currentHandler = (CallOptionBaseHandler)iterator.next();
previousHandler.setSuccessor(currentHandler);
previousHandler = currentHandler;
}
Request request = new Request(activityContext, applicationContext, intent, resultHandler,
cellConnMgr, telephonyInterface, isMultipleSim, is3GSwitchSupport,
mCallOptionHandlerFactory);
mCallOptionHandlerList.getFirst().handleRequest(request);
}
mCallOptionHandlerList是CallOptionBaseHandler类型的LinkedList,它存放了9个呼叫处理类,每个MO呼叫要依次被这9个呼叫处理类处理,主要完成的功能是号码处理、不同呼叫类型的处理和网络状态相关的处理(详细流程可自行分析)。要实现这样的功能,上面的doCallOptionHandle做了一个循环,通过setSuccessor将下一个处理类作为上一个类的mSuccessor,这样在上一个处理类完成之后,调用mSuccessor就可以执行到下一个类,类似于函数指针。
public CallOptionHandler(CallOptionHandlerFactory callOptionHandlerFactory) {
mCallOptionHandlerFactory = callOptionHandlerFactory;
mCallOptionHandlerList = new LinkedList<CallOptionBaseHandler>();
mCallOptionHandlerList.add(callOptionHandlerFactory.getFirstCallOptionHandler());
mCallOptionHandlerList.add(callOptionHandlerFactory.getInternetCallOptionHandler());
mCallOptionHandlerList.add(callOptionHandlerFactory.getVideoCallOptionHandler());
mCallOptionHandlerList.add(callOptionHandlerFactory.getSimSelectionCallOptionHandler());
mCallOptionHandlerList.add(callOptionHandlerFactory.getSimStatusCallOptionHandler());
mCallOptionHandlerList.add(callOptionHandlerFactory.getVoiceMailCallOptionHandler());
mCallOptionHandlerList.add(callOptionHandlerFactory.getInternationalCallOptionHandler());
mCallOptionHandlerList.add(callOptionHandlerFactory.getIpCallOptionHandler());
mCallOptionHandlerList.add(callOptionHandlerFactory.getFinalCallOptionHandler());
}
第一个执行的类是ContactsFirstCallOptionHandler,之后通过下面的方式跳转到下一个处理类,
if (null != mSuccessor) {
mSuccessor.handleRequest(request);
}
再执行到ContactsSimStatusCallOptionHandler的时候,handleRequest根据条件是否需要检查sim状态,发送了一个消息MESSAGE_CHECK_SIM_STATUS,
public void handleRequest(final Request request) {
int slot = request.getIntent().getIntExtra(Constants.EXTRA_SLOT_ID, -1);
…
if (needToCheckSIMStatus(slot))
mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_CHECK_SIM_STATUS, slot, 0));
…
其自身的handleMessage处理了这个消息,处理网络状态(通过handleCellConn(),再使用服务IPhoneStatesMgrService的方法verifyPhoneState),并在这里结束了这一阶段的处理过程。可以看到我们的9个处理类还并没有执行完。
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_CHECK_SIM_STATUS:
final int result = mRequest.getCellConnMgr().handleCellConn(msg.arg1,
CellConnMgr.REQUEST_TYPE_ROAMING, mRunnable);
log("result = " + result);
if (result == mRequest.getCellConnMgr().RESULT_WAIT) {
showProgressIndication(mRequest);
}
break;
下面是函数调用栈验证分析过程:
ContactsSimStatusCallOptionHandler(SimStatusCallOptionHandler).handleRequest(Request) line: 119
ContactsSimSelectionCallOptionHandler(SimSelectionCallOptionHandler).onMakeCall(SimSelectionCallOptionHandler$CallbackArgs) line: 432
ContactsSimSelectionCallOptionHandler(SimSelectionCallOptionHandler).handleRequest(Request) line: 279
ContactsVideoCallOptionHandler(VideoCallOptionHandler).handleRequest(Request) line: 64
ContactsInternetCallOptionHandler(InternetCallOptionHandler).handleRequest(Request) line: 64
ContactsFirstCallOptionHandler(FirstCallOptionHandler).handleRequest(Request) line: 73
ContactsFirstCallOptionHandler.handleRequest(Request) line: 58
ContactsCallOptionHandler(CallOptionHandler).doCallOptionHandle(Context, Context, Intent, CallOptionBaseHandler$ICallOptionResultHandle, CellConnMgr, ITelephony, boolean, boolean) line: 54
ContactsCallOptionHandler.doCallOptionHandle(Intent) line: 43
DialpadFragment.dialButtonPressedInner(String, int) line: 2670
DialpadFragment.dialButtonPressed() line: 2634
DialpadFragment.onClick(View) line: 1531
【Tips:】对于MTK平台,调试会有些文件找不到,如ContactsCallOptionHandler,可在.classpath中加入一条记录
<classpathentry kind="src" path="packages/apps/Phone/common/src"/>
3)阶段三
在这个阶段,我们需要找到拨号产生的intent如何向下传递的。
SimStatusCallOptionHandler的mRunnable将会被执行到,它处理了sim状态之后,又调用到mSuccessor。
private Runnable mRunnable = new Runnable() {
public void run() {
final int result = mRequest.getCellConnMgr().getResult();
final int slot = mRequest.getCellConnMgr().getPreferSlot();
log("run, result = " + result + " slot = " + slot);
dismissProgressIndication();
if (result != com.mediatek.CellConnService.CellConnMgr.RESULT_STATE_NORMAL) {
mRequest.getResultHandler().onHandlingFinish();
} else {
int oldSolt = mRequest.getIntent().getIntExtra(Constants.EXTRA_SLOT_ID, -1);
log("afterCheckSIMStatus, oldSolt = " + oldSolt);
if (oldSolt != -1 && slot != oldSolt) {
mRequest.getIntent().putExtra(Constants.EXTRA_SLOT_ID, slot);
}
//mRequest.getResultHandler().onContinueCallProcess(mRequest.getIntent());
if (null != mSuccessor) {
mSuccessor.handleRequest(mRequest);
}
}
}
};
这样又走回mCallOptionHandlerList所设计的串行迭代处理逻辑。后面将继续执行VoiceMailCallOptionHandler、InternationalCallOptionHandler、IpCallOptionHandler和FinalCallOptionHandler这几个类的判断处理过程。在FinalCallOptionHandler里面handleRequest结束了串行处理过程,
public void handleRequest(final Request request) {
log("handleRequest()");
request.getResultHandler().onContinueCallProcess(request.getIntent());
}
最后执行到ContactsCallOptionHandler的onContinueCallProcess,这里就通过sendBroadcast将intent广播出去了,并通过setClassName设置了接收类为com.mediatek.phone.OutgoingCallReceiver,到这里就完成了拨号盘拨号请求阶段的任务。
public void onContinueCallProcess(Intent intent) {
/** M: Ensure the Dialogs be dismissed before launch a new "call" @{ */
dismissDialogs();
/** @} */
intent.setAction(Constants.OUTGOING_CALL_RECEIVER);
intent.setClassName(Constants.PHONE_PACKAGE, Constants.OUTGOING_CALL_RECEIVER);
ContactsApplication.getInstance().sendBroadcast(intent);
}
下面是函数调用栈验证分析过程:
ContactsCallOptionHandler.onContinueCallProcess(Intent) line: 59
FinalCallOptionHandler.handleRequest(Request) line: 47
ContactsIpCallOptionHandler(IpCallOptionHandler).handleRequest(Request) line: 91
ContactsInternationalCallOptionHandler(InternationalCallOptionHandler).handleRequest(Request) line: 308
ContactsVoiceMailCallOptionHandler(VoiceMailCallOptionHandler).handleRequest(Request) line: 66
SimStatusCallOptionHandler$2.run() line: 94
2.1.2 拨号请求(Phone)
拨号盘里的拨号过程是在contact进程实现的,当它发出拨号广播后,就被Phone进程的com.mediatek.phone.OutgoingCallReceiver接收到,OutgoingCallReceiver的onReceive收到广播事件后,又发送一个ACTION_NEW_OUTGOING_CALL类型的广播,它自己能接收并处理这个广播,并调用callController.placeCall进行下一步的呼叫处理。
public void onReceive(Context context, Intent intent) {
if (Constants.OUTGOING_CALL_RECEIVER.equals(intent.getAction())) { //收到contact的广播
///Profiler.trace(Profiler.OutgoingCallReceiverEnterActionOnReceive);
Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);
//String number = PhoneNumberUtils.getNumberFromIntent(intent, context);
String number = CallOptionUtils.getInitialNumber(context, intent);
OutgoingCallBroadcaster.sendNewCallBroadcast(context, intent, number, false, this);//转发出去
} else if (Intent.ACTION_NEW_OUTGOING_CALL.equals(intent.getAction())) {//自己接收
PhoneGlobals.getInstance().callController.placeCall(newIntent);//调用placeCall
}
…
callController的placeCall完成3部分的工作,1)检查intent的数据并调用placeCallInternal,向底层发起呼叫请求,2)处理底层的返回结果 3)启动通话界面
public void placeCall(Intent intent) {
…
CallStatusCode status = placeCallInternal(intent);
switch (status) {
// Call was placed successfully:
case SUCCESS:
case EXITED_ECM:
…
mApp.displayCallScreen(!intent.getBooleanExtra(Constants.EXTRA_IS_VIDEO_CALL, false), forPlaceCall);
}
placeCallInternal对intent和当前手机状态进行了一些检查,看是是紧急拨号、是否允许拨号等,在进一步向下调用PhoneUtils.placeCallGemini。
…
callStatus = PhoneUtils.placeCallGemini(mApp,
phone,
number,
contactUri,
(isEmergencyNumber || isEmergencyIntent),
inCallUiState.providerGatewayUri,
slot);
placeCallGemini则进一步调用GeminiRegister.dial,GeminiRegister.dial 调用((MTKCallManager) callManager).dialGemini,其中MTKCallManager是MTK的封装类,最终会调用的CallManager的dial,在CallManager.dial中调用了basePhone.dial(dialString),basePhone实际上是一个GSMPhone的实例,所以最终调用下面的方法。其中mCT则是GsmCallTracker的实例,它在GSMPhone创建的时候被创建,GSMPhone通过它来完成呼叫相关的处理。
public Connection
dial (String dialString, UUSInfo uusInfo) throws CallStateException {
GsmMmiCode mmi = GsmMmiCode.newFromDialString(networkPortion, this, mUiccApplication.get());
if (LOCAL_DEBUG) Cclog("dialing w/ mmi '" + mmi + "'...");
//MTK-END [mtk04070][111118][ALPS00093395]Add Cclog
if (mmi == null) {
return mCT.dial(newDialString, uusInfo);
} else if (mmi.isTemporaryModeCLIR()) {
return mCT.dial(mmi.dialingNumber, mmi.getCLIRMode(), uusInfo);
} else {
mPendingMMIs.add(mmi);
mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
mmi.processCode();
// FIXME should this return null or something else?
return null;
}
}
在GsmCallTracker的dial方法中,会先将音频通道mute,再进行cm.dial拨号,之后再将状态信息更新到应用。其中cm是RIL.java的实例。这里还封装了一个事件为EVENT_DIAL_CALL_RESULT消息,但RIL层响应拨号请求后,被自身的handler处理。(clearDisconnected()和canDial()清空过去的非连接状态的Connections,然后检查是否可以拨打电话。接着检查foregroundCall是否处于Active状态,若是则调用switchWaitingOrHoldingAndActive将它们切换到后台,调用fakeHoldForegroundBeforeDial将前台中的连接全部切换到后台,并且状态变为HOLDING。在进行这些前期检查和准备后,创建一个GsmConnection实例即pendingMO,检查传递过来的电话号码是否有效合法,若不合法则调用pollCallsWhenSafe(),目的是将其标为dropped;若合法则设置为非静音后,调用RIL.dial进行拨号。最后,更新Phone状态并通知给注册者。)
synchronized Connection
dial (String dialString, int clirMode, UUSInfo uusInfo) throws CallStateException {
clearDisconnected();
…
setMute(false);
if (PhoneNumberUtils.isEmergencyNumber(dialString)
cm.emergencyDial(ret.toString(), clirMode, uusInfo, obtainCompleteMessage(EVENT_DIAL_CALL_RESULT));
} else {
cm.dial(ret.toString(), clirMode, uusInfo, obtainCompleteMessage(EVENT_DIAL_CALL_RESULT));
}
updatePhoneState();
phone.notifyPreciseCallStateChanged();
}
RIL.dial实现如下,封装一个RIL_REQUEST_DIAL类型的消息发送出去,将拨号请求发送给RILD,之后RILD再使用AT指令向modem发送呼叫请求。对于RILD之后的处理我们暂不进行分析了。(AT及modem这部分是feature phone很常规的呼叫流程过程,不属于android的特色代码。)
public void
dial(String address, int clirMode, UUSInfo uusInfo, Message result) {
RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);
rr.mp.writeString(address);
rr.mp.writeInt(clirMode);
rr.mp.writeInt(0); // UUS information is absent
if (uusInfo == null) {
rr.mp.writeInt(0); // UUS information is absent
} else {
rr.mp.writeInt(1); // UUS information is present
rr.mp.writeInt(uusInfo.getType());
rr.mp.writeInt(uusInfo.getDcs());
rr.mp.writeByteArray(uusInfo.getUserData());
}
send(rr);
}
这个拨号过程的调用栈如下,
下面是函数调用栈验证分析过程:
RIL.dial(String, int, UUSInfo, Message) line: 1148
GsmCallTracker.dial(String, int, UUSInfo) line: 420
GsmCallTracker.dial(String, UUSInfo) line: 443
GSMPhone.dial(String, UUSInfo) line: 1007
GSMPhone.dial(String) line: 999
CallManager.dial(Phone, String) line: 1382
MTKCallManager.dialGemini(Phone, String, int) line: 147
GeminiRegister.dial(Object, Phone, String, int) line: 964
PhoneUtils.placeCallGemini(Context, Phone, String, Uri, boolean, Uri, int) line: 859
CallController.placeCallInternal(Intent) line: 746
CallController.placeCall(Intent) line: 349
OutgoingCallReceiver.onReceive(Context, Intent) line: 72
LoadedApk$ReceiverDispatcher$Args.run() line: 788
RIL响应后,会调用processResponse,再调用processSolicited,processSolicited对RIL_REQUEST_DIAL处理比较简单,没有实质性的代码。在最后的代码里面,如下,则对之前封装的EVENT_DIAL_CALL_RESULT消息进行处理,进行回调状态处理。
if (rr.mResult != null) {
AsyncResult.forMessage(rr.mResult, ret, null);
rr.mResult.sendToTarget();
}
这个回调处理实际由GsmCallTracker的handleMessage完成。
EVENT_DIAL_CALL_RESULT执行的调用栈,
GsmCallTracker.handleMessage(Message) line: 1271
GsmCallTracker(Handler).dispatchMessage(Message) line: 107
2.1.3 进入通话界面
由于之前提到的callController的placeCall里启动了呼叫界面,拨号请求完成后,会进入inCallScreen,onCreate会被执行,开始构建通话界面。
在拨号请求后,updatePhoneState会被调用到,最初处理的是phone state由IDLE到OFF HOOK的变化,updatePhoneState的主要作用是更新Phone的呼叫状态,并将状态通知。
private void updatePhoneState() {
PhoneConstants.State oldState = state;
if (ringingCall.isRinging()) {
state = PhoneConstants.State.RINGING;
} else if (pendingMO != null ||
!(foregroundCall.isIdle() && backgroundCall.isIdle())) {
state = PhoneConstants.State.OFFHOOK;
} else {
state = PhoneConstants.State.IDLE;
}
if (state == PhoneConstants.State.IDLE && oldState != state) {
voiceCallEndedRegistrants.notifyRegistrants(
new AsyncResult(null, null, null));
} else if (oldState == PhoneConstants.State.IDLE && oldState != state) {
voiceCallStartedRegistrants.notifyRegistrants (
new AsyncResult(null, null, null));
}
if (state != oldState) {
phone.notifyPhoneStateChanged();
}
}
代码里还有两种特殊情况,
如果新状态变为IDLE,则表示通话断开,要发给voiceCallEndedRegistrants里面的注册函数处理,在GsmDataConnectionTracker里通过
p.getCallTracker().registerForVoiceCallEnded (this, DctConstants.EVENT_VOICE_CALL_ENDED, null); 来注册。
如果上一个状态是IDLE,则表示新建立通话,要发给voiceCallStartedRegistrants里面的注册函数处理,在GsmDataConnectionTracker里通过p.getCallTracker().registerForVoiceCallStarted (this, DctConstants.EVENT_VOICE_CALL_STARTED, null);
对于EVENT_VOICE_CALL_ENDED和.EVENT_VOICE_CALL_STARTED 的处理,先通过GsmDataConnectionTracker的handlemessage处理,没有匹配的case,再转交给父类 ,父类调用onVoiceCallEnded和onVoiceCallStarted方法进行处理,这两个方法在父类是抽象函数,实际又调用到了子类的函数。
onVoiceCallStarted
protected void onVoiceCallStarted() {
if (DBG) log("onVoiceCallStarted");
if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);
}
if (isConnected() && ! mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
if (DBG) log("onVoiceCallStarted stop polling");
stopNetStatPoll();
stopDataStallAlarm();
}
}
onVoiceCallEnded
protected void onVoiceCallEnded() {
if (DBG) log("onVoiceCallEnded");
if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);
}
if (isConnected()) {
if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
startNetStatPoll();
startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);
} else {
// clean slate after call end.
resetPollStats();
}
}
// reset reconnect timer
setupDataOnReadyApns(Phone.REASON_VOICE_CALL_ENDED);
}
我们重点关注notifyPhoneStateChanged是怎样将信息传递到应用上层的,onVoiceCallEnded和onVoiceCallStarted的过程类似。(使用SIM1拨号,观察log居然发现sim2收到了EVENT_VOICE_CALL_STARTED_PEER事件并进行了处理,待分析。)
1)
notifyPhoneStateChanged首先通过DefaultPhoneNotifier的notifyPhoneState,将信息传递给ITelephonyRegistry的notifyCallState,再通过AIDL,将消息传递给TelephonyRegistry的notifyCallState,这里会进行注册函数的遍历,并将状态通知到每个注册的函数,之后还会进行广播。
public void notifyCallState(int state, String incomingNumber) {
if (!checkNotifyPermission("notifyCallState()")) {
return;
}
synchronized (mRecords) {
mCallState = state;
mCallIncomingNumber = incomingNumber;
for (Record r : mRecords) {
if ((r.events & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
try {
r.callback.onCallStateChanged(state, incomingNumber); //遍历
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
}
}
handleRemoveListLocked();
}
broadcastCallStateChanged(state, incomingNumber); //广播
}
2)
TelephonyRegistry里遍历的回调函数onCallStateChanged会将消息发给IPhoneStateListener,通过AIDL方法,由PhoneStateListener接收并处理。
3)
在应用最上层,DialpadFragment.java通过SlotUtils.listenAllSlots间接向TelephonyRegistry注册,
SlotUtils.listenAllSlots(getActivity(), mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE
| PhoneStateListener.LISTEN_SERVICE_STATE);
并且,DialpadFragment.java创建PhoneStateListener的实例,并实现了onCallStateChanged方法。
public void onCallStateChanged(int state, String incomingNumber) {
if (state == TelephonyManager.CALL_STATE_IDLE) {
final boolean phoneIsInUse = phoneIsInUse();
if (dialpadChooserVisible()) {
if (!phoneIsInUse) {
showDialpadChooser(false);
adjustListViewLayoutParameters();
}
}
//modified by yanbin.zhang for fr441210 begin 2013-06-11
if (!phoneIsInUse && !simIsnotInUse()) {
//modified by yanbin.zhang for fr441210 begin 2013-06-11
if (mDigits != null) {
mDigits.setHint(null);
}
}
}
}
在PhoneStateListener的handleMessage处理LISTEN_CALL_STATE时,通过如下代码,调用DialpadFragment.java里的onCallStateChanged方法。
case LISTEN_CALL_STATE:
PhoneStateListener.this.onCallStateChanged(msg.arg1, (String)msg.obj);
break;
这样就完成了Phone state的向上传递。
2.2 呼叫建立
2.2.1 ECPI
RILJ收到呼叫请求并发出相应后,随即收到RILC的事件并进行处理,如下,
case RIL_UNSOL_CALL_PROGRESS_INFO:
if (RILJ_LOGD) unsljLogvRet(response, ret);
if (mCallProgressInfoRegistrants != null) {
mCallProgressInfoRegistrants.notifyRegistrants(
new AsyncResult (null, ret, null));
其中mCallProgressInfoRegistrants里面的处理实体是在GsmCallTracker注册完成的,注册函数为cm.registerForCallProgressInfo(this, EVENT_CALL_PROGRESS_INFO, null);,里面生成一个Registrants并添加到mCallProgressInfoRegistrants:mCallProgressInfoRegistrants.add()。
这样,EVENT_CALL_PROGRESS_INFO消息就会被传递给GsmCallTracker.java的handleMessage来处理,事件的处理过程在handleCallProgressInfo里面。
EVENT_CALL_PROGRESS_INFO携带的消息格式和说明如下,在handleCallProgressInfo有说明。
/* +ECPI:<call_id>, <msg_type>, <is_ibt>, <is_tch>, <dir>, <call_mode>[, <number>, <toa>], "",<cause>
*
* if msg_type = DISCONNECT_MSG or ALL_CALLS_DISC_MSG,
* +ECPI:<call_id>, <msg_type>, <is_ibt>, <is_tch>,,,"",,"",<cause>
*
* if msg_type = STATE_CHANGE_HELD or STATE_CHANGE_ACTIVE or STATE_CHANGE_DISCONNECTED,
* +ECPI:<call_id>, <msg_type>,,,,,"",,""
*
* if others,
* +ECPI:<call_id>, <msg_type>, <is_ibt>, <is_tch>, <dir>, <call_mode>[, <number>, <toa>], ""
* msg_type:
* 0 O CSMCC_SETUP_MSG 呼叫建立,用于MT
* 1 X CSMCC_DISCONNECT_MSG 呼叫断开
* 2 O CSMCC_ALERT_MSG 等待接通提示
* 3 X CSMCC_CALL_PROCESS_MSG 呼叫处理
* 4 X CSMCC_SYNC_MSG 信息同步
* 5 X CSMCC_PROGRESS_MSG 呼叫处理
* 6 O CSMCC_CALL_CONNECTED_MSG 呼叫连接
* 129 X CSMCC_ALL_CALLS_DISC_MSG 断开所有呼叫
* 130 O CSMCC_MO_CALL_ID_ASSIGN_MSG CALL ID指定
* 131 O CSMCC_STATE_CHANGE_HELD 呼叫保持状态
* 132 O CSMCC_STATE_CHANGE_ACTIVE 呼叫激活状态
* 133 O CSMCC_STATE_CHANGE_DISCONNECTED 呼叫断开
* 134 X CSMCC_STATE_CHANGE_MO_DISCONNECTING MO主动断开呼叫
*/
handleCallProgressInfo,从名字可以看出,它负责处理呼叫处理信息,分析其代码,其处理的呼叫分MO、MT两大类,涉及的过程和知识点有呼叫建立和维护的各种状态,处理语音和视频呼叫、多方通话等功能。
上面是一个MO呼叫的AT数据,从LOG上可以看到,ATD拨号命令发出后,收到很多ECPI数据,ECPI含义是呼叫处理信息(Call Process information)。可以看到MO呼叫建立时,会依次收到msg_type为130,3,4,2,132,6的CPI信息,从前面的描述可以看到,MO呼叫建立过程为,
130 CALL ID指定
3 CSMCC_CALL_PROCESS_MSG 呼叫处理
4 CSMCC_SYNC_MSG 信息同步
2 CSMCC_ALERT_MSG 等待接通提示
132 CSMCC_STATE_CHANGE_ACTIVE 呼叫激活状态
6 CSMCC_CALL_CONNECTED_MSG 呼叫连接
1.1.1 ECPI消息处理
1)
先分析msg_type为130的处理过程,数据如下:
+ECPI:<call_id>, <msg_type>, <is_ibt>, <is_tch>, <dir>, <call_mode>[, <number>, <toa>], "",<cause>
+ECPI: 1, 130, 0, 0 ,0, 0, "10086",129,""
由于代码量较大,我们只列出LOG表示handleCallProgressInfo代码的执行流程,这需要对照代码分析。LOG分两部分说明,
第一部分:
可以看出conn == null, pendingMO != null,Phone State=OFFHOOK,Foreground call,Background call,这些变量对我们分析呼叫的状态十分重要,我们将在其他地方展开详述。
期间调用的处理函数主要为
第二部分:
可以看出newRinging = null,droppedDuringPoll.size=0,hasNonHangupStateChanged=true。
期间调用的处理函数主要为internalClearDisconnected,updatePhoneState,phone.notifyPreciseCallStateChanged。
internalClearDisconnected主要完成清除ringingCall、foregroundCall、backgroundCall里已挂断电话的connection。这几个呼叫实例是GsmCall类,继承自Call,维护call connection的状态。通过attach将呼叫连接添加到ArrayList里面,再在呼叫过程中,对这些这些连接进行维护。
private void internalClearDisconnected() {
ringingCall.clearDisconnected();
foregroundCall.clearDisconnected();
backgroundCall.clearDisconnected();
}
updatePhoneState的流程分析见上文。
notifyPreciseCallStateChanged将state通知到注册到的应用,应用注册是通过PhoneBase 的registerForPreciseCallStateChanged完成的。
对于呼叫部分,在CallManager里的registerForPhoneStates,registerSinglePhoneStates里面进行注册。CallManager再在handler里面处理EVENT_PRECISE_CALL_STATE_CHANGED消息,并将消息通知给mPreciseCallStateRegistrantsGemini和mPreciseCallStateRegistrants
的注册客户。
mPreciseCallStateRegistrantsGemini里存放的实例是通过registerForPreciseCallStateChangedEx注册的,经查找没有发现有客户注册。
mPreciseCallStateRegistrants里存放的实例是通过registerForPreciseCallStateChanged来注册,对于呼叫部分,是InCallScreen.java来注册的。InCallScreen的handleMessage处理PHONE_STATE_CHANGED事件,调用onPhoneStateChanged,更新通话界面和相关状态。
2)
再分析msg_type为2的处理过程,数据如下:
+ECPI:<call_id>, <msg_type>, <is_ibt>, <is_tch>, <dir>, <call_mode>[, <number>, <toa>], "",<cause>
ECPI: 1, 2, 1, 1, 0, 0, "10086",129,""
msg_type为2,State变为ALERTING,会通知到应用,这个状态通知上去之后,界面会变化为等待对方接听的界面(影响界面变化的应该为notifyPreciseCallStateChanged)。
期间调用的处理函数主要为internalClearDisconnected,updatePhoneState,phone.notifyPreciseCallStateChanged。
3)
对于其他类型的消息分析过程类似,不详细说明。
1.1.1 界面更新
notifyPreciseCallStateChanged将phone state通知到InCallScreen.java,InCallScreen的handleMessage处理PHONE_STATE_CHANGED事件,调用onPhoneStateChanged,onPhoneStateChanged再调用requestUpdateScreen来更新界面。而onPhoneStateChanged则给mHandler发送REQUEST_UPDATE_SCREEN消息,让mHandler再调用updateScreen来刷新屏幕,
case REQUEST_UPDATE_SCREEN:
case REQUEST_UPDATE_SCREEN_EXT:
updateScreen();
break;
updateScreen负责界面的刷新。主要的刷新函数是mCallCard.updateState(mCM)。
1.1 呼叫结束
1.1.1 ECPI消息处理
呼叫结束的AT数据如下。
可以看出,需要处理的CPI信息依次如下,
* 134 X CSMCC_STATE_CHANGE_MO_DISCONNECTING MO主动断开呼叫
* 1 X CSMCC_DISCONNECT_MSG 呼叫断开
* 129 X CSMCC_ALL_CALLS_DISC_MSG 断开所有呼叫
* 133 O CSMCC_STATE_CHANGE_DISCONNECTED 呼叫断开
对象这些信息的代码分析就不再赘述了。
133的处理log:
updatePhoneState的时候会调用onVoiceCallEnded。并且sim2会收到一个EVENT_VOICE_CALL_ENDED_PEER事件,并执行onVoiceCallEndedPeer。
1.1.1 界面更新
断开连接时,通过onDisconnect来更新界面,调用栈如下:
InCallScreen.updateScreen(boolean) line: 4033
InCallScreen.onDisconnect(AsyncResult, int) line: 3527
InCallScreen.access$400(InCallScreen, AsyncResult, int) line: 178
InCallScreen$1.handleMessage(Message) line: 523
InCallScreen$1(Handler).dispatchMessage(Message) line: 107
1.1 MO呼叫流程图
根据以上分析,一个MO呼叫并主动断开的流程图如下:
呼叫请求
呼叫建立:
呼叫主动断开:
1.1 MT呼叫
1.1.1 接听
ringingCall正处于INCOMING则调用RIL.acceptCall去接听电话;若是WAITING状态,则调用switchWaitingOrHoldingAndActive将其切换到前台。
1.1.2 拒接
当ringingCall处于INCOMING时,则调用RIL.rejectCall拒绝;否则抛出异常,表示没有来电却去接听它。
1.2 挂断呼叫
它区分是ringingCall、foregroundCall还是backgroundCall。若是ringingCall,则调用RIL.hangupWaitingOrBackground;若是foregroundCall,并且是在DIALING或ALERTING状态则调用调用hangup (GsmConnection conn)挂断,否则调用hangupForegroundResumeBackground挂断前台通话后恢复后台通话;若是backgroundCall且ringingCall在响铃,则调用hangupAllConnections挂断所有在backgroundCall的通话连接,否则调用hangupWaitingOrBackground挂断呼叫等待和后台通话。
switchWaitingOrHoldingAndActive():进行电话切换
conference():进行电话会议
separate():分离出一路通话
fakeHoldForegroundBeforeDial()将前台电话(ForegroundCall)中的电话连接(GSMConnections)clone后台后,将这些连接删除,将Foreground置为IDLE状态,将后台电话BackgroundCall置为HOLDING状态。
clearDisconnected():清除已Disconnected的电话连接并更新电话状态,然后通知注册者当前最新的状态。
internalClearDisconnected():将ringingCall、 foregroundCall和 backgroundCall中状态为DISCONNECTED的 connections清除掉,若没有connection则将该GSMCall置为idle状态。
updatePhoneState():更新Phone状态,并向注册者通知语音通话开始或结束。
canDial():检查是否可以拨打电话,只有同时满足下列条件才可以:(1)射频Raido未关闭(2)PendingMO这个Connection为空(3)RingCall这个GSMCall未处于响铃状态(4)系统没有禁止电话拨打功能(5)ForegroundCall和BackgroundCall这2个GSMCall都不处于活动状态。其中当为INCOMING 和WAITING表示在响铃;当为DIALING 和 ALERTING时 表示在拨号;当不为IDLE 、 DISCONNECTED 和DISCONNECTING时表示活动状态,即处在上述的响铃、拨号、ACTIVE 和HOLDING时表示处于活动状态。
canConference():当ForegroundCall处于Active、BackgroundCall处于HOLDING以及它们的连接数少于5个时则可进行电话会议
canTransfer():当ForegroundCall处于Active、BackgroundCall处于HOLDING时则可进行交换。
hangup (GsmConnection conn):挂断某路电话连接
hangupWaitingOrBackground():挂断呼叫等待和后台电话
hangupForegroundResumeBackground():挂断前台电话并恢复后台电话
hangupConnectionByIndex(GsmCall call, int index):挂断某个索引指定的前台通话
hangupAllConnections(GsmCall call):挂断所有电话通路