前面分析了电话拨号界面及电话呼叫界面,由于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
  1. publicViewonCreateView(LayoutInflaterinflater,ViewGroupcontainer,BundlesavedState){
  2. ...
  3. //Checkwhetherweshouldshowtheonscreen"Dial"button.
  4. mDialButton=mAdditionalButtonsRow.findViewById(R.id.dialButton);
  5. if(r.getBoolean(R.bool.config_show_onscreen_dial_button)){
  6. mDialButton.setOnClickListener(this);
  7. }else{
  8. mDialButton.setVisibility(View.GONE);//It'sVISIBLEbydefault
  9. mDialButton=null;
  10. }
  11. ...

拨号按钮的单击事件响应:

[java] view plain copy
  1. publicvoidonClick(Viewview){
  2. switch(view.getId()){
  3. caseR.id.dialButton:{
  4. mHaptic.vibrate();
  5. dialButtonPressed();
  6. return;
  7. }
  8. ...
  9. }
  10. }

调用dialButtonPressed()函数发起电话呼叫

[java] view plain copy
  1. publicvoiddialButtonPressed(){
  2. if(mDigits==null){
  3. Log.e(TAG,"dialButtonPressed,mDigits==null");
  4. return;
  5. }
  6. //未输入号码处理
  7. if(isDigitsEmpty()){
  8. handleDialButtonClickWithEmptyDigits();
  9. }else{
  10. finalStringnumber=mDigits.getText().toString();
  11. //"persist.radio.otaspdial"isatemporaryhackneededforonecarrier'sautomated
  12. //testequipment.
  13. if(number!=null&&!TextUtils.isEmpty(mProhibitedPhoneNumberRegexp)
  14. &&number.matches(mProhibitedPhoneNumberRegexp)
  15. &&(SystemProperties.getInt("persist.radio.otaspdial",0)!=1)){
  16. Log.i(TAG,"Thephonenumberisprohibitedexplicitlybyarule.");
  17. if(getActivity()!=null){
  18. DialogFragmentdialogFragment=ErrorDialogFragment.newInstance(
  19. R.string.dialog_phone_call_prohibited_title);
  20. dialogFragment.show(getFragmentManager(),"phone_prohibited_dialog");
  21. }
  22. //号码输入不正确.
  23. mDigits.getText().clear();
  24. }elseif(number!=null&&(number.startsWith(",")||number.startsWith(";"))){
  25. mDigits.getText().clear();
  26. if(getActivity()!=null){
  27. Toast.makeText(getActivity(),getText(R.string.invalid_number),
  28. Toast.LENGTH_SHORT).show();
  29. }
  30. }else{
  31. //启动电话呼叫界面
  32. finalIntentintent=ContactsUtils.getCallIntent(number,(getActivity()instanceofDialtactsActivity?
  33. ((DialtactsActivity)getActivity()).getCallOrigin():null));
  34. startActivity(intent);
  35. mClearDigitsOnStop=true;
  36. mDigits.getText().clear();
  37. if(mFlagIntentNumber){
  38. getActivity().finish();
  39. }
  40. }
  41. }
  42. }

函数首先对输入的号码进行检查,如果没有输入号码,直接按下拨号按钮,则调用handleDialButtonClickWithEmptyDigits函数来处理

[java] view plain copy
  1. privatevoidhandleDialButtonClickWithEmptyDigits(){
  2. if(phoneIsCdma()&&phoneIsOffhook()){
  3. //ThisisreallyCDMAspecific.OnGSMisitpossible
  4. //tobeoffhookandwantedtoadda3rdpartyusing
  5. //theredialfeature.
  6. startActivity(newFlashIntent());
  7. }else{
  8. if(mDigits!=null&&!TextUtils.isEmpty(mLastNumberDialed)){
  9. //Recallthelastnumberdialed.
  10. mDigits.setText(mLastNumberDialed);
  11. //...andmovethecursortotheendofthedigitsstring,
  12. //soyou'llbeabletodeletedigitsusingtheDelete
  13. //button(justasifyouhadtypedthenumbermanually.)
  14. //
  15. //NoteweusemDigits.getText().length()here,not
  16. //mLastNumberDialed.length(),sincetheEditTextwidgetnow
  17. //containsa*formatted*versionofmLastNumberDialed(dueto
  18. //mTextWatcher)anditslengthmayhavechanged.
  19. mDigits.setSelection(mDigits.getText().length());
  20. }else{
  21. //There'sno"lastnumberdialed"orthe
  22. //backgroundqueryisstillrunning.There's
  23. //nothingusefulfortheDialbuttontodoin
  24. //thiscase.Note:withasoftdialbutton,this
  25. //canneverhappenssincethedialbuttonis
  26. //disabledundertheseconditons.
  27. playTone(ToneGenerator.TONE_PROP_NACK);
  28. }
  29. }
  30. }

如果号码输入正确合法,则使用ContactsUtils工具类来创建一个Intent。

DialtactsActivity.java

[java] view plain copy
  1. publicStringgetCallOrigin(){
  2. return!isDialIntent(getIntent())?CALL_ORIGIN_DIALTACTS:null;
  3. }
  4. /**Returnstrueifthegivenintentcontainsaphonenumbertopopulatethedialerwith*/
  5. privatebooleanisDialIntent(Intentintent){
  6. finalStringaction=intent.getAction();
  7. if(Intent.ACTION_DIAL.equals(action)||ACTION_TOUCH_DIALER.equals(action)){
  8. returntrue;
  9. }
  10. if(Intent.ACTION_VIEW.equals(action)){
  11. finalUridata=intent.getData();
  12. if(data!=null&&Constants.SCHEME_TEL.equals(data.getScheme())){
  13. returntrue;
  14. }
  15. }
  16. returnfalse;
  17. }

从Launcher点击拨号图标进入的,因此isDialIntent返回true,getCallOrigin返回null

ContactsUtils.java

[java] view plain copy
  1. publicstaticIntentgetCallIntent(Stringnumber,StringcallOrigin){
  2. returngetCallIntent(getCallUri(number),callOrigin);
  3. }
  4. publicstaticIntentgetCallIntent(Uriuri,StringcallOrigin){
  5. finalIntentintent=newIntent(Intent.ACTION_CALL_PRIVILEGED,uri);
  6. intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  7. if(callOrigin!=null){
  8. intent.putExtra(DialtactsActivity.EXTRA_CALL_ORIGIN,callOrigin);
  9. }
  10. returnintent;
  11. }

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
  1. protectedvoidonCreate(Bundleicicle){
  2. super.onCreate(icicle);
  3. setContentView(R.layout.outgoing_call_broadcaster);
  4. mWaitingSpinner=(ProgressBar)findViewById(R.id.spinner);
  5. Intentintent=getIntent();
  6. if(DBG){
  7. finalConfigurationconfiguration=getResources().getConfiguration();
  8. Log.v(TAG,"onCreate:this="+this+",icicle="+icicle);
  9. Log.v(TAG,"-getIntent()="+intent);
  10. Log.v(TAG,"-configuration="+configuration);
  11. }
  12. if(icicle!=null){
  13. //icicle不为空,表示重新初始化先前关闭的OutgoingCallBroadcaster,
  14. //Inpracticethishappensveryrarely(becausethelifetime
  15. //ofthisactivityissoshort!),butit*can*happenifthe
  16. //frameworkdetectsaconfigurationchangeatexactlythe
  17. //rightmoment;
  18. //Inthiscase,donothing.OuronCreate()methodhasalready
  19. //runonce(withicicle==nullthefirsttime),whichmeans
  20. //thattheNEW_OUTGOING_CALLbroadcastforthisnewcallhas
  21. //alreadybeensent.
  22. Log.i(TAG,"onCreate:non-nullicicle!"
  23. +"Bailingout,notsendingNEW_OUTGOING_CALLbroadcast...");
  24. return;
  25. }
  26. //处理得到的intent
  27. processIntent(intent);
  28. if(DBG)Log.v(TAG,"AttheendofonCreate().isFinishing():"+isFinishing());
  29. }

函数直接调用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
  1. privatevoidprocessIntent(Intentintent){
  2. if(DBG){
  3. Log.v(TAG,"processIntent()="+intent+",thread:"+Thread.currentThread());
  4. }
  5. finalConfigurationconfiguration=getResources().getConfiguration();
  6. //电话拨号只对具有语音通信能力的设备而言
  7. if(!PhoneGlobals.sVoiceCapable){
  8. Log.i(TAG,"Thisdeviceisdetectedasnon-voice-capabledevice.");
  9. handleNonVoiceCapable(intent);
  10. return;
  11. }
  12. //得到相应的Action
  13. Stringaction=intent.getAction();
  14. //从Intent中取出电话号码
  15. Stringnumber=PhoneNumberUtils.getNumberFromIntent(intent,this);
  16. //电话号码检查
  17. if(number!=null){
  18. if(!PhoneNumberUtils.isUriNumber(number)){
  19. //根据键盘map将字符转换为相应的数字
  20. number=PhoneNumberUtils.convertKeypadLettersToDigits(number);
  21. number=PhoneNumberUtils.stripSeparators(number);
  22. }
  23. }else{
  24. Log.w(TAG,"ThenumberobtainedfromIntentisnull.");
  25. }
  26. //如果callNow为true,表示当前为不允许拦截的如紧急拨号,这种情形下就无需这NEW_OUTGOING_CALL流程
  27. booleancallNow;
  28. if(getClass().getName().equals(intent.getComponent().getClassName())){
  29. //IfwewerelauncheddirectlyfromtheOutgoingCallBroadcaster,
  30. //notoneofitsmoreprivilegedaliases,thenmakesurethat
  31. //onlythenon-privilegedactionsareallowed.
  32. if(!Intent.ACTION_CALL.equals(intent.getAction())){
  33. Log.w(TAG,"Attempttodelivernon-CALLaction;forcingtoCALL");
  34. intent.setAction(Intent.ACTION_CALL);
  35. }
  36. }
  37. //检查当前号码是否为紧急号码,只有CALL_PRIVILEGED和CALL_EMERGENCY类型的intent才允许拨打紧急号码
  38. //(NotethattheACTION_CALLcheckbelowdependsontheresultof
  39. //isPotentialLocalEmergencyNumber()ratherthanjustplain
  40. //isLocalEmergencyNumber()
  41. //100%确保第三方应用不允许通过传递如"9111234"这种无效号码来拨打紧急号码
  42. finalbooleanisExactEmergencyNumber=(number!=null)&&PhoneNumberUtils.isLocalEmergencyNumber(number,this);
  43. finalbooleanisPotentialEmergencyNumber=(number!=null)&&PhoneNumberUtils.isPotentialLocalEmergencyNumber(number,this);
  44. if(VDBG){
  45. Log.v(TAG,"-Checkingrestrictionsfornumber'"+number+"':");
  46. Log.v(TAG,"isExactEmergencyNumber="+isExactEmergencyNumber);
  47. Log.v(TAG,"isPotentialEmergencyNumber="+isPotentialEmergencyNumber);
  48. }
  49. if(Intent.ACTION_CALL_PRIVILEGED.equals(action)){
  50. if(isPotentialEmergencyNumber){
  51. Log.i(TAG,"ACTION_CALL_PRIVILEGEDisusedwhilethenumberisapotential"
  52. +"emergencynumber.UseACTION_CALL_EMERGENCYasanactioninstead.");
  53. action=Intent.ACTION_CALL_EMERGENCY;
  54. }else{
  55. action=Intent.ACTION_CALL;
  56. }
  57. if(DBG)Log.v(TAG,"-updatingactionfromCALL_PRIVILEGEDto"+action);
  58. intent.setAction(action);
  59. }
  60. //如果普通拨打的号码为紧急号码,则启动电话拨号器
  61. if(Intent.ACTION_CALL.equals(action)){
  62. if(isPotentialEmergencyNumber){
  63. Log.w(TAG,"Cannotcallpotentialemergencynumber'"+number
  64. +"'withCALLIntent"+intent+".");
  65. Log.i(TAG,"Launchingdefaultdialerinstead...");
  66. //启动默认的电话拨号器DialtactsActivity
  67. IntentinvokeFrameworkDialer=newIntent();
  68. invokeFrameworkDialer.setClassName("com.android.contacts","com.android.contacts.DialtactsActivity");
  69. invokeFrameworkDialer.setAction(Intent.ACTION_DIAL);
  70. invokeFrameworkDialer.setData(intent.getData());
  71. if(DBG)Log.v(TAG,"onCreate():callingstartActivityforDialer:"+invokeFrameworkDialer);
  72. startActivity(invokeFrameworkDialer);
  73. finish();
  74. return;
  75. }
  76. callNow=false;
  77. //如果是紧急拨号,1.通过紧急拨号器拨号;2.ACTION_CALL_PRIVILEGED拨打紧急号码;将callNow设置为true
  78. }elseif(Intent.ACTION_CALL_EMERGENCY.equals(action)){
  79. if(!isPotentialEmergencyNumber){
  80. Log.w(TAG,"Cannotcallnon-potential-emergencynumber"+number
  81. +"withEMERGENCY_CALLIntent"+intent+"."
  82. +"FinishtheActivityimmediately.");
  83. finish();
  84. return;
  85. }
  86. callNow=true;
  87. }else{
  88. Log.e(TAG,"UnhandledIntent"+intent+".FinishtheActivityimmediately.");
  89. finish();
  90. return;
  91. }
  92. //唤醒屏幕
  93. PhoneGlobals.getInstance().wakeUpScreen();
  94. //Ifnumberisnull,we'reprobablytryingtocallanon-existentvoicemailnumber,
  95. //sendanemptyflashorsomethingelseisfishy.Whatevertheproblem,there'sno
  96. //number,sothere'snopointinallowingappstomodifythenumber.
  97. if(TextUtils.isEmpty(number)){
  98. if(intent.getBooleanExtra(EXTRA_SEND_EMPTY_FLASH,false)){
  99. Log.i(TAG,"onCreate:SEND_EMPTY_FLASH...");
  100. PhoneUtils.sendEmptyFlash(PhoneGlobals.getPhone());
  101. finish();
  102. return;
  103. }else{
  104. Log.i(TAG,"onCreate:nulloremptynumber,settingcallNow=true...");
  105. callNow=true;
  106. }
  107. }
  108. //如果是紧急拨号,直接启动拨号界面
  109. if(callNow){
  110. Log.i(TAG,"onCreate():callNowcase!CallingplaceCall():"+intent);
  111. //Initiatetheoutgoingcall,andsimultaneouslylaunchthe
  112. //InCallScreentodisplaythein-callUI:
  113. PhoneGlobals.getInstance().callController.placeCall(intent);
  114. }
  115. //Rememberthecalloriginsothatuserswillbeabletoseeanappropriatescreen
  116. //afterthephonecall.ThisshouldaffectbothphonecallsandSIPcalls.
  117. finalStringcallOrigin=intent.getStringExtra(PhoneGlobals.EXTRA_CALL_ORIGIN);
  118. if(callOrigin!=null){
  119. if(DBG)Log.v(TAG,"-Calloriginispassed("+callOrigin+")");
  120. PhoneGlobals.getInstance().setLatestActiveCallOrigin(callOrigin);
  121. }else{
  122. if(DBG)Log.v(TAG,"-Calloriginisnotpassed.Resetcurrentone.");
  123. PhoneGlobals.getInstance().resetLatestActiveCallOrigin();
  124. }
  125. //Fornow,SIPcallswillbeprocesseddirectlywithouta
  126. //NEW_OUTGOING_CALLbroadcast.
  127. //
  128. //TODO:Inthefuture,though,3rdpartyapps*should*beallowedto
  129. //interceptoutgoingcallstoSIPaddressesaswell.Todothis,weshould
  130. //(1)updatetheNEW_OUTGOING_CALLintentdocumentationtoexplainthis
  131. //case,and(2)passtheoutgoingSIPaddressby*not*overloadingthe
  132. //EXTRA_PHONE_NUMBERextra,butinsteadusinganewseparateextratohold
  133. //theoutgoingSIPaddress.(Besuretodocumentwhetherit'saURIorjust
  134. //aplainaddress,whetheritcouldbeatel:URI,etc.)
  135. Uriuri=intent.getData();
  136. Stringscheme=uri.getScheme();
  137. if(Constants.SCHEME_SIP.equals(scheme)||PhoneNumberUtils.isUriNumber(number)){
  138. Log.i(TAG,"TherequestednumberwasdetectedasSIPcall.");
  139. startSipCallOptionHandler(this,intent,uri,number);
  140. finish();
  141. return;
  142. }
  143. IntentbroadcastIntent=newIntent(Intent.ACTION_NEW_OUTGOING_CALL);
  144. if(number!=null){
  145. broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER,number);
  146. }
  147. PhoneUtils.checkAndCopyPhoneProviderExtras(intent,broadcastIntent);
  148. broadcastIntent.putExtra(EXTRA_ALREADY_CALLED,callNow);
  149. broadcastIntent.putExtra(EXTRA_ORIGINAL_URI,uri.toString());
  150. //Needtoraiseforegroundin-callUIassoonaspossiblewhileallowing3rdpartyapp
  151. //tointercepttheoutgoingcall.
  152. broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
  153. if(DBG)Log.v(TAG,"-Broadcastingintent:"+broadcastIntent+".");
  154. //发送超时消息,当OutgoingCallReceiver在指定的时间内还未接受到广播时,显示超时
  155. mHandler.sendEmptyMessageDelayed(EVENT_OUTGOING_CALL_TIMEOUT,
  156. OUTGOING_CALL_TIMEOUT_THRESHOLD);
  157. //发送ACTION_NEW_OUTGOING_CALL广播
  158. sendOrderedBroadcastAsUser(broadcastIntent,UserHandle.OWNER,
  159. PERMISSION,newOutgoingCallReceiver(),
  160. null,//scheduler
  161. Activity.RESULT_OK,//initialCode
  162. number,//initialData:initialvaluefortheresultdata
  163. null);//initialExtras
  164. }
首先获取Intent对象,获取拨出的号码。接着判断号码是否为紧急号码,如果是紧急号码,将callNow变量赋值为true,启动InCallScreen,并发送Intent.ACTION_NEW_OUTGOING_CALL广播。
[java] view plain copy
  1. publicvoidonReceive(Contextcontext,Intentintent){
  2. mHandler.removeMessages(EVENT_OUTGOING_CALL_TIMEOUT);
  3. doReceive(context,intent);
  4. if(DBG)Log.v(TAG,"OutgoingCallReceiverisgoingtofinishtheActivityitself.");
  5. finish();
  6. }
直接调用函数doReceive函数来处理ntent.ACTION_NEW_OUTGOING_CALL广播 [java] view plain copy
  1. publicvoiddoReceive(Contextcontext,Intentintent){
  2. if(DBG)Log.v(TAG,"doReceive:"+intent);
  3. booleanalreadyCalled;
  4. Stringnumber;
  5. StringoriginalUri;
  6. alreadyCalled=intent.getBooleanExtra(OutgoingCallBroadcaster.EXTRA_ALREADY_CALLED,false);
  7. if(alreadyCalled){
  8. if(DBG)Log.v(TAG,"CALLalreadyplaced--returning.");
  9. return;
  10. }
  11. number=getResultData();
  12. if(VDBG)Log.v(TAG,"-gotnumberfromresultData:'"+number+"'");
  13. finalPhoneGlobalsapp=PhoneGlobals.getInstance();
  14. //如果电话支持Otasp
  15. if(TelephonyCapabilities.supportsOtasp(app.phone)){
  16. booleanactivateState=(app.cdmaOtaScreenState.otaScreenState
  17. ==OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION);
  18. booleandialogState=(app.cdmaOtaScreenState.otaScreenState
  19. ==OtaUtils.CdmaOtaScreenState.OtaScreenState
  20. .OTA_STATUS_SUCCESS_FAILURE_DLG);
  21. booleanisOtaCallActive=false;
  22. if((app.cdmaOtaScreenState.otaScreenState
  23. ==OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_PROGRESS)
  24. ||(app.cdmaOtaScreenState.otaScreenState
  25. ==OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_LISTENING)){
  26. isOtaCallActive=true;
  27. }
  28. if(activateState||dialogState){
  29. //TheOTASPsequenceisactive,buteither(1)thecall
  30. //hasn'tstartedyet,or(2)thecallhasendedandwe're
  31. //showingthesuccess/failurescreen.Ineitherofthese
  32. //casesit'sOKtomakeanewoutgoingcall,butweneed
  33. //totakedownanyOTASP-relatedUIfirst.
  34. if(dialogState)app.dismissOtaDialogs();
  35. app.clearOtaState();
  36. app.clearInCallScreenMode();
  37. }elseif(isOtaCallActive){
  38. //TheactualOTASPcallisactive.Don'tallownew
  39. //outgoingcallsatallfromthisstate.
  40. Log.w(TAG,"OTASPcallisactive:disallowinganewoutgoingcall.");
  41. return;
  42. }
  43. }
  44. if(number==null){
  45. if(DBG)Log.v(TAG,"CALLcancelled(nullnumber),returning...");
  46. return;
  47. }elseif(TelephonyCapabilities.supportsOtasp(app.phone)
  48. &&(app.phone.getState()!=PhoneConstants.State.IDLE)
  49. &&(app.phone.isOtaSpNumber(number))){
  50. if(DBG)Log.v(TAG,"Callisactive,a2ndOTAcallcancelled--returning.");
  51. return;
  52. }elseif(PhoneNumberUtils.isPotentialLocalEmergencyNumber(number,context)){
  53. Log.w(TAG,"Cannotmodifyoutgoingcalltoemergencynumber"+number+".");
  54. return;
  55. }
  56. originalUri=intent.getStringExtra(OutgoingCallBroadcaster.EXTRA_ORIGINAL_URI);
  57. if(originalUri==null){
  58. Log.e(TAG,"IntentismissingEXTRA_ORIGINAL_URI--returning.");
  59. return;
  60. }
  61. Uriuri=Uri.parse(originalUri);
  62. number=PhoneNumberUtils.convertKeypadLettersToDigits(number);
  63. number=PhoneNumberUtils.stripSeparators(number);
  64. if(DBG)Log.v(TAG,"doReceive:proceedingwithcall...");
  65. if(VDBG)Log.v(TAG,"-uri:"+uri);
  66. if(VDBG)Log.v(TAG,"-actualnumbertodial:'"+number+"'");
  67. startSipCallOptionHandler(context,intent,uri,number);
  68. }

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
  1. privatevoidstartSipCallOptionHandler(Contextcontext,Intentintent,
  2. Uriuri,Stringnumber){
  3. if(VDBG){
  4. Log.i(TAG,"startSipCallOptionHandler...");
  5. Log.i(TAG,"-intent:"+intent);
  6. Log.i(TAG,"-uri:"+uri);
  7. Log.i(TAG,"-number:"+number);
  8. }
  9. //创建原始电话拨号intent的副本
  10. IntentnewIntent=newIntent(Intent.ACTION_CALL,uri);
  11. newIntent.putExtra(EXTRA_ACTUAL_NUMBER_TO_DIAL,number);
  12. PhoneUtils.checkAndCopyPhoneProviderExtras(intent,newIntent);
  13. IntentselectPhoneIntent=newIntent(ACTION_SIP_SELECT_PHONE,uri);
  14. selectPhoneIntent.setClass(context,SipCallOptionHandler.class);
  15. selectPhoneIntent.putExtra(EXTRA_NEW_CALL_INTENT,newIntent);
  16. selectPhoneIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  17. if(DBG){
  18. Log.v(TAG,"startSipCallOptionHandler():"+
  19. "callingstartActivity:"+selectPhoneIntent);
  20. }
  21. //启动电话类型选择界面
  22. context.startActivity(selectPhoneIntent);
  23. }
电话类型选择处理:1.读取用户设置;2.弹出对话框让用户选择
src\com\android\phone\SipCallOptionHandler.java [java] view plain copy
  1. publicvoidonCreate(BundlesavedInstanceState){
  2. super.onCreate(savedInstanceState);
  3. Intentintent=getIntent();
  4. Stringaction=intent.getAction();
  5. if(!OutgoingCallBroadcaster.ACTION_SIP_SELECT_PHONE.equals(action)){
  6. Log.wtf(TAG,"onCreate:gotintentaction'"+action+"',expected"
  7. +OutgoingCallBroadcaster.ACTION_SIP_SELECT_PHONE);
  8. finish();
  9. return;
  10. }
  11. //取出原始电话拨号intent的副本
  12. mIntent=(Intent)intent.getParcelableExtra(OutgoingCallBroadcaster.EXTRA_NEW_CALL_INTENT);
  13. if(mIntent==null){
  14. finish();
  15. return;
  16. }
  17. //Allowthisactivitytobevisibleinfrontofthekeyguard.
  18. getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
  19. //-Ifit'sasip:URI,thisisdefinitelyaSIPcall,regardless
  20. //ofwhetherthedataisaSIPaddressoraregularphone
  21. //number.
  22. //-Ifthisisatel:URIbutthedatacontainsan"@"character
  23. //(seePhoneNumberUtils.isUriNumber())weconsiderthattobea
  24. //SIPnumbertoo.
  25. booleanvoipSupported=PhoneUtils.isVoipSupported();
  26. if(DBG)Log.v(TAG,"voipSupported:"+voipSupported);
  27. mSipProfileDb=newSipProfileDb(this);
  28. mSipSharedPreferences=newSipSharedPreferences(this);
  29. mCallOption=mSipSharedPreferences.getSipCallOption();
  30. if(DBG)Log.v(TAG,"Calloption:"+mCallOption);
  31. Uriuri=mIntent.getData();
  32. Stringscheme=uri.getScheme();
  33. mNumber=PhoneNumberUtils.getNumberFromIntent(mIntent,this);
  34. booleanisInCellNetwork=PhoneGlobals.getInstance().phoneMgr.isRadioOn();
  35. booleanisKnownCallScheme=Constants.SCHEME_TEL.equals(scheme)
  36. ||Constants.SCHEME_SIP.equals(scheme);
  37. booleanisRegularCall=Constants.SCHEME_TEL.equals(scheme)
  38. &&!PhoneNumberUtils.isUriNumber(mNumber);
  39. //Bypassthehandlerifthecallschemeisnotsiportel.
  40. if(!isKnownCallScheme){
  41. setResultAndFinish();
  42. return;
  43. }
  44. //CheckifVoIPfeatureissupported.
  45. if(!voipSupported){
  46. if(!isRegularCall){
  47. showDialog(DIALOG_NO_VOIP);
  48. }else{
  49. setResultAndFinish();
  50. }
  51. return;
  52. }
  53. if(!PhoneUtils.hasPhoneProviderExtras(mIntent)){
  54. if(!isNetworkConnected()){
  55. if(!isRegularCall){
  56. //显示无网络错误提示对话框
  57. showDialog(DIALOG_NO_INTERNET_ERROR);
  58. return;
  59. }
  60. }else{
  61. if(mCallOption.equals(Settings.System.SIP_ASK_ME_EACH_TIME)
  62. &&isRegularCall&&isInCellNetwork){
  63. //显示电话类型选择对话框
  64. showDialog(DIALOG_SELECT_PHONE_TYPE);
  65. return;
  66. }
  67. if(!mCallOption.equals(Settings.System.SIP_ADDRESS_ONLY)
  68. ||!isRegularCall){
  69. mUseSipPhone=true;
  70. }
  71. }
  72. }
  73. if(mUseSipPhone){
  74. //Ifthereisnosipprofileanditisaregularcall,thenwe
  75. //shouldusepstnnetworkinstead.
  76. if((mSipProfileDb.getProfilesCount()>0)||!isRegularCall){
  77. startGetPrimarySipPhoneThread();
  78. return;
  79. }else{
  80. mUseSipPhone=false;
  81. }
  82. }
  83. setResultAndFinish();
选择SIP拨号还是PSTN拨号 [java] view plain copy
  1. privatevoidsetResultAndFinish(){
  2. runOnUiThread(newRunnable(){
  3. publicvoidrun(){
  4. if(mOutgoingSipProfile!=null){
  5. if(!isNetworkConnected()){
  6. showDialog(DIALOG_NO_INTERNET_ERROR);
  7. return;
  8. }
  9. if(DBG)Log.v(TAG,"primarySIPURIis"+
  10. mOutgoingSipProfile.getUriString());
  11. createSipPhoneIfNeeded(mOutgoingSipProfile);
  12. mIntent.putExtra(OutgoingCallBroadcaster.EXTRA_SIP_PHONE_URI,
  13. mOutgoingSipProfile.getUriString());
  14. if(mMakePrimary){
  15. mSipSharedPreferences.setPrimaryAccount(
  16. mOutgoingSipProfile.getUriString());
  17. }
  18. }
  19. //mUseSipPhone在SipCallOptionHandler的onCreate函数中被设置为false
  20. if(mUseSipPhone&&mOutgoingSipProfile==null){
  21. showDialog(DIALOG_START_SIP_SETTINGS);
  22. return;
  23. }else{
  24. //Woohoo--it'sfinallyOKtoinitiatetheoutgoingcall!
  25. PhoneGlobals.getInstance().callController.placeCall(mIntent);
  26. }
  27. finish();
  28. }
  29. });
  30. }
src\com\android\phone\CallController.java
[java] view plain copy
  1. publicvoidplaceCall(Intentintent){
  2. log("placeCall()...intent="+intent);
  3. if(VDBG)log("extras="+intent.getExtras());
  4. finalInCallUiStateinCallUiState=mApp.inCallUiState;
  5. if(intent==null){
  6. Log.wtf(TAG,"placeCall:calledwithnullintent");
  7. thrownewIllegalArgumentException("placeCall:calledwithnullintent");
  8. }
  9. Stringaction=intent.getAction();
  10. Uriuri=intent.getData();
  11. if(uri==null){
  12. Log.wtf(TAG,"placeCall:intenthadnodata");
  13. thrownewIllegalArgumentException("placeCall:intenthadnodata");
  14. }
  15. Stringscheme=uri.getScheme();
  16. Stringnumber=PhoneNumberUtils.getNumberFromIntent(intent,mApp);
  17. if(VDBG){
  18. log("-action:"+action);
  19. log("-uri:"+uri);
  20. log("-scheme:"+scheme);
  21. log("-number:"+number);
  22. }
  23. if(!(Intent.ACTION_CALL.equals(action)
  24. ||Intent.ACTION_CALL_EMERGENCY.equals(action)
  25. ||Intent.ACTION_CALL_PRIVILEGED.equals(action))){
  26. Log.wtf(TAG,"placeCall:unexpectedintentaction"+action);
  27. thrownewIllegalArgumentException("Unexpectedaction:"+action);
  28. }
  29. //ChecktoseeifthisisanOTASPcall(the"activation"call
  30. //usedtoprovisionCDMAdevices),andifso,dosome
  31. //OTASP-specificsetup.
  32. Phonephone=mApp.mCM.getDefaultPhone();
  33. if(TelephonyCapabilities.supportsOtasp(phone)){
  34. checkForOtaspCall(intent);
  35. }
  36. mApp.setRestoreMuteOnInCallResume(false);
  37. //Ifaproviderisused,extracttheinfotobuildthe
  38. //overlayandroutethecall.Theoverlaywillbe
  39. //displayedwhentheInCallScreenbecomesvisible.
  40. if(PhoneUtils.hasPhoneProviderExtras(intent)){
  41. inCallUiState.setProviderInfo(intent);
  42. }else{
  43. inCallUiState.clearProviderInfo();
  44. }
  45. //拨号
  46. CallStatusCodestatus=placeCallInternal(intent);
  47. switch(status){
  48. caseSUCCESS:
  49. caseEXITED_ECM:
  50. if(DBG)log("==>placeCall():successfromplaceCallInternal():"+status);
  51. if(status==CallStatusCode.EXITED_ECM){
  52. //Callsucceeded,butwealsoneedtotellthe
  53. //InCallScreentoshowthe"ExitingECM"warning.
  54. inCallUiState.setPendingCallStatusCode(CallStatusCode.EXITED_ECM);
  55. }else{
  56. //Callsucceeded.There'sno"errorcondition"that
  57. //needstobedisplayedtotheuser,soclearoutthe
  58. //InCallUiState's"pendingcallstatuscode".
  59. inCallUiState.clearPendingCallStatusCode();
  60. }
  61. //Notifythephoneappthatacallisbeginningsoitcan
  62. //enabletheproximitysensor
  63. mApp.setBeginningCall(true);
  64. break;
  65. default:
  66. //Anyotherstatuscodeisafailure.
  67. log("==>placeCall():failurecodefromplaceCallInternal():"+status);
  68. //Handlethevariouserrorconditionsthatcanoccurwhen
  69. //initiatinganoutgoingcall,typicallybydirectingthe
  70. //InCallScreentodisplayadiagnosticmessage(viathe
  71. //"pendingcallstatuscode"flag.)
  72. handleOutgoingCallError(status);
  73. break;
  74. }
  75. mApp.displayCallScreen();
  76. }
该函数首先得到拨打的电话号码及默认的Phone对象,调用placeCallInternal发起拨号请求,同时启动电话呼叫界面InCallScreen。

1.拨号流程

[java] view plain copy
  1. privateCallStatusCodeplaceCallInternal(Intentintent){
  2. finalInCallUiStateinCallUiState=mApp.inCallUiState;
  3. finalUriuri=intent.getData();
  4. finalStringscheme=(uri!=null)?uri.getScheme():null;
  5. Stringnumber;
  6. Phonephone=null;
  7. CallStatusCodeokToCallStatus=checkIfOkToInitiateOutgoingCall(
  8. mCM.getServiceState());
  9. try{
  10. number=PhoneUtils.getInitialNumber(intent);
  11. if(VDBG)log("-actualnumbertodial:'"+number+"'");
  12. StringsipPhoneUri=intent.getStringExtra(OutgoingCallBroadcaster.EXTRA_SIP_PHONE_URI);
  13. phone=PhoneUtils.pickPhoneBasedOnNumber(mCM,scheme,number,sipPhoneUri);
  14. if(VDBG)log("-gotPhoneinstance:"+phone+",class="+phone.getClass());
  15. okToCallStatus=checkIfOkToInitiateOutgoingCall(phone.getServiceState().getState());
  16. }catch(PhoneUtils.VoiceMailNumberMissingExceptionex){
  17. if(okToCallStatus!=CallStatusCode.SUCCESS){
  18. if(DBG)log("VoicemailnumbernotreachableincurrentSIMcardstate.");
  19. returnokToCallStatus;
  20. }
  21. if(DBG)log("VoiceMailNumberMissingExceptionfromgetInitialNumber()");
  22. returnCallStatusCode.VOICEMAIL_NUMBER_MISSING;
  23. }
  24. if(number==null){
  25. Log.w(TAG,"placeCall:couldn'tgetaphonenumberfromIntent"+intent);
  26. returnCallStatusCode.NO_PHONE_NUMBER_SUPPLIED;
  27. }
  28. booleanisEmergencyNumber=PhoneNumberUtils.isLocalEmergencyNumber(number,mApp);
  29. booleanisPotentialEmergencyNumber=PhoneNumberUtils.isPotentialLocalEmergencyNumber(number,mApp);
  30. booleanisEmergencyIntent=Intent.ACTION_CALL_EMERGENCY.equals(intent.getAction());
  31. if(isPotentialEmergencyNumber&&!isEmergencyIntent){
  32. Log.e(TAG,"Non-CALL_EMERGENCYIntent"+intent+"attemptedtocallpotentialemergencynumber"+number+".");
  33. returnCallStatusCode.CALL_FAILED;
  34. }elseif(!isPotentialEmergencyNumber&&isEmergencyIntent){
  35. Log.e(TAG,"ReceivedCALL_EMERGENCYIntent"+intent
  36. +"withnon-potential-emergencynumber"+number
  37. +"--failingcall.");
  38. returnCallStatusCode.CALL_FAILED;
  39. }
  40. //Ifwe'retryingtocallanemergencynumber,thenit'sOKto
  41. //proceedincertainstateswherewe'dotherwisebringup
  42. //anerrordialog:
  43. //-Ifwe'reinEMERGENCY_ONLYmode,then(obviously)you'reallowed
  44. //todialemergencynumbers.
  45. //-Ifwe'reOUT_OF_SERVICE,westillattempttomakeacall,
  46. //sincetheradiowillregistertoanyavailablenetwork.
  47. if(isEmergencyNumber
  48. &&((okToCallStatus==CallStatusCode.EMERGENCY_ONLY)
  49. ||(okToCallStatus==CallStatusCode.OUT_OF_SERVICE))){
  50. if(DBG)log("placeCall:Emergencynumberdetectedwithstatus="+okToCallStatus);
  51. okToCallStatus=CallStatusCode.SUCCESS;
  52. if(DBG)log("==>UPDATINGstatusto:"+okToCallStatus);
  53. }
  54. if(okToCallStatus!=CallStatusCode.SUCCESS){
  55. //Ifthisisanemergencycall,launchtheEmergencyCallHelperService
  56. //toturnontheradioandretrythecall.
  57. if(isEmergencyNumber&&(okToCallStatus==CallStatusCode.POWER_OFF)){
  58. Log.i(TAG,"placeCall:TryingtomakeemergencycallwhilePOWER_OFF!");
  59. //Ifneeded,lazilyinstantiateanEmergencyCallHelperinstance.
  60. synchronized(this){
  61. if(mEmergencyCallHelper==null){
  62. mEmergencyCallHelper=newEmergencyCallHelper(this);
  63. }
  64. }
  65. //...andkickoffthe"emergencycallfromairplanemode"sequence.
  66. mEmergencyCallHelper.startEmergencyCallFromAirplaneModeSequence(number);
  67. returnCallStatusCode.SUCCESS;
  68. }else{
  69. if(DBG)log("==>placeCallInternal():non-successstatus:"+okToCallStatus);
  70. returnokToCallStatus;
  71. }
  72. }
  73. //Ok,wecanproceedwiththisoutgoingcall.
  74. inCallUiState.needToShowCallLostDialog=false;
  75. inCallUiState.clearProgressIndication();
  76. UricontactUri=intent.getData();
  77. //真正的电话拨号过程
  78. intcallStatus=PhoneUtils.placeCall(mApp,
  79. phone,
  80. number,
  81. contactUri,
  82. (isEmergencyNumber||isEmergencyIntent),
  83. inCallUiState.providerGatewayUri);
  84. switch(callStatus){
  85. casePhoneUtils.CALL_STATUS_DIALED:
  86. if(VDBG)log("placeCall:PhoneUtils.placeCall()succeededforregularcall'"
  87. +number+"'.");
  88. if(VDBG)log("-inCallUiState.inCallScreenMode="
  89. +inCallUiState.inCallScreenMode);
  90. if(inCallUiState.inCallScreenMode==InCallScreenMode.OTA_NORMAL){
  91. if(VDBG)log("==>OTA_NORMALnote:switchingtoOTA_STATUS_LISTENING.");
  92. mApp.cdmaOtaScreenState.otaScreenState=
  93. CdmaOtaScreenState.OtaScreenState.OTA_STATUS_LISTENING;
  94. }
  95. booleanvoicemailUriSpecified=scheme!=null&&scheme.equals("voicemail");
  96. //Whenvoicemailisrequestedmostlikelytheuserwantstoopen
  97. //dialpadimmediately,soweshowitinthefirstplace.
  98. //Otherwisewewanttomakesuretheusercanseetheregular
  99. //in-callUIwhilethenewcallisdialing,andwhenit
  100. //firstgetsconnected.)
  101. inCallUiState.showDialpad=voicemailUriSpecified;
  102. //Forvoicemails,weaddcontexttexttolettheuserknowthey
  103. //aredialingtheirvoicemail.
  104. //TODO:Thisisonlysethereandbecomesproblematicwhenswappingcalls
  105. inCallUiState.dialpadContextText=voicemailUriSpecified?
  106. phone.getVoiceMailAlphaTag():"";
  107. //Also,incaseapreviouscallwasalreadyactive(i.e.if
  108. //wejustdid"Addcall"),clearoutthe"history"ofDTMF
  109. //digitsyoutyped,tomakesureitdoesn'tpersistfromthe
  110. //previouscalltothenewcall.
  111. //TODO:itwouldbemoreprecisetodothiswhentheactual
  112. //phonestatechangehappens(i.e.whenanewforeground
  113. //callappearsandthepreviouscallmovestothe
  114. //background),buttheInCallScreendoesn'tkeepenough
  115. //staterightnowtonoticethatspecifictransitionin
  116. //onPhoneStateChanged().
  117. inCallUiState.dialpadDigits=null;
  118. //CheckforanobscureECM-relatedscenario:Ifthephone
  119. //iscurrentlyinECM(Emergencycallbackmode)andwe
  120. //dialanon-emergencynumber,thatautomatically
  121. //*cancels*ECM.Sowarntheuseraboutit.
  122. //(SeeInCallScreen.showExitingECMDialog()formoreinfo.)
  123. booleanexitedEcm=false;
  124. if(PhoneUtils.isPhoneInEcm(phone)&&!isEmergencyNumber){
  125. Log.i(TAG,"AbouttoexitECMbecauseofanoutgoingnon-emergencycall");
  126. exitedEcm=true;//thiswillcauseustoreturnEXITED_ECMfromthismethod
  127. }
  128. if(phone.getPhoneType()==PhoneConstants.PHONE_TYPE_CDMA){
  129. //Startthetimerfor3WayCallerInfo
  130. if(mApp.cdmaPhoneCallState.getCurrentCallState()
  131. ==CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE){
  132. //UnmuteforthesecondMOcall
  133. PhoneUtils.setMute(false);
  134. //Thisisa"CDMA3-waycall",whichmeansthatyou'redialinga
  135. //2ndoutgoingcallwhileapreviouscallisalreadyinprogress.
  136. //
  137. //DuetothelimitationsofCDMAthiscalldoesn'tactuallygo
  138. //throughtheDIALING/ALERTINGstates,sowecan'ttellforsure
  139. //when(orif)it'sactuallyanswered.Butwewanttoshow
  140. //*some*indicationofwhat'sgoingonintheUI,sowe"fakeit"
  141. //bydisplayingthe"Dialing"statefor3seconds.
  142. //SetthemThreeWayCallOrigStateDialingstatetotrue
  143. mApp.cdmaPhoneCallState.setThreeWayCallOrigState(true);
  144. //Schedulethe"Dialing"indicationtobetakendownin3seconds:
  145. sendEmptyMessageDelayed(THREEWAY_CALLERINFO_DISPLAY_DONE,
  146. THREEWAY_CALLERINFO_DISPLAY_TIME);
  147. }
  148. }
  149. //Success!
  150. if(exitedEcm){
  151. returnCallStatusCode.EXITED_ECM;
  152. }else{
  153. returnCallStatusCode.SUCCESS;
  154. }
  155. casePhoneUtils.CALL_STATUS_DIALED_MMI:
  156. if(DBG)log("placeCall:specifiednumberwasanMMIcode:'"+number+"'.");
  157. //Thepassed-innumberwasanMMIcode,notaregularphonenumber!
  158. //Thisisn'treallyafailure;theDialermayhavedeliberately
  159. //firedanACTION_CALLintenttodialanMMIcode,likefora
  160. //USSDcall.
  161. //
  162. //PresumablyanMMI_INITIATEmessagewillcomeinshortly
  163. //(andwe'llbringupthe"MMIStarted"dialog),orelse
  164. //anMMI_COMPLETEwillcomein(whichwilltakeustoa
  165. //differentActivity;seePhoneUtils.displayMMIComplete()).
  166. returnCallStatusCode.DIALED_MMI;
  167. casePhoneUtils.CALL_STATUS_FAILED:
  168. Log.w(TAG,"placeCall:PhoneUtils.placeCall()FAILEDfornumber'"
  169. +number+"'.");
  170. //Wecouldn'tsuccessfullyplacethecall;therewassome
  171. //failureinthetelephonylayer.
  172. returnCallStatusCode.CALL_FAILED;
  173. default:
  174. Log.wtf(TAG,"placeCall:unknowncallStatus"+callStatus
  175. +"fromPhoneUtils.placeCall()fornumber'"+number+"'.");
  176. returnCallStatusCode.SUCCESS;//Trytocontinueanyway...
  177. }
  178. }
该函数通过调用PhoneUtils类的placeCall函数进入Framework层异步完成电话呼叫

src\com\android\phone\PhoneUtils.java

[java] view plain copy
  1. publicstaticintplaceCall(Contextcontext,Phonephone,
  2. Stringnumber,UricontactRef,booleanisEmergencyCall,
  3. UrigatewayUri){
  4. finalPhoneGlobalsapp=PhoneGlobals.getInstance();
  5. booleanuseGateway=false;
  6. if(null!=gatewayUri&&
  7. !isEmergencyCall&&
  8. PhoneUtils.isRoutableViaGateway(number)){//FilteroutMMI,OTAandothercodes.
  9. useGateway=true;
  10. }
  11. intstatus=CALL_STATUS_DIALED;
  12. Connectionconnection;
  13. StringnumberToDial;
  14. if(useGateway){
  15. if(null==gatewayUri||!Constants.SCHEME_TEL.equals(gatewayUri.getScheme())){
  16. Log.e(LOG_TAG,"UnsupportedURL:"+gatewayUri);
  17. returnCALL_STATUS_FAILED;
  18. }
  19. //WecanusegetSchemeSpecificPartbecausewedon'tallow#
  20. //inthegatewaynumbers(treatedafragmentdelim.)However
  21. //ifweallowmorecomplexgatewaynumberssequence(with
  22. //passwordsorwhatnot)thatuse#,thismaybreak.
  23. //TODO:NeedtosupportMMIcodes.
  24. numberToDial=gatewayUri.getSchemeSpecificPart();
  25. }else{
  26. numberToDial=number;
  27. }
  28. //RememberifthephonestatewasinIDLEstatebeforethiscall.
  29. //AftercallingCallManager#dial(),getState()willreturndifferentstate.
  30. finalbooleaninitiallyIdle=app.mCM.getState()==PhoneConstants.State.IDLE;
  31. try{
  32. connection=app.mCM.dial(phone,numberToDial);
  33. }catch(CallStateExceptionex){
  34. //CallStateExceptionmeansanewoutgoingcallisnotcurrently
  35. //possible:eithernomorecallslotsexist,orthere'sanother
  36. //callalreadyintheprocessofdialingorringing.
  37. Log.w(LOG_TAG,"Exceptionfromapp.mCM.dial()",ex);
  38. returnCALL_STATUS_FAILED;
  39. //Notethatit'spossibleforCallManager.dial()toreturn
  40. //null*without*throwinganexception;thatindicatesthat
  41. //wedialedanMMI(seebelow).
  42. }
  43. intphoneType=phone.getPhoneType();
  44. //OnGSMphones,nullisreturnedforMMIcodes
  45. if(null==connection){
  46. if(phoneType==PhoneConstants.PHONE_TYPE_GSM&&gatewayUri==null){
  47. if(DBG)log("dialedMMIcode:"+number);
  48. status=CALL_STATUS_DIALED_MMI;
  49. }else{
  50. status=CALL_STATUS_FAILED;
  51. }
  52. }else{
  53. if(phoneType==PhoneConstants.PHONE_TYPE_CDMA){
  54. updateCdmaCallStateOnNewOutgoingCall(app);
  55. }
  56. //Cleanupthenumbertobedisplayed.
  57. if(phoneType==PhoneConstants.PHONE_TYPE_CDMA){
  58. number=CdmaConnection.formatDialString(number);
  59. }
  60. number=PhoneNumberUtils.extractNetworkPortion(number);
  61. number=PhoneNumberUtils.convertKeypadLettersToDigits(number);
  62. number=PhoneNumberUtils.formatNumber(number);
  63. if(gatewayUri==null){
  64. //phone.dial()succeeded:we'renowinanormalphonecall.
  65. //attachtheURItotheCallerInfoObjectifitisthere,
  66. //otherwisejustattachtheUriReference.
  67. //iftheuridoesnothavea"content"scheme,thenwetreat
  68. //itasifitdoesNOThaveauniquereference.
  69. Stringcontent=context.getContentResolver().SCHEME_CONTENT;
  70. if((contactRef!=null)&&(contactRef.getScheme().equals(content))){
  71. ObjectuserDataObject=connection.getUserData();
  72. if(userDataObject==null){
  73. connection.setUserData(contactRef);
  74. }else{
  75. //TODO:Thisbranchisdeadcode,wehave
  76. //justcreatedtheconnectionwhichhas
  77. //nouserdata(null)bydefault.
  78. if(userDataObjectinstanceofCallerInfo){
  79. ((CallerInfo)userDataObject).contactRefUri=contactRef;
  80. }else{
  81. ((CallerInfoToken)userDataObject).currentInfo.contactRefUri=
  82. contactRef;
  83. }
  84. }
  85. }
  86. }else{
  87. //Getthecallerinfosynchronouslybecauseweneedthefinal
  88. //CallerInfoobjecttoupdatethedialednumberwiththeone
  89. //requestedbytheuser(andnottheprovider'sgatewaynumber).
  90. CallerInfoinfo=null;
  91. Stringcontent=phone.getContext().getContentResolver().SCHEME_CONTENT;
  92. if((contactRef!=null)&&(contactRef.getScheme().equals(content))){
  93. info=CallerInfo.getCallerInfo(context,contactRef);
  94. }
  95. //Fallback,lookupcontactusingthephonenumberifthe
  96. //contact'sURIschemewasnotcontent://orifiswasbut
  97. //thelookupfailed.
  98. if(null==info){
  99. info=CallerInfo.getCallerInfo(context,number);
  100. }
  101. info.phoneNumber=number;
  102. connection.setUserData(info);
  103. }
  104. setAudioMode();
  105. if(DBG)log("abouttoactivatespeaker");
  106. //Checkisphoneinanydock,andturnonspeakeraccordingly
  107. finalbooleanspeakerActivated=activateSpeakerIfDocked(phone);
  108. //SeealsosimilarlogicinanswerCall().
  109. if(initiallyIdle&&!speakerActivated&&isSpeakerOn(app)
  110. &&!app.isBluetoothHeadsetAudioOn()){
  111. //Thisisnotanerrorbutmightcauseusers'confusion.Addlogjustincase.
  112. Log.i(LOG_TAG,"Forcingspeakeroffwheninitiatinganewoutgoingcall...");
  113. PhoneUtils.turnOnSpeaker(app,false,true);
  114. }
  115. }
  116. returnstatus;
  117. }
该函数调用framework层的CallManager的dial函数。 [java] view plain copy
  1. publicConnectiondial(Phonephone,StringdialString)throwsCallStateException{
  2. PhonebasePhone=getPhoneBase(phone);
  3. Connectionresult;
  4. if(VDBG){
  5. Log.d(LOG_TAG,"dial("+basePhone+","+dialString+")");
  6. Log.d(LOG_TAG,this.toString());
  7. }
  8. if(!canDial(phone)){
  9. thrownewCallStateException("cannotdialincurrentstate");
  10. }
  11. if(hasActiveFgCall()){
  12. PhoneactivePhone=getActiveFgCall().getPhone();
  13. booleanhasBgCall=!(activePhone.getBackgroundCall().isIdle());
  14. if(DBG){
  15. Log.d(LOG_TAG,"hasBgCall:"+hasBgCall+"sameChannel:"+(activePhone==basePhone));
  16. }
  17. if(activePhone!=basePhone){
  18. if(hasBgCall){
  19. Log.d(LOG_TAG,"Hangup");
  20. getActiveFgCall().hangup();
  21. }else{
  22. Log.d(LOG_TAG,"Switch");
  23. activePhone.switchHoldingAndActive();
  24. }
  25. }
  26. }
  27. result=basePhone.dial(dialString);
  28. if(VDBG){
  29. Log.d(LOG_TAG,"Enddial("+basePhone+","+dialString+")");
  30. Log.d(LOG_TAG,this.toString());
  31. }
  32. returnresult;
  33. }
函数首先取得相应类型的Phone,并判断该Phone的状态,这里得到PhoneProxy类型的Phone,PhoneProxy是所有类型Phone的代理类,在构造PhoneProxy时,把对应类型的Phone保存在其成员变量mActivePhone中,有关Phone,PhoneProxy,GmsPhone,CDMAPhone之间的关系请参看 Android电话Phone设计框架介绍,GSM类型的网络对应GSMPhone,因此这里将调用GSMPhone类的dial函数。

./telephony/java/com/android/internal/telephony/gsm/GSMPhone.java

[java] view plain copy
  1. publicConnectiondial(StringdialString)throwsCallStateException{
  2. returndial(dialString,null);
  3. }

[java] view plain copy
  1. publicConnectiondial(StringdialString,UUSInfouusInfo)throwsCallStateException{
  2. //NeedtomakesuredialStringgetsparsedproperly
  3. StringnewDialString=PhoneNumberUtils.stripSeparators(dialString);
  4. //handlein-callMMIfirstifapplicable
  5. if(handleInCallMmiCommands(newDialString)){
  6. returnnull;
  7. }
  8. //OnlylookattheNetworkportionformmi
  9. StringnetworkPortion=PhoneNumberUtils.extractNetworkPortionAlt(newDialString);
  10. GsmMmiCodemmi=GsmMmiCode.newFromDialString(networkPortion,this);
  11. if(mmi==null){
  12. returnmCT.dial(newDialString,uusInfo);
  13. }elseif(mmi.isTemporaryModeCLIR()){
  14. returnmCT.dial(mmi.dialingNumber,mmi.getCLIRMode(),uusInfo);
  15. }else{
  16. mPendingMMIs.add(mmi);
  17. mMmiRegistrants.notifyRegistrants(newAsyncResult(null,mmi,null));
  18. mmi.processCode();
  19. //FIXMEshouldthisreturnnullorsomethingelse?
  20. returnnull;
  21. }
  22. }
GSMPhone又通过CallTracker来向RIL发送请求,关于CallTracker的类关系请参阅 Android电话Phone设计框架介绍

./telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java

[java] view plain copy
  1. synchronizedConnectiondial(StringdialString,intclirMode,UUSInfouusInfo)throwsCallStateException{
  2. //notethatthistriggerscallstatechangednotif
  3. clearDisconnected();
  4. if(!canDial()){
  5. thrownewCallStateException("cannotdialincurrentstate");
  6. }
  7. //Thenewcallmustbeassignedtotheforegroundcall.
  8. //Thatcallmustbeidle,soplaceanythingthat's
  9. //thereonhold
  10. if(foregroundCall.getState()==GsmCall.State.ACTIVE){
  11. //thiswillprobablybedonebytheradioanyway
  12. //butthedialmightfailbeforethishappens
  13. //andweneedtomakesuretheforegroundcallisclear
  14. //forthenewlydialedconnection
  15. switchWaitingOrHoldingAndActive();
  16. //Fakelocalstatesothat
  17. //a)foregroundCallisemptyforthenewlydialedconnection
  18. //b)hasNonHangupStateChangedremainsfalseinthe
  19. //nextpoll,sothatwedon'tclearafaileddialingcall
  20. fakeHoldForegroundBeforeDial();
  21. }
  22. if(foregroundCall.getState()!=GsmCall.State.IDLE){
  23. //weshouldhavefailedin!canDial()abovebeforewegethere
  24. thrownewCallStateException("cannotdialincurrentstate");
  25. }
  26. //pendingMO=newGsmConnection(phone.getContext(),checkForTestEmergencyNumber(dialString),
  27. //this,foregroundCall);
  28. booleanisStkCall=getStkCall();
  29. log("GsmCallTrackerdial:isStkCall="+isStkCall);
  30. pendingMO=newGsmConnection(phone.getContext(),dialString,this,foregroundCall,isStkCall,false);
  31. hangupPendingMO=false;
  32. if(pendingMO.address==null||pendingMO.address.length()==0
  33. ||pendingMO.address.indexOf(PhoneNumberUtils.WILD)>=0
  34. ){
  35. //Phonenumberisinvalid
  36. pendingMO.cause=Connection.DisconnectCause.INVALID_NUMBER;
  37. //handlePollCalls()willnoticethiscallnotpresent
  38. //andwillmarkitasdropped.
  39. pollCallsWhenSafe();
  40. }else{
  41. //Alwaysunmutewheninitiatinganewcall
  42. setMute(false);
  43. //cm.dial(pendingMO.address,clirMode,uusInfo,obtainCompleteMessage());
  44. //Addforbug121825Start
  45. StringtmpAddr=pendingMO.address;
  46. if(PhoneNumberUtils.isCustomEmergencyNumber(pendingMO.address)){
  47. Log.d(LOG_TAG,"PendingMOisCustomEmergencycall");
  48. tmpAddr=tmpAddr+"/1";
  49. }
  50. //cm.dial(pendingMO.address,clirMode,uusInfo,isStkCall,obtainCompleteMessage());
  51. cm.dial(tmpAddr,clirMode,uusInfo,isStkCall,obtainCompleteMessage());
  52. //Addforbug121825End
  53. }
  54. updatePhoneState();
  55. phone.notifyPreciseCallStateChanged();
  56. returnpendingMO;
  57. }
cm的类型为CommandsInterface,RIL.java实现了CommandsInterface接口,因此GsmCallTracker最终是通过RIL来发送拨号请求的。

./telephony/java/com/android/internal/telephony/RIL.java

[java] view plain copy
  1. publicvoiddial(Stringaddress,intclirMode,UUSInfouusInfo,booleanisStkCall,Messageresult){
  2. RILRequestrr;
  3. if(address.indexOf('/')==-1){
  4. rr=RILRequest.obtain(RIL_REQUEST_DIAL,result);
  5. }else{
  6. rr=RILRequest.obtain(RIL_REQUEST_DIAL_EMERGENCY_CALL,result);
  7. }
  8. rr.mp.writeString(address);
  9. rr.mp.writeInt(clirMode);
  10. rr.mp.writeInt(0);//UUSinformationisabsent
  11. if(uusInfo==null){
  12. rr.mp.writeInt(0);//UUSinformationisabsent
  13. }else{
  14. rr.mp.writeInt(1);//UUSinformationispresent
  15. rr.mp.writeInt(uusInfo.getType());
  16. rr.mp.writeInt(uusInfo.getDcs());
  17. rr.mp.writeByteArray(uusInfo.getUserData());
  18. }
  19. rr.mp.writeInt(isStkCall?1:0);
  20. if(RILJ_LOGD)riljLog(rr.serialString()+">"+requestToString(rr.mRequest));
  21. send(rr);
  22. }

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
  1. protectedvoidsend(RILRequestrr){
  2. Messagemsg;
  3. if(mSocket==null){
  4. rr.onError(RADIO_NOT_AVAILABLE,null);
  5. rr.release();
  6. return;
  7. }
  8. msg=mSender.obtainMessage(EVENT_SEND,rr);
  9. acquireWakeLock();
  10. msg.sendToTarget();
  11. }
RILSender消息处理过程 [java] view plain copy
  1. classRILSenderextendsHandlerimplementsRunnable{
  2. publicRILSender(Looperlooper){
  3. super(looper);
  4. }
  5. byte[]dataLength=newbyte[4];
  6. publicvoid
  7. run(){
  8. //setupifneeded
  9. }
  10. @Overridepublicvoid
  11. handleMessage(Messagemsg){
  12. RILRequestrr=(RILRequest)(msg.obj);
  13. RILRequestreq=null;
  14. switch(msg.what){
  15. caseEVENT_SEND:
  16. /**
  17. *mRequestMessagePending++alreadyhappenedforevery
  18. *EVENT_SEND,thuswemustmakesure
  19. *mRequestMessagePending--happensonceandonlyonce
  20. */
  21. booleanalreadySubtracted=false;
  22. try{
  23. LocalSockets;
  24. s=mSocket;
  25. if(s==null){
  26. rr.onError(RADIO_NOT_AVAILABLE,null);
  27. rr.release();
  28. if(mRequestMessagesPending>0)
  29. mRequestMessagesPending--;
  30. alreadySubtracted=true;
  31. return;
  32. }
  33. synchronized(mRequestsList){
  34. mRequestsList.add(rr);
  35. mRequestMessagesWaiting++;
  36. }
  37. if(mRequestMessagesPending>0)
  38. mRequestMessagesPending--;
  39. alreadySubtracted=true;
  40. byte[]data;
  41. data=rr.mp.marshall();
  42. rr.mp.recycle();
  43. rr.mp=null;
  44. if(data.length>RIL_MAX_COMMAND_BYTES){
  45. thrownewRuntimeException("Parcellargerthanmaxbytesallowed!"+data.length);
  46. }
  47. //parcellengthinbigendian
  48. dataLength[0]=dataLength[1]=0;
  49. dataLength[2]=(byte)((data.length>>8)&0xff);
  50. dataLength[3]=(byte)((data.length)&0xff);
  51. //Log.v(LOG_TAG,"writingpacket:"+data.length+"bytes");
  52. s.getOutputStream().write(dataLength);
  53. s.getOutputStream().write(data);
  54. }catch(IOExceptionex){
  55. Log.e(LOG_TAG,"IOException",ex);
  56. req=findAndRemoveRequestFromList(rr.mSerial);
  57. //makesurethisrequesthasnotalreadybeenhandled,
  58. //eg,ifRILReceiverclearedthelist.
  59. if(req!=null||!alreadySubtracted){
  60. rr.onError(RADIO_NOT_AVAILABLE,null);
  61. rr.release();
  62. }
  63. }catch(RuntimeExceptionexc){
  64. Log.e(LOG_TAG,"Uncaughtexception",exc);
  65. req=findAndRemoveRequestFromList(rr.mSerial);
  66. //makesurethisrequesthasnotalreadybeenhandled,
  67. //eg,ifRILReceiverclearedthelist.
  68. if(req!=null||!alreadySubtracted){
  69. rr.onError(GENERIC_FAILURE,null);
  70. rr.release();
  71. }
  72. }finally{
  73. //Note:Weare"Done"onlyiftherearenooutstanding
  74. //requestsorreplies.Thusthiscodepathwillonlyrelease
  75. //thewakelockonerrors.
  76. releaseWakeLockIfDone();
  77. }
  78. if(!alreadySubtracted&&mRequestMessagesPending>0){
  79. mRequestMessagesPending--;
  80. }
  81. break;
  82. }
  83. }
  84. }
在处理EVENT_SEND消息时,将请求参数写入到rild套接字中,在 Android电话Phone设计框架介绍中介绍了rild服务进程作为Android电话系统的服务端,接收客户端framework层发送过来的请求,并与modem交互,实现整个拨号过程。关于rild服务在 Android之rild进程启动源码分析已经详细介绍了,至此电话拨号请求的发送过程就完成了。拨号的本质就是应用层Phone进程首先对拨打的号码进行一系列处理,然后进入framework层,通过framework层的电话客户端发送线程将请求通过套接字的方式发送给电话服务进程rild,rild将该请求映射为相应的AT指令发送到modem中。


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
  1. voiddisplayCallScreen(){
  2. if(VDBG)Log.d(LOG_TAG,"displayCallScreen()...");
  3. //Onnon-voice-capabledevicesweshouldn'teverbetryingto
  4. //bringuptheInCallScreeninthefirstplace.
  5. if(!sVoiceCapable){
  6. Log.w(LOG_TAG,"displayCallScreen()notallowed:non-voice-capabledevice",newThrowable("stackdump"));
  7. return;
  8. }
  9. //启动电话呼叫界面InCallScreen
  10. try{
  11. startActivity(createInCallIntent());
  12. }catch(ActivityNotFoundExceptione){
  13. Log.w(LOG_TAG,"displayCallScreen:transitiontoInCallScreenfailed:"+e);
  14. }
  15. Profiler.callScreenRequested();
  16. }


[java] view plain copy
  1. staticIntentcreateInCallIntent(){
  2. Intentintent=newIntent(Intent.ACTION_MAIN,null);
  3. intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
  4. |Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
  5. |Intent.FLAG_ACTIVITY_NO_USER_ACTION);
  6. intent.setClassName("com.android.phone",getCallScreenClassName());
  7. returnintent;
  8. }
  9. privatestaticStringgetCallScreenClassName(){
  10. returnInCallScreen.class.getName();
  11. }

第一次启动InCallScreen,首先调用其onCreate函数

src\com\android\phone\InCallScreen.java
[java] view plain copy
  1. protectedvoidonCreate(Bundleicicle){
  2. Log.i(LOG_TAG,"onCreate()...this="+this);
  3. //获得通话界面被创建的时间
  4. Profiler.callScreenOnCreate();
  5. super.onCreate(icicle);
  6. //Makesurethisisavoice-capabledevice.
  7. if(!PhoneGlobals.sVoiceCapable){
  8. Log.wtf(LOG_TAG,"onCreate()reachedonnon-voice-capabledevice");
  9. finish();
  10. return;
  11. }
  12. mApp=PhoneGlobals.getInstance();
  13. mApp.setInCallScreenInstance(this);
  14. //setthisflagsothisactivitywillstayinfrontofthekeyguard
  15. intflags=WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
  16. |WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
  17. if(mApp.getPhoneState()==PhoneConstants.State.OFFHOOK){
  18. flags|=WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
  19. }
  20. WindowManager.LayoutParamslp=getWindow().getAttributes();
  21. lp.flags|=flags;
  22. if(!mApp.proximitySensorModeEnabled()){
  23. //Ifwedon'thaveaproximitysensor,thenthein-callscreenexplicitly
  24. //controlsuseractivity.Thisistopreventspurioustouchesfromwaking
  25. //thedisplay.
  26. lp.inputFeatures|=WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;
  27. }
  28. //设置窗体属性
  29. getWindow().setAttributes(lp);
  30. setPhone(mApp.phone);//SetsmPhone
  31. mCM=mApp.mCM;
  32. log("-onCreate:phonestate="+mCM.getState());
  33. mBluetoothAdapter=BluetoothAdapter.getDefaultAdapter();
  34. if(mBluetoothAdapter!=null){
  35. mBluetoothAdapter.getProfileProxy(getApplicationContext(),mBluetoothProfileServiceListener,
  36. BluetoothProfile.HEADSET);
  37. }
  38. //设置窗体显示风格
  39. requestWindowFeature(Window.FEATURE_NO_TITLE);
  40. //加载布局文件
  41. setContentView(R.layout.incall_screen);
  42. finalViewStubtouchUiStub=(ViewStub)findViewById(
  43. mPhone.getPhoneType()==PhoneConstants.PHONE_TYPE_CDMA
  44. ?R.id.inCallTouchUiCdmaStub:R.id.inCallTouchUiStub);
  45. if(touchUiStub!=null)touchUiStub.inflate();
  46. //加载各种view组建
  47. initInCallScreen();
  48. //对通话的各种状态进行广播。
  49. registerForPhoneStates();
  50. //判断是否使用了OTA技术,通过该判断设置通话界面的样式。
  51. if(icicle==null){
  52. if(DBG)log("onCreate():thisisourveryfirstlaunch,checkingintent...");
  53. internalResolveIntent(getIntent());
  54. }
  55. //记录通话界面创建完成后的时间
  56. Profiler.callScreenCreated();
  57. if(DBG)log("onCreate():exit");
  58. }
UI初始化过程,为了能更好的理解UI初始化过程,请查看 Android电话Phone UI分析一文了解电话呼叫界面的UI布局。 [java] view plain copy
  1. privatevoidinitInCallScreen(){
  2. if(VDBG)log("initInCallScreen()...");
  3. //HavetheWindowManagerfilterouttoucheventsthatare"toofat".
  4. getWindow().addFlags(WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES);
  5. //InitializetheCallCard.
  6. mCallCard=(CallCard)findViewById(R.id.callCard);
  7. if(VDBG)log("-mCallCard="+mCallCard);
  8. mCallCard.setInCallScreenInstance(this);
  9. //InitializetheonscreenUIelements.
  10. initInCallTouchUi();
  11. //Helperclasstokeeptrackofenabledness/stateofUIcontrols
  12. mInCallControlState=newInCallControlState(this,mCM);
  13. //Helperclasstorunthe"Manageconference"UI
  14. mManageConferenceUtils=newManageConferenceUtils(this,mCM);
  15. //TheDTMFDialpad.
  16. ViewStubstub=(ViewStub)findViewById(R.id.dtmf_twelve_key_dialer_stub);
  17. mDialer=newDTMFTwelveKeyDialer(this,stub);
  18. mPowerManager=(PowerManager)getSystemService(Context.POWER_SERVICE);
  19. }

[java] view plain copy
  1. privatevoidinternalResolveIntent(Intentintent){
  2. if(intent==null||intent.getAction()==null){
  3. return;
  4. }
  5. Stringaction=intent.getAction();
  6. if(DBG)log("internalResolveIntent:action="+action);
  7. if(action.equals(intent.ACTION_MAIN)){
  8. if(intent.hasExtra(SHOW_DIALPAD_EXTRA)){
  9. booleanshowDialpad=intent.getBooleanExtra(SHOW_DIALPAD_EXTRA,false);
  10. if(VDBG)log("-internalResolveIntent:SHOW_DIALPAD_EXTRA:"+showDialpad);
  11. mApp.inCallUiState.showDialpad=showDialpad;
  12. finalbooleanhasActiveCall=mCM.hasActiveFgCall();
  13. finalbooleanhasHoldingCall=mCM.hasActiveBgCall();
  14. if(showDialpad&&!hasActiveCall&&hasHoldingCall){
  15. PhoneUtils.switchHoldingAndActive(mCM.getFirstActiveBgCall());
  16. }
  17. }
  18. return;
  19. }
  20. if(action.equals(OtaUtils.ACTION_DISPLAY_ACTIVATION_SCREEN)){
  21. if(!TelephonyCapabilities.supportsOtasp(mPhone)){
  22. thrownewIllegalStateException(
  23. "ReceivedACTION_DISPLAY_ACTIVATION_SCREENintentonnon-OTASP-capabledevice:"
  24. +intent);
  25. }
  26. setInCallScreenMode(InCallScreenMode.OTA_NORMAL);
  27. if((mApp.cdmaOtaProvisionData!=null)
  28. &&(!mApp.cdmaOtaProvisionData.isOtaCallIntentProcessed)){
  29. mApp.cdmaOtaProvisionData.isOtaCallIntentProcessed=true;
  30. mApp.cdmaOtaScreenState.otaScreenState=
  31. CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION;
  32. }
  33. return;
  34. }
  35. if(action.equals(OtaUtils.ACTION_PERFORM_CDMA_PROVISIONING)){
  36. thrownewIllegalStateException(
  37. "UnexpectedACTION_PERFORM_CDMA_PROVISIONINGreceivedbyInCallScreen:"+intent);
  38. }elseif(action.equals(Intent.ACTION_CALL)||action.equals(Intent.ACTION_CALL_EMERGENCY)){
  39. //ACTION_CALL*intentsgototheOutgoingCallBroadcaster,whichnow
  40. //translatesthemintoCallController.placeCall()callsratherthan
  41. //launchingtheInCallScreendirectly.
  42. thrownewIllegalStateException("UnexpectedCALLactionreceivedbyInCallScreen:"+intent);
  43. }elseif(action.equals(ACTION_UNDEFINED)){
  44. //Thisactionisonlyusedforinternalbookkeeping;weshould
  45. //neveractuallygetlaunchedwithit.
  46. Log.wtf(LOG_TAG,"internalResolveIntent:gotlaunchedwithACTION_UNDEFINED");
  47. return;
  48. }else{
  49. Log.wtf(LOG_TAG,"internalResolveIntent:unexpectedintentaction:"+action);
  50. //Butcontinuethebestwecan(basicallytreatingthiscase
  51. //likeACTION_MAIN...)
  52. return;
  53. }
  54. }

更多相关文章

  1. android studio编译android M时无法使用org.apache.http.**的解
  2. Android如何实现模态对话框(Modal Dialog)
  3. 向Android模拟器发短信打电话
  4. android中View.measure方法详解
  5. Android(安卓)build/envsetup.sh 脚本分析(lunch函数)
  6. 刚学会百度地图最新版的sdk,总结一个简单的demo
  7. Android复制手机号码到剪切板并调起打电话功能
  8. android wifi子系统
  9. Android(安卓)实现自动接听和挂断电话功能

随机推荐

  1. Android 无线启动过程分析 无线启动过程
  2. android recycleView局部刷新的选择
  3. Android学习——TextView 设置中划线 下
  4. Best Android Remote Desktop Apps?
  5. Android中的文件存储数据方式 .
  6. android 开机动画(boot animation)的制作
  7. Android基础入门教程——7.1.3 Android(
  8. Android NDK之二:创建NativeActivity
  9. Android指纹解锁边界性问题
  10. android TextView和EditText中显示图片