Android电话拨打流程源码分析
前面分析了电话拨号界面及电话呼叫界面,由于Android的电话Phone设计的很复杂,因此先从UI层入手分析。想要了解Android的电话拨号UI,请查看Android电话拨号UI分析,电话拨号UI在Contacts包中。想要了解Android电话呼叫UI,请查看Android电话Phone UI分析,该UI在Phone包中,了解完电话想要UI后,还必须首先了解Android的Phone设计框架,Android电话Phone设计框架介绍介绍了Phone的框架设计及Phone进程的启动,本文以源码的形式介绍Android的电话拨打流程。点击Launcher上的拨号图标,首先进入电话拨号界面,前面已经分析了,该UI在Contacts包中,启动显示的是DialtactsActivity,关于DialtactsActivity的布局解析、UI布局在Android电话拨号UI分析中有详细的分析,这里不在重复介绍。我们从点击拨号按钮开始分析电话的拨号流程:
DialpadFragment.java
[html] view plain copy- publicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer,BundlesavedState){
- ...
- //Checkwhetherweshouldshowtheonscreen"Dial"button.
- mDialButton=mAdditionalButtonsRow.findViewById(R.id.dialButton);
- if(r.getBoolean(R.bool.config_show_onscreen_dial_button)){
- mDialButton.setOnClickListener(this);
- }else{
- mDialButton.setVisibility(View.GONE);//It'sVISIBLEbydefault
- mDialButton=null;
- }
- ...
拨号按钮的单击事件响应:
[java] view plain copy- publicvoidonClick(Viewview){
- switch(view.getId()){
- caseR.id.dialButton:{
- mHaptic.vibrate();
- dialButtonPressed();
- return;
- }
- ...
- }
- }
调用dialButtonPressed()函数发起电话呼叫
[java] view plain copy- publicvoiddialButtonPressed(){
- if(mDigits==null){
- Log.e(TAG,"dialButtonPressed,mDigits==null");
- return;
- }
- //未输入号码处理
- if(isDigitsEmpty()){
- handleDialButtonClickWithEmptyDigits();
- }else{
- finalStringnumber=mDigits.getText().toString();
- //"persist.radio.otaspdial"isatemporaryhackneededforonecarrier'sautomated
- //testequipment.
- if(number!=null&&!TextUtils.isEmpty(mProhibitedPhoneNumberRegexp)
- &&number.matches(mProhibitedPhoneNumberRegexp)
- &&(SystemProperties.getInt("persist.radio.otaspdial",0)!=1)){
- Log.i(TAG,"Thephonenumberisprohibitedexplicitlybyarule.");
- if(getActivity()!=null){
- DialogFragmentdialogFragment=ErrorDialogFragment.newInstance(
- R.string.dialog_phone_call_prohibited_title);
- dialogFragment.show(getFragmentManager(),"phone_prohibited_dialog");
- }
- //号码输入不正确.
- mDigits.getText().clear();
- }elseif(number!=null&&(number.startsWith(",")||number.startsWith(";"))){
- mDigits.getText().clear();
- if(getActivity()!=null){
- Toast.makeText(getActivity(),getText(R.string.invalid_number),
- Toast.LENGTH_SHORT).show();
- }
- }else{
- //启动电话呼叫界面
- finalIntentintent=ContactsUtils.getCallIntent(number,(getActivity()instanceofDialtactsActivity?
- ((DialtactsActivity)getActivity()).getCallOrigin():null));
- startActivity(intent);
- mClearDigitsOnStop=true;
- mDigits.getText().clear();
- if(mFlagIntentNumber){
- getActivity().finish();
- }
- }
- }
- }
函数首先对输入的号码进行检查,如果没有输入号码,直接按下拨号按钮,则调用handleDialButtonClickWithEmptyDigits函数来处理
[java] view plain copy- privatevoidhandleDialButtonClickWithEmptyDigits(){
- if(phoneIsCdma()&&phoneIsOffhook()){
- //ThisisreallyCDMAspecific.OnGSMisitpossible
- //tobeoffhookandwantedtoadda3rdpartyusing
- //theredialfeature.
- startActivity(newFlashIntent());
- }else{
- if(mDigits!=null&&!TextUtils.isEmpty(mLastNumberDialed)){
- //Recallthelastnumberdialed.
- mDigits.setText(mLastNumberDialed);
- //...andmovethecursortotheendofthedigitsstring,
- //soyou'llbeabletodeletedigitsusingtheDelete
- //button(justasifyouhadtypedthenumbermanually.)
- //
- //NoteweusemDigits.getText().length()here,not
- //mLastNumberDialed.length(),sincetheEditTextwidgetnow
- //containsa*formatted*versionofmLastNumberDialed(dueto
- //mTextWatcher)anditslengthmayhavechanged.
- mDigits.setSelection(mDigits.getText().length());
- }else{
- //There'sno"lastnumberdialed"orthe
- //backgroundqueryisstillrunning.There's
- //nothingusefulfortheDialbuttontodoin
- //thiscase.Note:withasoftdialbutton,this
- //canneverhappenssincethedialbuttonis
- //disabledundertheseconditons.
- playTone(ToneGenerator.TONE_PROP_NACK);
- }
- }
- }
如果号码输入正确合法,则使用ContactsUtils工具类来创建一个Intent。
DialtactsActivity.java
[java] view plain copy- publicStringgetCallOrigin(){
- return!isDialIntent(getIntent())?CALL_ORIGIN_DIALTACTS:null;
- }
- /**Returnstrueifthegivenintentcontainsaphonenumbertopopulatethedialerwith*/
- privatebooleanisDialIntent(Intentintent){
- finalStringaction=intent.getAction();
- if(Intent.ACTION_DIAL.equals(action)||ACTION_TOUCH_DIALER.equals(action)){
- returntrue;
- }
- if(Intent.ACTION_VIEW.equals(action)){
- finalUridata=intent.getData();
- if(data!=null&&Constants.SCHEME_TEL.equals(data.getScheme())){
- returntrue;
- }
- }
- returnfalse;
- }
从Launcher点击拨号图标进入的,因此isDialIntent返回true,getCallOrigin返回null
ContactsUtils.java
[java] view plain copy- publicstaticIntentgetCallIntent(Stringnumber,StringcallOrigin){
- returngetCallIntent(getCallUri(number),callOrigin);
- }
- publicstaticIntentgetCallIntent(Uriuri,StringcallOrigin){
- finalIntentintent=newIntent(Intent.ACTION_CALL_PRIVILEGED,uri);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- if(callOrigin!=null){
- intent.putExtra(DialtactsActivity.EXTRA_CALL_ORIGIN,callOrigin);
- }
- returnintent;
- }
action为Intent.ACTION_CALL_PRIVILEGED,因此使用隐式启动OutgoingCallBroadcaster
因此Phone进程中的OutgoingCallBroadcaster将被启动。google对电话拨号步骤有详细的说明:
/*
* Here's the most typical outgoing call sequence:
*
* (1) OutgoingCallBroadcaster receives a CALL intent and sends the
* NEW_OUTGOING_CALL broadcast
*
* (2) The broadcast finally reaches OutgoingCallReceiver, which stashes
* away a copy of the original CALL intent and launches
* SipCallOptionHandler
*
* (3) SipCallOptionHandler decides whether this is a PSTN or SIP call (and
* in some cases brings up a dialog to let the user choose), and
* ultimately calls CallController.placeCall() (from the
* setResultAndFinish() method) with the stashed-away intent from step
* (2) as the "intent" parameter.
*
* (4) Here in CallController.placeCall() we read the phone number or SIP
* address out of the intent and actually initiate the call, and
* simultaneously launch the InCallScreen to display the in-call UI.
*
* (5) We handle various errors by directing the InCallScreen to
* display error messages or dialogs (via the InCallUiState
* "pending call status code" flag), and in some cases we also
* sometimes continue working in the background to resolve the
* problem (like in the case of an emergency call while in
* airplane mode). Any time that some onscreen indication to the
* user needs to change, we update the "status dialog" info in
* the inCallUiState and (re)launch the InCallScreen to make sure
* it's visible.
*/
如OutgoingCallBroadcaster接收 CALL 和CALL_PRIVILEGED 两种Intents,然后广播出ACTION_NEW_OUTGOING_CALL intent,让别的应用程序有机会去监视这些intent,最后这些呼叫intent又被自己收到转换,启动InCallScreen.
src\com\android\phone\OutgoingCallBroadcaster.java
[java] view plain copy- protectedvoidonCreate(Bundleicicle){
- super.onCreate(icicle);
- setContentView(R.layout.outgoing_call_broadcaster);
- mWaitingSpinner=(ProgressBar)findViewById(R.id.spinner);
- Intentintent=getIntent();
- if(DBG){
- finalConfigurationconfiguration=getResources().getConfiguration();
- Log.v(TAG,"onCreate:this="+this+",icicle="+icicle);
- Log.v(TAG,"-getIntent()="+intent);
- Log.v(TAG,"-configuration="+configuration);
- }
- if(icicle!=null){
- //icicle不为空,表示重新初始化先前关闭的OutgoingCallBroadcaster,
- //Inpracticethishappensveryrarely(becausethelifetime
- //ofthisactivityissoshort!),butit*can*happenifthe
- //frameworkdetectsaconfigurationchangeatexactlythe
- //rightmoment;
- //Inthiscase,donothing.OuronCreate()methodhasalready
- //runonce(withicicle==nullthefirsttime),whichmeans
- //thattheNEW_OUTGOING_CALLbroadcastforthisnewcallhas
- //alreadybeensent.
- Log.i(TAG,"onCreate:non-nullicicle!"
- +"Bailingout,notsendingNEW_OUTGOING_CALLbroadcast...");
- return;
- }
- //处理得到的intent
- processIntent(intent);
- if(DBG)Log.v(TAG,"AttheendofonCreate().isFinishing():"+isFinishing());
- }
函数直接调用processIntent函数处理前面发送过来的intent,该方法可以处理以下三种actions,
CALL (action for usual outgoing voicecalls)
CALL_PRIVILEGED (can come from built-inapps like contacts / voice dialer / bluetooth)
CALL_EMERGENCY (from the EmergencyDialerthat's reachable from the lockscreen.)
对于数据为tel: URI的电话处理流程为:OutgoingCallReceiver -> SipCallOptionHandler ->InCallScreen.
对于数据为sip: URI的网络电话,则跳过NEW_OUTGOING_CALL广播,直接调用SipCallOptionHandler处理
对于数据为voicemail: URIs的语音信箱处理同电话处理流程类似
[java] view plain copy- privatevoidprocessIntent(Intentintent){
- if(DBG){
- Log.v(TAG,"processIntent()="+intent+",thread:"+Thread.currentThread());
- }
- finalConfigurationconfiguration=getResources().getConfiguration();
- //电话拨号只对具有语音通信能力的设备而言
- if(!PhoneGlobals.sVoiceCapable){
- Log.i(TAG,"Thisdeviceisdetectedasnon-voice-capabledevice.");
- handleNonVoiceCapable(intent);
- return;
- }
- //得到相应的Action
- Stringaction=intent.getAction();
- //从Intent中取出电话号码
- Stringnumber=PhoneNumberUtils.getNumberFromIntent(intent,this);
- //电话号码检查
- if(number!=null){
- if(!PhoneNumberUtils.isUriNumber(number)){
- //根据键盘map将字符转换为相应的数字
- number=PhoneNumberUtils.convertKeypadLettersToDigits(number);
- number=PhoneNumberUtils.stripSeparators(number);
- }
- }else{
- Log.w(TAG,"ThenumberobtainedfromIntentisnull.");
- }
- //如果callNow为true,表示当前为不允许拦截的如紧急拨号,这种情形下就无需这NEW_OUTGOING_CALL流程
- booleancallNow;
- if(getClass().getName().equals(intent.getComponent().getClassName())){
- //IfwewerelauncheddirectlyfromtheOutgoingCallBroadcaster,
- //notoneofitsmoreprivilegedaliases,thenmakesurethat
- //onlythenon-privilegedactionsareallowed.
- if(!Intent.ACTION_CALL.equals(intent.getAction())){
- Log.w(TAG,"Attempttodelivernon-CALLaction;forcingtoCALL");
- intent.setAction(Intent.ACTION_CALL);
- }
- }
- //检查当前号码是否为紧急号码,只有CALL_PRIVILEGED和CALL_EMERGENCY类型的intent才允许拨打紧急号码
- //(NotethattheACTION_CALLcheckbelowdependsontheresultof
- //isPotentialLocalEmergencyNumber()ratherthanjustplain
- //isLocalEmergencyNumber()
- //100%确保第三方应用不允许通过传递如"9111234"这种无效号码来拨打紧急号码
- finalbooleanisExactEmergencyNumber=(number!=null)&&PhoneNumberUtils.isLocalEmergencyNumber(number,this);
- finalbooleanisPotentialEmergencyNumber=(number!=null)&&PhoneNumberUtils.isPotentialLocalEmergencyNumber(number,this);
- if(VDBG){
- Log.v(TAG,"-Checkingrestrictionsfornumber'"+number+"':");
- Log.v(TAG,"isExactEmergencyNumber="+isExactEmergencyNumber);
- Log.v(TAG,"isPotentialEmergencyNumber="+isPotentialEmergencyNumber);
- }
- if(Intent.ACTION_CALL_PRIVILEGED.equals(action)){
- if(isPotentialEmergencyNumber){
- Log.i(TAG,"ACTION_CALL_PRIVILEGEDisusedwhilethenumberisapotential"
- +"emergencynumber.UseACTION_CALL_EMERGENCYasanactioninstead.");
- action=Intent.ACTION_CALL_EMERGENCY;
- }else{
- action=Intent.ACTION_CALL;
- }
- if(DBG)Log.v(TAG,"-updatingactionfromCALL_PRIVILEGEDto"+action);
- intent.setAction(action);
- }
- //如果普通拨打的号码为紧急号码,则启动电话拨号器
- if(Intent.ACTION_CALL.equals(action)){
- if(isPotentialEmergencyNumber){
- Log.w(TAG,"Cannotcallpotentialemergencynumber'"+number
- +"'withCALLIntent"+intent+".");
- Log.i(TAG,"Launchingdefaultdialerinstead...");
- //启动默认的电话拨号器DialtactsActivity
- IntentinvokeFrameworkDialer=newIntent();
- invokeFrameworkDialer.setClassName("com.android.contacts","com.android.contacts.DialtactsActivity");
- invokeFrameworkDialer.setAction(Intent.ACTION_DIAL);
- invokeFrameworkDialer.setData(intent.getData());
- if(DBG)Log.v(TAG,"onCreate():callingstartActivityforDialer:"+invokeFrameworkDialer);
- startActivity(invokeFrameworkDialer);
- finish();
- return;
- }
- callNow=false;
- //如果是紧急拨号,1.通过紧急拨号器拨号;2.ACTION_CALL_PRIVILEGED拨打紧急号码;将callNow设置为true
- }elseif(Intent.ACTION_CALL_EMERGENCY.equals(action)){
- if(!isPotentialEmergencyNumber){
- Log.w(TAG,"Cannotcallnon-potential-emergencynumber"+number
- +"withEMERGENCY_CALLIntent"+intent+"."
- +"FinishtheActivityimmediately.");
- finish();
- return;
- }
- callNow=true;
- }else{
- Log.e(TAG,"UnhandledIntent"+intent+".FinishtheActivityimmediately.");
- finish();
- return;
- }
- //唤醒屏幕
- PhoneGlobals.getInstance().wakeUpScreen();
- //Ifnumberisnull,we'reprobablytryingtocallanon-existentvoicemailnumber,
- //sendanemptyflashorsomethingelseisfishy.Whatevertheproblem,there'sno
- //number,sothere'snopointinallowingappstomodifythenumber.
- if(TextUtils.isEmpty(number)){
- if(intent.getBooleanExtra(EXTRA_SEND_EMPTY_FLASH,false)){
- Log.i(TAG,"onCreate:SEND_EMPTY_FLASH...");
- PhoneUtils.sendEmptyFlash(PhoneGlobals.getPhone());
- finish();
- return;
- }else{
- Log.i(TAG,"onCreate:nulloremptynumber,settingcallNow=true...");
- callNow=true;
- }
- }
- //如果是紧急拨号,直接启动拨号界面
- if(callNow){
- Log.i(TAG,"onCreate():callNowcase!CallingplaceCall():"+intent);
- //Initiatetheoutgoingcall,andsimultaneouslylaunchthe
- //InCallScreentodisplaythein-callUI:
- PhoneGlobals.getInstance().callController.placeCall(intent);
- }
- //Rememberthecalloriginsothatuserswillbeabletoseeanappropriatescreen
- //afterthephonecall.ThisshouldaffectbothphonecallsandSIPcalls.
- finalStringcallOrigin=intent.getStringExtra(PhoneGlobals.EXTRA_CALL_ORIGIN);
- if(callOrigin!=null){
- if(DBG)Log.v(TAG,"-Calloriginispassed("+callOrigin+")");
- PhoneGlobals.getInstance().setLatestActiveCallOrigin(callOrigin);
- }else{
- if(DBG)Log.v(TAG,"-Calloriginisnotpassed.Resetcurrentone.");
- PhoneGlobals.getInstance().resetLatestActiveCallOrigin();
- }
- //Fornow,SIPcallswillbeprocesseddirectlywithouta
- //NEW_OUTGOING_CALLbroadcast.
- //
- //TODO:Inthefuture,though,3rdpartyapps*should*beallowedto
- //interceptoutgoingcallstoSIPaddressesaswell.Todothis,weshould
- //(1)updatetheNEW_OUTGOING_CALLintentdocumentationtoexplainthis
- //case,and(2)passtheoutgoingSIPaddressby*not*overloadingthe
- //EXTRA_PHONE_NUMBERextra,butinsteadusinganewseparateextratohold
- //theoutgoingSIPaddress.(Besuretodocumentwhetherit'saURIorjust
- //aplainaddress,whetheritcouldbeatel:URI,etc.)
- Uriuri=intent.getData();
- Stringscheme=uri.getScheme();
- if(Constants.SCHEME_SIP.equals(scheme)||PhoneNumberUtils.isUriNumber(number)){
- Log.i(TAG,"TherequestednumberwasdetectedasSIPcall.");
- startSipCallOptionHandler(this,intent,uri,number);
- finish();
- return;
- }
- IntentbroadcastIntent=newIntent(Intent.ACTION_NEW_OUTGOING_CALL);
- if(number!=null){
- broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER,number);
- }
- PhoneUtils.checkAndCopyPhoneProviderExtras(intent,broadcastIntent);
- broadcastIntent.putExtra(EXTRA_ALREADY_CALLED,callNow);
- broadcastIntent.putExtra(EXTRA_ORIGINAL_URI,uri.toString());
- //Needtoraiseforegroundin-callUIassoonaspossiblewhileallowing3rdpartyapp
- //tointercepttheoutgoingcall.
- broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- if(DBG)Log.v(TAG,"-Broadcastingintent:"+broadcastIntent+".");
- //发送超时消息,当OutgoingCallReceiver在指定的时间内还未接受到广播时,显示超时
- mHandler.sendEmptyMessageDelayed(EVENT_OUTGOING_CALL_TIMEOUT,
- OUTGOING_CALL_TIMEOUT_THRESHOLD);
- //发送ACTION_NEW_OUTGOING_CALL广播
- sendOrderedBroadcastAsUser(broadcastIntent,UserHandle.OWNER,
- PERMISSION,newOutgoingCallReceiver(),
- null,//scheduler
- Activity.RESULT_OK,//initialCode
- number,//initialData:initialvaluefortheresultdata
- null);//initialExtras
- }
[java] view plain copy
- publicvoidonReceive(Contextcontext,Intentintent){
- mHandler.removeMessages(EVENT_OUTGOING_CALL_TIMEOUT);
- doReceive(context,intent);
- if(DBG)Log.v(TAG,"OutgoingCallReceiverisgoingtofinishtheActivityitself.");
- finish();
- }
- publicvoiddoReceive(Contextcontext,Intentintent){
- if(DBG)Log.v(TAG,"doReceive:"+intent);
- booleanalreadyCalled;
- Stringnumber;
- StringoriginalUri;
- alreadyCalled=intent.getBooleanExtra(OutgoingCallBroadcaster.EXTRA_ALREADY_CALLED,false);
- if(alreadyCalled){
- if(DBG)Log.v(TAG,"CALLalreadyplaced--returning.");
- return;
- }
- number=getResultData();
- if(VDBG)Log.v(TAG,"-gotnumberfromresultData:'"+number+"'");
- finalPhoneGlobalsapp=PhoneGlobals.getInstance();
- //如果电话支持Otasp
- if(TelephonyCapabilities.supportsOtasp(app.phone)){
- booleanactivateState=(app.cdmaOtaScreenState.otaScreenState
- ==OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION);
- booleandialogState=(app.cdmaOtaScreenState.otaScreenState
- ==OtaUtils.CdmaOtaScreenState.OtaScreenState
- .OTA_STATUS_SUCCESS_FAILURE_DLG);
- booleanisOtaCallActive=false;
- if((app.cdmaOtaScreenState.otaScreenState
- ==OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_PROGRESS)
- ||(app.cdmaOtaScreenState.otaScreenState
- ==OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_LISTENING)){
- isOtaCallActive=true;
- }
- if(activateState||dialogState){
- //TheOTASPsequenceisactive,buteither(1)thecall
- //hasn'tstartedyet,or(2)thecallhasendedandwe're
- //showingthesuccess/failurescreen.Ineitherofthese
- //casesit'sOKtomakeanewoutgoingcall,butweneed
- //totakedownanyOTASP-relatedUIfirst.
- if(dialogState)app.dismissOtaDialogs();
- app.clearOtaState();
- app.clearInCallScreenMode();
- }elseif(isOtaCallActive){
- //TheactualOTASPcallisactive.Don'tallownew
- //outgoingcallsatallfromthisstate.
- Log.w(TAG,"OTASPcallisactive:disallowinganewoutgoingcall.");
- return;
- }
- }
- if(number==null){
- if(DBG)Log.v(TAG,"CALLcancelled(nullnumber),returning...");
- return;
- }elseif(TelephonyCapabilities.supportsOtasp(app.phone)
- &&(app.phone.getState()!=PhoneConstants.State.IDLE)
- &&(app.phone.isOtaSpNumber(number))){
- if(DBG)Log.v(TAG,"Callisactive,a2ndOTAcallcancelled--returning.");
- return;
- }elseif(PhoneNumberUtils.isPotentialLocalEmergencyNumber(number,context)){
- Log.w(TAG,"Cannotmodifyoutgoingcalltoemergencynumber"+number+".");
- return;
- }
- originalUri=intent.getStringExtra(OutgoingCallBroadcaster.EXTRA_ORIGINAL_URI);
- if(originalUri==null){
- Log.e(TAG,"IntentismissingEXTRA_ORIGINAL_URI--returning.");
- return;
- }
- Uriuri=Uri.parse(originalUri);
- number=PhoneNumberUtils.convertKeypadLettersToDigits(number);
- number=PhoneNumberUtils.stripSeparators(number);
- if(DBG)Log.v(TAG,"doReceive:proceedingwithcall...");
- if(VDBG)Log.v(TAG,"-uri:"+uri);
- if(VDBG)Log.v(TAG,"-actualnumbertodial:'"+number+"'");
- startSipCallOptionHandler(context,intent,uri,number);
- }
OutgoingCallReceiver是OutgoingCallBroadcaster的一个内部类,作用是接收OutgoingCallBroadcaster发送的广播,判断是否已经启动InCallScreen。没有启动的话就进行一些初始化,如:对OTA进行初始化。接收到广播之后,从Intent里面取出电话号码及其URi。然后设置Intent为ACTION_CALL,并带上号码和uri。启动InCallScreen。关闭OutgoingCallReceiver。
OTA:Over-the-Air Technology 空中下载技术,是通过移动通信(GSM或CDMA)的空中接口对SIM卡数据及应用进行远程管理的技术。空中接口可以采用WAP、GPRS、CDMA1X及短消息技术。OTA技术的应用,使得移动通信不仅可以提供语音和数据服务,而且还能提供新业务下载。
[java] view plain copy- privatevoidstartSipCallOptionHandler(Contextcontext,Intentintent,
- Uriuri,Stringnumber){
- if(VDBG){
- Log.i(TAG,"startSipCallOptionHandler...");
- Log.i(TAG,"-intent:"+intent);
- Log.i(TAG,"-uri:"+uri);
- Log.i(TAG,"-number:"+number);
- }
- //创建原始电话拨号intent的副本
- IntentnewIntent=newIntent(Intent.ACTION_CALL,uri);
- newIntent.putExtra(EXTRA_ACTUAL_NUMBER_TO_DIAL,number);
- PhoneUtils.checkAndCopyPhoneProviderExtras(intent,newIntent);
- IntentselectPhoneIntent=newIntent(ACTION_SIP_SELECT_PHONE,uri);
- selectPhoneIntent.setClass(context,SipCallOptionHandler.class);
- selectPhoneIntent.putExtra(EXTRA_NEW_CALL_INTENT,newIntent);
- selectPhoneIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- if(DBG){
- Log.v(TAG,"startSipCallOptionHandler():"+
- "callingstartActivity:"+selectPhoneIntent);
- }
- //启动电话类型选择界面
- context.startActivity(selectPhoneIntent);
- }
src\com\android\phone\SipCallOptionHandler.java [java] view plain copy
- publicvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- Intentintent=getIntent();
- Stringaction=intent.getAction();
- if(!OutgoingCallBroadcaster.ACTION_SIP_SELECT_PHONE.equals(action)){
- Log.wtf(TAG,"onCreate:gotintentaction'"+action+"',expected"
- +OutgoingCallBroadcaster.ACTION_SIP_SELECT_PHONE);
- finish();
- return;
- }
- //取出原始电话拨号intent的副本
- mIntent=(Intent)intent.getParcelableExtra(OutgoingCallBroadcaster.EXTRA_NEW_CALL_INTENT);
- if(mIntent==null){
- finish();
- return;
- }
- //Allowthisactivitytobevisibleinfrontofthekeyguard.
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
- //-Ifit'sasip:URI,thisisdefinitelyaSIPcall,regardless
- //ofwhetherthedataisaSIPaddressoraregularphone
- //number.
- //-Ifthisisatel:URIbutthedatacontainsan"@"character
- //(seePhoneNumberUtils.isUriNumber())weconsiderthattobea
- //SIPnumbertoo.
- booleanvoipSupported=PhoneUtils.isVoipSupported();
- if(DBG)Log.v(TAG,"voipSupported:"+voipSupported);
- mSipProfileDb=newSipProfileDb(this);
- mSipSharedPreferences=newSipSharedPreferences(this);
- mCallOption=mSipSharedPreferences.getSipCallOption();
- if(DBG)Log.v(TAG,"Calloption:"+mCallOption);
- Uriuri=mIntent.getData();
- Stringscheme=uri.getScheme();
- mNumber=PhoneNumberUtils.getNumberFromIntent(mIntent,this);
- booleanisInCellNetwork=PhoneGlobals.getInstance().phoneMgr.isRadioOn();
- booleanisKnownCallScheme=Constants.SCHEME_TEL.equals(scheme)
- ||Constants.SCHEME_SIP.equals(scheme);
- booleanisRegularCall=Constants.SCHEME_TEL.equals(scheme)
- &&!PhoneNumberUtils.isUriNumber(mNumber);
- //Bypassthehandlerifthecallschemeisnotsiportel.
- if(!isKnownCallScheme){
- setResultAndFinish();
- return;
- }
- //CheckifVoIPfeatureissupported.
- if(!voipSupported){
- if(!isRegularCall){
- showDialog(DIALOG_NO_VOIP);
- }else{
- setResultAndFinish();
- }
- return;
- }
- if(!PhoneUtils.hasPhoneProviderExtras(mIntent)){
- if(!isNetworkConnected()){
- if(!isRegularCall){
- //显示无网络错误提示对话框
- showDialog(DIALOG_NO_INTERNET_ERROR);
- return;
- }
- }else{
- if(mCallOption.equals(Settings.System.SIP_ASK_ME_EACH_TIME)
- &&isRegularCall&&isInCellNetwork){
- //显示电话类型选择对话框
- showDialog(DIALOG_SELECT_PHONE_TYPE);
- return;
- }
- if(!mCallOption.equals(Settings.System.SIP_ADDRESS_ONLY)
- ||!isRegularCall){
- mUseSipPhone=true;
- }
- }
- }
- if(mUseSipPhone){
- //Ifthereisnosipprofileanditisaregularcall,thenwe
- //shouldusepstnnetworkinstead.
- if((mSipProfileDb.getProfilesCount()>0)||!isRegularCall){
- startGetPrimarySipPhoneThread();
- return;
- }else{
- mUseSipPhone=false;
- }
- }
- setResultAndFinish();
- privatevoidsetResultAndFinish(){
- runOnUiThread(newRunnable(){
- publicvoidrun(){
- if(mOutgoingSipProfile!=null){
- if(!isNetworkConnected()){
- showDialog(DIALOG_NO_INTERNET_ERROR);
- return;
- }
- if(DBG)Log.v(TAG,"primarySIPURIis"+
- mOutgoingSipProfile.getUriString());
- createSipPhoneIfNeeded(mOutgoingSipProfile);
- mIntent.putExtra(OutgoingCallBroadcaster.EXTRA_SIP_PHONE_URI,
- mOutgoingSipProfile.getUriString());
- if(mMakePrimary){
- mSipSharedPreferences.setPrimaryAccount(
- mOutgoingSipProfile.getUriString());
- }
- }
- //mUseSipPhone在SipCallOptionHandler的onCreate函数中被设置为false
- if(mUseSipPhone&&mOutgoingSipProfile==null){
- showDialog(DIALOG_START_SIP_SETTINGS);
- return;
- }else{
- //Woohoo--it'sfinallyOKtoinitiatetheoutgoingcall!
- PhoneGlobals.getInstance().callController.placeCall(mIntent);
- }
- finish();
- }
- });
- }
[java] view plain copy
- publicvoidplaceCall(Intentintent){
- log("placeCall()...intent="+intent);
- if(VDBG)log("extras="+intent.getExtras());
- finalInCallUiStateinCallUiState=mApp.inCallUiState;
- if(intent==null){
- Log.wtf(TAG,"placeCall:calledwithnullintent");
- thrownewIllegalArgumentException("placeCall:calledwithnullintent");
- }
- Stringaction=intent.getAction();
- Uriuri=intent.getData();
- if(uri==null){
- Log.wtf(TAG,"placeCall:intenthadnodata");
- thrownewIllegalArgumentException("placeCall:intenthadnodata");
- }
- Stringscheme=uri.getScheme();
- Stringnumber=PhoneNumberUtils.getNumberFromIntent(intent,mApp);
- if(VDBG){
- log("-action:"+action);
- log("-uri:"+uri);
- log("-scheme:"+scheme);
- log("-number:"+number);
- }
- if(!(Intent.ACTION_CALL.equals(action)
- ||Intent.ACTION_CALL_EMERGENCY.equals(action)
- ||Intent.ACTION_CALL_PRIVILEGED.equals(action))){
- Log.wtf(TAG,"placeCall:unexpectedintentaction"+action);
- thrownewIllegalArgumentException("Unexpectedaction:"+action);
- }
- //ChecktoseeifthisisanOTASPcall(the"activation"call
- //usedtoprovisionCDMAdevices),andifso,dosome
- //OTASP-specificsetup.
- Phonephone=mApp.mCM.getDefaultPhone();
- if(TelephonyCapabilities.supportsOtasp(phone)){
- checkForOtaspCall(intent);
- }
- mApp.setRestoreMuteOnInCallResume(false);
- //Ifaproviderisused,extracttheinfotobuildthe
- //overlayandroutethecall.Theoverlaywillbe
- //displayedwhentheInCallScreenbecomesvisible.
- if(PhoneUtils.hasPhoneProviderExtras(intent)){
- inCallUiState.setProviderInfo(intent);
- }else{
- inCallUiState.clearProviderInfo();
- }
- //拨号
- CallStatusCodestatus=placeCallInternal(intent);
- switch(status){
- caseSUCCESS:
- caseEXITED_ECM:
- if(DBG)log("==>placeCall():successfromplaceCallInternal():"+status);
- if(status==CallStatusCode.EXITED_ECM){
- //Callsucceeded,butwealsoneedtotellthe
- //InCallScreentoshowthe"ExitingECM"warning.
- inCallUiState.setPendingCallStatusCode(CallStatusCode.EXITED_ECM);
- }else{
- //Callsucceeded.There'sno"errorcondition"that
- //needstobedisplayedtotheuser,soclearoutthe
- //InCallUiState's"pendingcallstatuscode".
- inCallUiState.clearPendingCallStatusCode();
- }
- //Notifythephoneappthatacallisbeginningsoitcan
- //enabletheproximitysensor
- mApp.setBeginningCall(true);
- break;
- default:
- //Anyotherstatuscodeisafailure.
- log("==>placeCall():failurecodefromplaceCallInternal():"+status);
- //Handlethevariouserrorconditionsthatcanoccurwhen
- //initiatinganoutgoingcall,typicallybydirectingthe
- //InCallScreentodisplayadiagnosticmessage(viathe
- //"pendingcallstatuscode"flag.)
- handleOutgoingCallError(status);
- break;
- }
- mApp.displayCallScreen();
- }
1.拨号流程
[java] view plain copy- privateCallStatusCodeplaceCallInternal(Intentintent){
- finalInCallUiStateinCallUiState=mApp.inCallUiState;
- finalUriuri=intent.getData();
- finalStringscheme=(uri!=null)?uri.getScheme():null;
- Stringnumber;
- Phonephone=null;
- CallStatusCodeokToCallStatus=checkIfOkToInitiateOutgoingCall(
- mCM.getServiceState());
- try{
- number=PhoneUtils.getInitialNumber(intent);
- if(VDBG)log("-actualnumbertodial:'"+number+"'");
- StringsipPhoneUri=intent.getStringExtra(OutgoingCallBroadcaster.EXTRA_SIP_PHONE_URI);
- phone=PhoneUtils.pickPhoneBasedOnNumber(mCM,scheme,number,sipPhoneUri);
- if(VDBG)log("-gotPhoneinstance:"+phone+",class="+phone.getClass());
- okToCallStatus=checkIfOkToInitiateOutgoingCall(phone.getServiceState().getState());
- }catch(PhoneUtils.VoiceMailNumberMissingExceptionex){
- if(okToCallStatus!=CallStatusCode.SUCCESS){
- if(DBG)log("VoicemailnumbernotreachableincurrentSIMcardstate.");
- returnokToCallStatus;
- }
- if(DBG)log("VoiceMailNumberMissingExceptionfromgetInitialNumber()");
- returnCallStatusCode.VOICEMAIL_NUMBER_MISSING;
- }
- if(number==null){
- Log.w(TAG,"placeCall:couldn'tgetaphonenumberfromIntent"+intent);
- returnCallStatusCode.NO_PHONE_NUMBER_SUPPLIED;
- }
- booleanisEmergencyNumber=PhoneNumberUtils.isLocalEmergencyNumber(number,mApp);
- booleanisPotentialEmergencyNumber=PhoneNumberUtils.isPotentialLocalEmergencyNumber(number,mApp);
- booleanisEmergencyIntent=Intent.ACTION_CALL_EMERGENCY.equals(intent.getAction());
- if(isPotentialEmergencyNumber&&!isEmergencyIntent){
- Log.e(TAG,"Non-CALL_EMERGENCYIntent"+intent+"attemptedtocallpotentialemergencynumber"+number+".");
- returnCallStatusCode.CALL_FAILED;
- }elseif(!isPotentialEmergencyNumber&&isEmergencyIntent){
- Log.e(TAG,"ReceivedCALL_EMERGENCYIntent"+intent
- +"withnon-potential-emergencynumber"+number
- +"--failingcall.");
- returnCallStatusCode.CALL_FAILED;
- }
- //Ifwe'retryingtocallanemergencynumber,thenit'sOKto
- //proceedincertainstateswherewe'dotherwisebringup
- //anerrordialog:
- //-Ifwe'reinEMERGENCY_ONLYmode,then(obviously)you'reallowed
- //todialemergencynumbers.
- //-Ifwe'reOUT_OF_SERVICE,westillattempttomakeacall,
- //sincetheradiowillregistertoanyavailablenetwork.
- if(isEmergencyNumber
- &&((okToCallStatus==CallStatusCode.EMERGENCY_ONLY)
- ||(okToCallStatus==CallStatusCode.OUT_OF_SERVICE))){
- if(DBG)log("placeCall:Emergencynumberdetectedwithstatus="+okToCallStatus);
- okToCallStatus=CallStatusCode.SUCCESS;
- if(DBG)log("==>UPDATINGstatusto:"+okToCallStatus);
- }
- if(okToCallStatus!=CallStatusCode.SUCCESS){
- //Ifthisisanemergencycall,launchtheEmergencyCallHelperService
- //toturnontheradioandretrythecall.
- if(isEmergencyNumber&&(okToCallStatus==CallStatusCode.POWER_OFF)){
- Log.i(TAG,"placeCall:TryingtomakeemergencycallwhilePOWER_OFF!");
- //Ifneeded,lazilyinstantiateanEmergencyCallHelperinstance.
- synchronized(this){
- if(mEmergencyCallHelper==null){
- mEmergencyCallHelper=newEmergencyCallHelper(this);
- }
- }
- //...andkickoffthe"emergencycallfromairplanemode"sequence.
- mEmergencyCallHelper.startEmergencyCallFromAirplaneModeSequence(number);
- returnCallStatusCode.SUCCESS;
- }else{
- if(DBG)log("==>placeCallInternal():non-successstatus:"+okToCallStatus);
- returnokToCallStatus;
- }
- }
- //Ok,wecanproceedwiththisoutgoingcall.
- inCallUiState.needToShowCallLostDialog=false;
- inCallUiState.clearProgressIndication();
- UricontactUri=intent.getData();
- //真正的电话拨号过程
- intcallStatus=PhoneUtils.placeCall(mApp,
- phone,
- number,
- contactUri,
- (isEmergencyNumber||isEmergencyIntent),
- inCallUiState.providerGatewayUri);
- switch(callStatus){
- casePhoneUtils.CALL_STATUS_DIALED:
- if(VDBG)log("placeCall:PhoneUtils.placeCall()succeededforregularcall'"
- +number+"'.");
- if(VDBG)log("-inCallUiState.inCallScreenMode="
- +inCallUiState.inCallScreenMode);
- if(inCallUiState.inCallScreenMode==InCallScreenMode.OTA_NORMAL){
- if(VDBG)log("==>OTA_NORMALnote:switchingtoOTA_STATUS_LISTENING.");
- mApp.cdmaOtaScreenState.otaScreenState=
- CdmaOtaScreenState.OtaScreenState.OTA_STATUS_LISTENING;
- }
- booleanvoicemailUriSpecified=scheme!=null&&scheme.equals("voicemail");
- //Whenvoicemailisrequestedmostlikelytheuserwantstoopen
- //dialpadimmediately,soweshowitinthefirstplace.
- //Otherwisewewanttomakesuretheusercanseetheregular
- //in-callUIwhilethenewcallisdialing,andwhenit
- //firstgetsconnected.)
- inCallUiState.showDialpad=voicemailUriSpecified;
- //Forvoicemails,weaddcontexttexttolettheuserknowthey
- //aredialingtheirvoicemail.
- //TODO:Thisisonlysethereandbecomesproblematicwhenswappingcalls
- inCallUiState.dialpadContextText=voicemailUriSpecified?
- phone.getVoiceMailAlphaTag():"";
- //Also,incaseapreviouscallwasalreadyactive(i.e.if
- //wejustdid"Addcall"),clearoutthe"history"ofDTMF
- //digitsyoutyped,tomakesureitdoesn'tpersistfromthe
- //previouscalltothenewcall.
- //TODO:itwouldbemoreprecisetodothiswhentheactual
- //phonestatechangehappens(i.e.whenanewforeground
- //callappearsandthepreviouscallmovestothe
- //background),buttheInCallScreendoesn'tkeepenough
- //staterightnowtonoticethatspecifictransitionin
- //onPhoneStateChanged().
- inCallUiState.dialpadDigits=null;
- //CheckforanobscureECM-relatedscenario:Ifthephone
- //iscurrentlyinECM(Emergencycallbackmode)andwe
- //dialanon-emergencynumber,thatautomatically
- //*cancels*ECM.Sowarntheuseraboutit.
- //(SeeInCallScreen.showExitingECMDialog()formoreinfo.)
- booleanexitedEcm=false;
- if(PhoneUtils.isPhoneInEcm(phone)&&!isEmergencyNumber){
- Log.i(TAG,"AbouttoexitECMbecauseofanoutgoingnon-emergencycall");
- exitedEcm=true;//thiswillcauseustoreturnEXITED_ECMfromthismethod
- }
- if(phone.getPhoneType()==PhoneConstants.PHONE_TYPE_CDMA){
- //Startthetimerfor3WayCallerInfo
- if(mApp.cdmaPhoneCallState.getCurrentCallState()
- ==CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE){
- //UnmuteforthesecondMOcall
- PhoneUtils.setMute(false);
- //Thisisa"CDMA3-waycall",whichmeansthatyou'redialinga
- //2ndoutgoingcallwhileapreviouscallisalreadyinprogress.
- //
- //DuetothelimitationsofCDMAthiscalldoesn'tactuallygo
- //throughtheDIALING/ALERTINGstates,sowecan'ttellforsure
- //when(orif)it'sactuallyanswered.Butwewanttoshow
- //*some*indicationofwhat'sgoingonintheUI,sowe"fakeit"
- //bydisplayingthe"Dialing"statefor3seconds.
- //SetthemThreeWayCallOrigStateDialingstatetotrue
- mApp.cdmaPhoneCallState.setThreeWayCallOrigState(true);
- //Schedulethe"Dialing"indicationtobetakendownin3seconds:
- sendEmptyMessageDelayed(THREEWAY_CALLERINFO_DISPLAY_DONE,
- THREEWAY_CALLERINFO_DISPLAY_TIME);
- }
- }
- //Success!
- if(exitedEcm){
- returnCallStatusCode.EXITED_ECM;
- }else{
- returnCallStatusCode.SUCCESS;
- }
- casePhoneUtils.CALL_STATUS_DIALED_MMI:
- if(DBG)log("placeCall:specifiednumberwasanMMIcode:'"+number+"'.");
- //Thepassed-innumberwasanMMIcode,notaregularphonenumber!
- //Thisisn'treallyafailure;theDialermayhavedeliberately
- //firedanACTION_CALLintenttodialanMMIcode,likefora
- //USSDcall.
- //
- //PresumablyanMMI_INITIATEmessagewillcomeinshortly
- //(andwe'llbringupthe"MMIStarted"dialog),orelse
- //anMMI_COMPLETEwillcomein(whichwilltakeustoa
- //differentActivity;seePhoneUtils.displayMMIComplete()).
- returnCallStatusCode.DIALED_MMI;
- casePhoneUtils.CALL_STATUS_FAILED:
- Log.w(TAG,"placeCall:PhoneUtils.placeCall()FAILEDfornumber'"
- +number+"'.");
- //Wecouldn'tsuccessfullyplacethecall;therewassome
- //failureinthetelephonylayer.
- returnCallStatusCode.CALL_FAILED;
- default:
- Log.wtf(TAG,"placeCall:unknowncallStatus"+callStatus
- +"fromPhoneUtils.placeCall()fornumber'"+number+"'.");
- returnCallStatusCode.SUCCESS;//Trytocontinueanyway...
- }
- }
src\com\android\phone\PhoneUtils.java
[java] view plain copy- publicstaticintplaceCall(Contextcontext,Phonephone,
- Stringnumber,UricontactRef,booleanisEmergencyCall,
- UrigatewayUri){
- finalPhoneGlobalsapp=PhoneGlobals.getInstance();
- booleanuseGateway=false;
- if(null!=gatewayUri&&
- !isEmergencyCall&&
- PhoneUtils.isRoutableViaGateway(number)){//FilteroutMMI,OTAandothercodes.
- useGateway=true;
- }
- intstatus=CALL_STATUS_DIALED;
- Connectionconnection;
- StringnumberToDial;
- if(useGateway){
- if(null==gatewayUri||!Constants.SCHEME_TEL.equals(gatewayUri.getScheme())){
- Log.e(LOG_TAG,"UnsupportedURL:"+gatewayUri);
- returnCALL_STATUS_FAILED;
- }
- //WecanusegetSchemeSpecificPartbecausewedon'tallow#
- //inthegatewaynumbers(treatedafragmentdelim.)However
- //ifweallowmorecomplexgatewaynumberssequence(with
- //passwordsorwhatnot)thatuse#,thismaybreak.
- //TODO:NeedtosupportMMIcodes.
- numberToDial=gatewayUri.getSchemeSpecificPart();
- }else{
- numberToDial=number;
- }
- //RememberifthephonestatewasinIDLEstatebeforethiscall.
- //AftercallingCallManager#dial(),getState()willreturndifferentstate.
- finalbooleaninitiallyIdle=app.mCM.getState()==PhoneConstants.State.IDLE;
- try{
- connection=app.mCM.dial(phone,numberToDial);
- }catch(CallStateExceptionex){
- //CallStateExceptionmeansanewoutgoingcallisnotcurrently
- //possible:eithernomorecallslotsexist,orthere'sanother
- //callalreadyintheprocessofdialingorringing.
- Log.w(LOG_TAG,"Exceptionfromapp.mCM.dial()",ex);
- returnCALL_STATUS_FAILED;
- //Notethatit'spossibleforCallManager.dial()toreturn
- //null*without*throwinganexception;thatindicatesthat
- //wedialedanMMI(seebelow).
- }
- intphoneType=phone.getPhoneType();
- //OnGSMphones,nullisreturnedforMMIcodes
- if(null==connection){
- if(phoneType==PhoneConstants.PHONE_TYPE_GSM&&gatewayUri==null){
- if(DBG)log("dialedMMIcode:"+number);
- status=CALL_STATUS_DIALED_MMI;
- }else{
- status=CALL_STATUS_FAILED;
- }
- }else{
- if(phoneType==PhoneConstants.PHONE_TYPE_CDMA){
- updateCdmaCallStateOnNewOutgoingCall(app);
- }
- //Cleanupthenumbertobedisplayed.
- if(phoneType==PhoneConstants.PHONE_TYPE_CDMA){
- number=CdmaConnection.formatDialString(number);
- }
- number=PhoneNumberUtils.extractNetworkPortion(number);
- number=PhoneNumberUtils.convertKeypadLettersToDigits(number);
- number=PhoneNumberUtils.formatNumber(number);
- if(gatewayUri==null){
- //phone.dial()succeeded:we'renowinanormalphonecall.
- //attachtheURItotheCallerInfoObjectifitisthere,
- //otherwisejustattachtheUriReference.
- //iftheuridoesnothavea"content"scheme,thenwetreat
- //itasifitdoesNOThaveauniquereference.
- Stringcontent=context.getContentResolver().SCHEME_CONTENT;
- if((contactRef!=null)&&(contactRef.getScheme().equals(content))){
- ObjectuserDataObject=connection.getUserData();
- if(userDataObject==null){
- connection.setUserData(contactRef);
- }else{
- //TODO:Thisbranchisdeadcode,wehave
- //justcreatedtheconnectionwhichhas
- //nouserdata(null)bydefault.
- if(userDataObjectinstanceofCallerInfo){
- ((CallerInfo)userDataObject).contactRefUri=contactRef;
- }else{
- ((CallerInfoToken)userDataObject).currentInfo.contactRefUri=
- contactRef;
- }
- }
- }
- }else{
- //Getthecallerinfosynchronouslybecauseweneedthefinal
- //CallerInfoobjecttoupdatethedialednumberwiththeone
- //requestedbytheuser(andnottheprovider'sgatewaynumber).
- CallerInfoinfo=null;
- Stringcontent=phone.getContext().getContentResolver().SCHEME_CONTENT;
- if((contactRef!=null)&&(contactRef.getScheme().equals(content))){
- info=CallerInfo.getCallerInfo(context,contactRef);
- }
- //Fallback,lookupcontactusingthephonenumberifthe
- //contact'sURIschemewasnotcontent://orifiswasbut
- //thelookupfailed.
- if(null==info){
- info=CallerInfo.getCallerInfo(context,number);
- }
- info.phoneNumber=number;
- connection.setUserData(info);
- }
- setAudioMode();
- if(DBG)log("abouttoactivatespeaker");
- //Checkisphoneinanydock,andturnonspeakeraccordingly
- finalbooleanspeakerActivated=activateSpeakerIfDocked(phone);
- //SeealsosimilarlogicinanswerCall().
- if(initiallyIdle&&!speakerActivated&&isSpeakerOn(app)
- &&!app.isBluetoothHeadsetAudioOn()){
- //Thisisnotanerrorbutmightcauseusers'confusion.Addlogjustincase.
- Log.i(LOG_TAG,"Forcingspeakeroffwheninitiatinganewoutgoingcall...");
- PhoneUtils.turnOnSpeaker(app,false,true);
- }
- }
- returnstatus;
- }
- publicConnectiondial(Phonephone,StringdialString)throwsCallStateException{
- PhonebasePhone=getPhoneBase(phone);
- Connectionresult;
- if(VDBG){
- Log.d(LOG_TAG,"dial("+basePhone+","+dialString+")");
- Log.d(LOG_TAG,this.toString());
- }
- if(!canDial(phone)){
- thrownewCallStateException("cannotdialincurrentstate");
- }
- if(hasActiveFgCall()){
- PhoneactivePhone=getActiveFgCall().getPhone();
- booleanhasBgCall=!(activePhone.getBackgroundCall().isIdle());
- if(DBG){
- Log.d(LOG_TAG,"hasBgCall:"+hasBgCall+"sameChannel:"+(activePhone==basePhone));
- }
- if(activePhone!=basePhone){
- if(hasBgCall){
- Log.d(LOG_TAG,"Hangup");
- getActiveFgCall().hangup();
- }else{
- Log.d(LOG_TAG,"Switch");
- activePhone.switchHoldingAndActive();
- }
- }
- }
- result=basePhone.dial(dialString);
- if(VDBG){
- Log.d(LOG_TAG,"Enddial("+basePhone+","+dialString+")");
- Log.d(LOG_TAG,this.toString());
- }
- returnresult;
- }
./telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
[java] view plain copy- publicConnectiondial(StringdialString)throwsCallStateException{
- returndial(dialString,null);
- }
[java] view plain copy
- publicConnectiondial(StringdialString,UUSInfouusInfo)throwsCallStateException{
- //NeedtomakesuredialStringgetsparsedproperly
- StringnewDialString=PhoneNumberUtils.stripSeparators(dialString);
- //handlein-callMMIfirstifapplicable
- if(handleInCallMmiCommands(newDialString)){
- returnnull;
- }
- //OnlylookattheNetworkportionformmi
- StringnetworkPortion=PhoneNumberUtils.extractNetworkPortionAlt(newDialString);
- GsmMmiCodemmi=GsmMmiCode.newFromDialString(networkPortion,this);
- if(mmi==null){
- returnmCT.dial(newDialString,uusInfo);
- }elseif(mmi.isTemporaryModeCLIR()){
- returnmCT.dial(mmi.dialingNumber,mmi.getCLIRMode(),uusInfo);
- }else{
- mPendingMMIs.add(mmi);
- mMmiRegistrants.notifyRegistrants(newAsyncResult(null,mmi,null));
- mmi.processCode();
- //FIXMEshouldthisreturnnullorsomethingelse?
- returnnull;
- }
- }
./telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java
[java] view plain copy- synchronizedConnectiondial(StringdialString,intclirMode,UUSInfouusInfo)throwsCallStateException{
- //notethatthistriggerscallstatechangednotif
- clearDisconnected();
- if(!canDial()){
- thrownewCallStateException("cannotdialincurrentstate");
- }
- //Thenewcallmustbeassignedtotheforegroundcall.
- //Thatcallmustbeidle,soplaceanythingthat's
- //thereonhold
- if(foregroundCall.getState()==GsmCall.State.ACTIVE){
- //thiswillprobablybedonebytheradioanyway
- //butthedialmightfailbeforethishappens
- //andweneedtomakesuretheforegroundcallisclear
- //forthenewlydialedconnection
- switchWaitingOrHoldingAndActive();
- //Fakelocalstatesothat
- //a)foregroundCallisemptyforthenewlydialedconnection
- //b)hasNonHangupStateChangedremainsfalseinthe
- //nextpoll,sothatwedon'tclearafaileddialingcall
- fakeHoldForegroundBeforeDial();
- }
- if(foregroundCall.getState()!=GsmCall.State.IDLE){
- //weshouldhavefailedin!canDial()abovebeforewegethere
- thrownewCallStateException("cannotdialincurrentstate");
- }
- //pendingMO=newGsmConnection(phone.getContext(),checkForTestEmergencyNumber(dialString),
- //this,foregroundCall);
- booleanisStkCall=getStkCall();
- log("GsmCallTrackerdial:isStkCall="+isStkCall);
- pendingMO=newGsmConnection(phone.getContext(),dialString,this,foregroundCall,isStkCall,false);
- hangupPendingMO=false;
- if(pendingMO.address==null||pendingMO.address.length()==0
- ||pendingMO.address.indexOf(PhoneNumberUtils.WILD)>=0
- ){
- //Phonenumberisinvalid
- pendingMO.cause=Connection.DisconnectCause.INVALID_NUMBER;
- //handlePollCalls()willnoticethiscallnotpresent
- //andwillmarkitasdropped.
- pollCallsWhenSafe();
- }else{
- //Alwaysunmutewheninitiatinganewcall
- setMute(false);
- //cm.dial(pendingMO.address,clirMode,uusInfo,obtainCompleteMessage());
- //Addforbug121825Start
- StringtmpAddr=pendingMO.address;
- if(PhoneNumberUtils.isCustomEmergencyNumber(pendingMO.address)){
- Log.d(LOG_TAG,"PendingMOisCustomEmergencycall");
- tmpAddr=tmpAddr+"/1";
- }
- //cm.dial(pendingMO.address,clirMode,uusInfo,isStkCall,obtainCompleteMessage());
- cm.dial(tmpAddr,clirMode,uusInfo,isStkCall,obtainCompleteMessage());
- //Addforbug121825End
- }
- updatePhoneState();
- phone.notifyPreciseCallStateChanged();
- returnpendingMO;
- }
./telephony/java/com/android/internal/telephony/RIL.java
[java] view plain copy- publicvoiddial(Stringaddress,intclirMode,UUSInfouusInfo,booleanisStkCall,Messageresult){
- RILRequestrr;
- if(address.indexOf('/')==-1){
- rr=RILRequest.obtain(RIL_REQUEST_DIAL,result);
- }else{
- rr=RILRequest.obtain(RIL_REQUEST_DIAL_EMERGENCY_CALL,result);
- }
- rr.mp.writeString(address);
- rr.mp.writeInt(clirMode);
- rr.mp.writeInt(0);//UUSinformationisabsent
- if(uusInfo==null){
- rr.mp.writeInt(0);//UUSinformationisabsent
- }else{
- rr.mp.writeInt(1);//UUSinformationispresent
- rr.mp.writeInt(uusInfo.getType());
- rr.mp.writeInt(uusInfo.getDcs());
- rr.mp.writeByteArray(uusInfo.getUserData());
- }
- rr.mp.writeInt(isStkCall?1:0);
- if(RILJ_LOGD)riljLog(rr.serialString()+">"+requestToString(rr.mRequest));
- send(rr);
- }
Java部分的request请求号需要与C/C++部分的请求号保持一致。当需要执行某种AT命令request请求时,则需创建一个新的RILRequest,使用RILRequest的obtain函数,该obtain静态函数用于从其内部维护的一个 RIL request池sPool中取下一个request,得到一个RILRequest对象,它里面的请求号和用于回送结果及处理者handler的消息来自传递的实参。当一个RILRequest对象不再使用时,调用release() 函数将其释放回池中。将RILRequest请求放置到消息队列上,然后sender线程将其写入socket,rild侧通过dispatch线程将请求分发出去。在RIL类中,还维护了一个RILRequest请求列表,RILRequest类中的serial作为其id标识。当sender发送一个RIL请求后,则将其添加到该列表中,若发送时出现异常则需再清除;当请求完成并得到回送的response消息后,使用findAndRemoveRequestFromList函数将其移除。RIL请求执行AT是一个异步的过程:调用者调用RIL类的API函数只是往线程的消息队列上添加了一消息就返回;然后线程在执行无限循环时将其写到socket中,并将RILRequest对象添加到一个列表中;当RILReciever线程收到数据并解析,然后查询系列号后得到这是某个先前的RIL请求后,将AT执行的返回结果放到AsynResult中并赋值给Message中的obj成员后,由Message.sendToTarget送回到调用者并由其处理。
[java] view plain copy
- protectedvoidsend(RILRequestrr){
- Messagemsg;
- if(mSocket==null){
- rr.onError(RADIO_NOT_AVAILABLE,null);
- rr.release();
- return;
- }
- msg=mSender.obtainMessage(EVENT_SEND,rr);
- acquireWakeLock();
- msg.sendToTarget();
- }
- classRILSenderextendsHandlerimplementsRunnable{
- publicRILSender(Looperlooper){
- super(looper);
- }
- byte[]dataLength=newbyte[4];
- publicvoid
- run(){
- //setupifneeded
- }
- @Overridepublicvoid
- handleMessage(Messagemsg){
- RILRequestrr=(RILRequest)(msg.obj);
- RILRequestreq=null;
- switch(msg.what){
- caseEVENT_SEND:
- /**
- *mRequestMessagePending++alreadyhappenedforevery
- *EVENT_SEND,thuswemustmakesure
- *mRequestMessagePending--happensonceandonlyonce
- */
- booleanalreadySubtracted=false;
- try{
- LocalSockets;
- s=mSocket;
- if(s==null){
- rr.onError(RADIO_NOT_AVAILABLE,null);
- rr.release();
- if(mRequestMessagesPending>0)
- mRequestMessagesPending--;
- alreadySubtracted=true;
- return;
- }
- synchronized(mRequestsList){
- mRequestsList.add(rr);
- mRequestMessagesWaiting++;
- }
- if(mRequestMessagesPending>0)
- mRequestMessagesPending--;
- alreadySubtracted=true;
- byte[]data;
- data=rr.mp.marshall();
- rr.mp.recycle();
- rr.mp=null;
- if(data.length>RIL_MAX_COMMAND_BYTES){
- thrownewRuntimeException("Parcellargerthanmaxbytesallowed!"+data.length);
- }
- //parcellengthinbigendian
- dataLength[0]=dataLength[1]=0;
- dataLength[2]=(byte)((data.length>>8)&0xff);
- dataLength[3]=(byte)((data.length)&0xff);
- //Log.v(LOG_TAG,"writingpacket:"+data.length+"bytes");
- s.getOutputStream().write(dataLength);
- s.getOutputStream().write(data);
- }catch(IOExceptionex){
- Log.e(LOG_TAG,"IOException",ex);
- req=findAndRemoveRequestFromList(rr.mSerial);
- //makesurethisrequesthasnotalreadybeenhandled,
- //eg,ifRILReceiverclearedthelist.
- if(req!=null||!alreadySubtracted){
- rr.onError(RADIO_NOT_AVAILABLE,null);
- rr.release();
- }
- }catch(RuntimeExceptionexc){
- Log.e(LOG_TAG,"Uncaughtexception",exc);
- req=findAndRemoveRequestFromList(rr.mSerial);
- //makesurethisrequesthasnotalreadybeenhandled,
- //eg,ifRILReceiverclearedthelist.
- if(req!=null||!alreadySubtracted){
- rr.onError(GENERIC_FAILURE,null);
- rr.release();
- }
- }finally{
- //Note:Weare"Done"onlyiftherearenooutstanding
- //requestsorreplies.Thusthiscodepathwillonlyrelease
- //thewakelockonerrors.
- releaseWakeLockIfDone();
- }
- if(!alreadySubtracted&&mRequestMessagesPending>0){
- mRequestMessagesPending--;
- }
- break;
- }
- }
- }
2.拨号界面显示
在CallController的placeCall函数中,首先将拨号请求发送到rild服务进程,然后启动呼叫界面InCallScreen,Android电话Phone UI分析对InCallScreen的UI布局进行了详细的分析,只有了解InCallScreen的UI布局,才能更好地理解InCallScreen的启动过程。InCallScreen主要是显示通话界面, 并且还负责菜单项各种按键事件和触摸时间的处理。同时本类还复写的finish()方法,所以一般不会被finish掉,调用该方法时它又把自己放回栈中。
src\com\android\phone\PhoneGlobals.java
[java] view plain copy- voiddisplayCallScreen(){
- if(VDBG)Log.d(LOG_TAG,"displayCallScreen()...");
- //Onnon-voice-capabledevicesweshouldn'teverbetryingto
- //bringuptheInCallScreeninthefirstplace.
- if(!sVoiceCapable){
- Log.w(LOG_TAG,"displayCallScreen()notallowed:non-voice-capabledevice",newThrowable("stackdump"));
- return;
- }
- //启动电话呼叫界面InCallScreen
- try{
- startActivity(createInCallIntent());
- }catch(ActivityNotFoundExceptione){
- Log.w(LOG_TAG,"displayCallScreen:transitiontoInCallScreenfailed:"+e);
- }
- Profiler.callScreenRequested();
- }
- staticIntentcreateInCallIntent(){
- Intentintent=newIntent(Intent.ACTION_MAIN,null);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- |Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- |Intent.FLAG_ACTIVITY_NO_USER_ACTION);
- intent.setClassName("com.android.phone",getCallScreenClassName());
- returnintent;
- }
- privatestaticStringgetCallScreenClassName(){
- returnInCallScreen.class.getName();
- }
第一次启动InCallScreen,首先调用其onCreate函数
src\com\android\phone\InCallScreen.java[java] view plain copy
- protectedvoidonCreate(Bundleicicle){
- Log.i(LOG_TAG,"onCreate()...this="+this);
- //获得通话界面被创建的时间
- Profiler.callScreenOnCreate();
- super.onCreate(icicle);
- //Makesurethisisavoice-capabledevice.
- if(!PhoneGlobals.sVoiceCapable){
- Log.wtf(LOG_TAG,"onCreate()reachedonnon-voice-capabledevice");
- finish();
- return;
- }
- mApp=PhoneGlobals.getInstance();
- mApp.setInCallScreenInstance(this);
- //setthisflagsothisactivitywillstayinfrontofthekeyguard
- intflags=WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
- |WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
- if(mApp.getPhoneState()==PhoneConstants.State.OFFHOOK){
- flags|=WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
- }
- WindowManager.LayoutParamslp=getWindow().getAttributes();
- lp.flags|=flags;
- if(!mApp.proximitySensorModeEnabled()){
- //Ifwedon'thaveaproximitysensor,thenthein-callscreenexplicitly
- //controlsuseractivity.Thisistopreventspurioustouchesfromwaking
- //thedisplay.
- lp.inputFeatures|=WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;
- }
- //设置窗体属性
- getWindow().setAttributes(lp);
- setPhone(mApp.phone);//SetsmPhone
- mCM=mApp.mCM;
- log("-onCreate:phonestate="+mCM.getState());
- mBluetoothAdapter=BluetoothAdapter.getDefaultAdapter();
- if(mBluetoothAdapter!=null){
- mBluetoothAdapter.getProfileProxy(getApplicationContext(),mBluetoothProfileServiceListener,
- BluetoothProfile.HEADSET);
- }
- //设置窗体显示风格
- requestWindowFeature(Window.FEATURE_NO_TITLE);
- //加载布局文件
- setContentView(R.layout.incall_screen);
- finalViewStubtouchUiStub=(ViewStub)findViewById(
- mPhone.getPhoneType()==PhoneConstants.PHONE_TYPE_CDMA
- ?R.id.inCallTouchUiCdmaStub:R.id.inCallTouchUiStub);
- if(touchUiStub!=null)touchUiStub.inflate();
- //加载各种view组建
- initInCallScreen();
- //对通话的各种状态进行广播。
- registerForPhoneStates();
- //判断是否使用了OTA技术,通过该判断设置通话界面的样式。
- if(icicle==null){
- if(DBG)log("onCreate():thisisourveryfirstlaunch,checkingintent...");
- internalResolveIntent(getIntent());
- }
- //记录通话界面创建完成后的时间
- Profiler.callScreenCreated();
- if(DBG)log("onCreate():exit");
- }
- privatevoidinitInCallScreen(){
- if(VDBG)log("initInCallScreen()...");
- //HavetheWindowManagerfilterouttoucheventsthatare"toofat".
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES);
- //InitializetheCallCard.
- mCallCard=(CallCard)findViewById(R.id.callCard);
- if(VDBG)log("-mCallCard="+mCallCard);
- mCallCard.setInCallScreenInstance(this);
- //InitializetheonscreenUIelements.
- initInCallTouchUi();
- //Helperclasstokeeptrackofenabledness/stateofUIcontrols
- mInCallControlState=newInCallControlState(this,mCM);
- //Helperclasstorunthe"Manageconference"UI
- mManageConferenceUtils=newManageConferenceUtils(this,mCM);
- //TheDTMFDialpad.
- ViewStubstub=(ViewStub)findViewById(R.id.dtmf_twelve_key_dialer_stub);
- mDialer=newDTMFTwelveKeyDialer(this,stub);
- mPowerManager=(PowerManager)getSystemService(Context.POWER_SERVICE);
- }
[java] view plain copy
- privatevoidinternalResolveIntent(Intentintent){
- if(intent==null||intent.getAction()==null){
- return;
- }
- Stringaction=intent.getAction();
- if(DBG)log("internalResolveIntent:action="+action);
- if(action.equals(intent.ACTION_MAIN)){
- if(intent.hasExtra(SHOW_DIALPAD_EXTRA)){
- booleanshowDialpad=intent.getBooleanExtra(SHOW_DIALPAD_EXTRA,false);
- if(VDBG)log("-internalResolveIntent:SHOW_DIALPAD_EXTRA:"+showDialpad);
- mApp.inCallUiState.showDialpad=showDialpad;
- finalbooleanhasActiveCall=mCM.hasActiveFgCall();
- finalbooleanhasHoldingCall=mCM.hasActiveBgCall();
- if(showDialpad&&!hasActiveCall&&hasHoldingCall){
- PhoneUtils.switchHoldingAndActive(mCM.getFirstActiveBgCall());
- }
- }
- return;
- }
- if(action.equals(OtaUtils.ACTION_DISPLAY_ACTIVATION_SCREEN)){
- if(!TelephonyCapabilities.supportsOtasp(mPhone)){
- thrownewIllegalStateException(
- "ReceivedACTION_DISPLAY_ACTIVATION_SCREENintentonnon-OTASP-capabledevice:"
- +intent);
- }
- setInCallScreenMode(InCallScreenMode.OTA_NORMAL);
- if((mApp.cdmaOtaProvisionData!=null)
- &&(!mApp.cdmaOtaProvisionData.isOtaCallIntentProcessed)){
- mApp.cdmaOtaProvisionData.isOtaCallIntentProcessed=true;
- mApp.cdmaOtaScreenState.otaScreenState=
- CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION;
- }
- return;
- }
- if(action.equals(OtaUtils.ACTION_PERFORM_CDMA_PROVISIONING)){
- thrownewIllegalStateException(
- "UnexpectedACTION_PERFORM_CDMA_PROVISIONINGreceivedbyInCallScreen:"+intent);
- }elseif(action.equals(Intent.ACTION_CALL)||action.equals(Intent.ACTION_CALL_EMERGENCY)){
- //ACTION_CALL*intentsgototheOutgoingCallBroadcaster,whichnow
- //translatesthemintoCallController.placeCall()callsratherthan
- //launchingtheInCallScreendirectly.
- thrownewIllegalStateException("UnexpectedCALLactionreceivedbyInCallScreen:"+intent);
- }elseif(action.equals(ACTION_UNDEFINED)){
- //Thisactionisonlyusedforinternalbookkeeping;weshould
- //neveractuallygetlaunchedwithit.
- Log.wtf(LOG_TAG,"internalResolveIntent:gotlaunchedwithACTION_UNDEFINED");
- return;
- }else{
- Log.wtf(LOG_TAG,"internalResolveIntent:unexpectedintentaction:"+action);
- //Butcontinuethebestwecan(basicallytreatingthiscase
- //likeACTION_MAIN...)
- return;
- }
- }
更多相关文章
- android studio编译android M时无法使用org.apache.http.**的解
- Android如何实现模态对话框(Modal Dialog)
- 向Android模拟器发短信打电话
- android中View.measure方法详解
- Android(安卓)build/envsetup.sh 脚本分析(lunch函数)
- 刚学会百度地图最新版的sdk,总结一个简单的demo
- Android复制手机号码到剪切板并调起打电话功能
- android wifi子系统
- Android(安卓)实现自动接听和挂断电话功能