前面分析了电话拨号界面及电话呼叫界面,由于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
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
- ...
- // Check whether we should show the onscreen "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's VISIBLE by default
- mDialButton = null;
- }
- ...
拨号按钮的单击事件响应:
- public void onClick(View view) {
- switch (view.getId()) {
- case R.id.dialButton: {
- mHaptic.vibrate();
- dialButtonPressed();
- return;
- }
- ...
- }
- }
调用dialButtonPressed()函数发起电话呼叫
- public void dialButtonPressed() {
- if(mDigits == null){
- Log.e(TAG,"dialButtonPressed,mDigits == null");
- return;
- }
-
- if (isDigitsEmpty()) {
- handleDialButtonClickWithEmptyDigits();
- } else {
- final String number = mDigits.getText().toString();
-
-
- if (number != null&& !TextUtils.isEmpty(mProhibitedPhoneNumberRegexp)
- && number.matches(mProhibitedPhoneNumberRegexp)
- && (SystemProperties.getInt("persist.radio.otaspdial", 0) != 1)) {
- Log.i(TAG, "The phone number is prohibited explicitly by a rule.");
- if (getActivity() != null) {
- DialogFragment dialogFragment = ErrorDialogFragment.newInstance(
- R.string.dialog_phone_call_prohibited_title);
- dialogFragment.show(getFragmentManager(), "phone_prohibited_dialog");
- }
-
- mDigits.getText().clear();
- } else if(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 {
-
- final Intent intent = ContactsUtils.getCallIntent(number,(getActivity() instanceof DialtactsActivity ?
- ((DialtactsActivity)getActivity()).getCallOrigin() : null));
- startActivity(intent);
- mClearDigitsOnStop = true;
- mDigits.getText().clear();
- if(mFlagIntentNumber){
- getActivity().finish();
- }
- }
- }
- }
函数首先对输入的号码进行检查,如果没有输入号码,直接按下拨号按钮,则调用handleDialButtonClickWithEmptyDigits函数来处理
- private void handleDialButtonClickWithEmptyDigits() {
- if (phoneIsCdma() && phoneIsOffhook()) {
-
-
-
- startActivity(newFlashIntent());
- } else {
- if (mDigits != null && !TextUtils.isEmpty(mLastNumberDialed)) {
-
- mDigits.setText(mLastNumberDialed);
-
-
-
-
-
-
-
-
-
- mDigits.setSelection(mDigits.getText().length());
- } else {
-
-
-
-
-
-
- playTone(ToneGenerator.TONE_PROP_NACK);
- }
- }
- }
如果号码输入正确合法,则使用ContactsUtils工具类来创建一个Intent。
DialtactsActivity.java
- public String getCallOrigin() {
- return !isDialIntent(getIntent()) ? CALL_ORIGIN_DIALTACTS : null;
- }
-
-
- private boolean isDialIntent(Intent intent) {
- final String action = intent.getAction();
- if (Intent.ACTION_DIAL.equals(action) || ACTION_TOUCH_DIALER.equals(action)) {
- return true;
- }
- if (Intent.ACTION_VIEW.equals(action)) {
- final Uri data = intent.getData();
- if (data != null && Constants.SCHEME_TEL.equals(data.getScheme())) {
- return true;
- }
- }
- return false;
- }
从Launcher点击拨号图标进入的,因此isDialIntent返回true,getCallOrigin返回null
ContactsUtils.java
- public static Intent getCallIntent(String number, String callOrigin) {
- return getCallIntent(getCallUri(number), callOrigin);
- }
-
- public static Intent getCallIntent(Uri uri, String callOrigin) {
- final Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED, uri);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- if (callOrigin != null) {
- intent.putExtra(DialtactsActivity.EXTRA_CALL_ORIGIN, callOrigin);
- }
- return intent;
- }
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
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- setContentView(R.layout.outgoing_call_broadcaster);
- mWaitingSpinner = (ProgressBar) findViewById(R.id.spinner);
-
- Intent intent = getIntent();
- if (DBG) {
- final Configuration configuration = getResources().getConfiguration();
- Log.v(TAG, "onCreate: this = " + this + ", icicle = " + icicle);
- Log.v(TAG, " - getIntent() = " + intent);
- Log.v(TAG, " - configuration = " + configuration);
- }
-
- if (icicle != null) {
-
-
-
-
-
-
-
-
-
- Log.i(TAG, "onCreate: non-null icicle! "
- + "Bailing out, not sending NEW_OUTGOING_CALL broadcast...");
- return;
- }
-
- processIntent(intent);
- if (DBG) Log.v(TAG, "At the end of onCreate(). 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的语音信箱处理同电话处理流程类似
- private void processIntent(Intent intent) {
- if (DBG) {
- Log.v(TAG, "processIntent() = " + intent + ", thread: " + Thread.currentThread());
- }
- final Configuration configuration = getResources().getConfiguration();
-
-
- if (!PhoneGlobals.sVoiceCapable) {
- Log.i(TAG, "This device is detected as non-voice-capable device.");
- handleNonVoiceCapable(intent);
- return;
- }
-
- String action = intent.getAction();
-
- String number = PhoneNumberUtils.getNumberFromIntent(intent, this);
-
- if (number != null) {
- if (!PhoneNumberUtils.isUriNumber(number)) {
-
- number = PhoneNumberUtils.convertKeypadLettersToDigits(number);
- number = PhoneNumberUtils.stripSeparators(number);
- }
- } else {
- Log.w(TAG, "The number obtained from Intent is null.");
- }
-
- boolean callNow;
-
- if (getClass().getName().equals(intent.getComponent().getClassName())) {
-
-
-
- if (!Intent.ACTION_CALL.equals(intent.getAction())) {
- Log.w(TAG, "Attempt to deliver non-CALL action; forcing to CALL");
- intent.setAction(Intent.ACTION_CALL);
- }
- }
-
-
-
-
-
-
- final boolean isExactEmergencyNumber =(number != null) && PhoneNumberUtils.isLocalEmergencyNumber(number, this);
- final boolean isPotentialEmergencyNumber = (number != null) && PhoneNumberUtils.isPotentialLocalEmergencyNumber(number, this);
- if (VDBG) {
- Log.v(TAG, " - Checking restrictions for number '" + 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_PRIVILEGED is used while the number is a potential"
- + " emergency number. Use ACTION_CALL_EMERGENCY as an action instead.");
- action = Intent.ACTION_CALL_EMERGENCY;
- } else {
- action = Intent.ACTION_CALL;
- }
- if (DBG) Log.v(TAG, " - updating action from CALL_PRIVILEGED to " + action);
- intent.setAction(action);
- }
-
- if (Intent.ACTION_CALL.equals(action)) {
- if (isPotentialEmergencyNumber) {
- Log.w(TAG, "Cannot call potential emergency number '" + number
- + "' with CALL Intent " + intent + ".");
- Log.i(TAG, "Launching default dialer instead...");
-
- Intent invokeFrameworkDialer = new Intent();
- invokeFrameworkDialer.setClassName("com.android.contacts","com.android.contacts.DialtactsActivity");
- invokeFrameworkDialer.setAction(Intent.ACTION_DIAL);
- invokeFrameworkDialer.setData(intent.getData());
- if (DBG) Log.v(TAG, "onCreate(): calling startActivity for Dialer: " + invokeFrameworkDialer);
- startActivity(invokeFrameworkDialer);
- finish();
- return;
- }
- callNow = false;
-
- } else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) {
- if (!isPotentialEmergencyNumber) {
- Log.w(TAG, "Cannot call non-potential-emergency number " + number
- + " with EMERGENCY_CALL Intent " + intent + "."
- + " Finish the Activity immediately.");
- finish();
- return;
- }
- callNow = true;
- } else {
- Log.e(TAG, "Unhandled Intent " + intent + ". Finish the Activity immediately.");
- finish();
- return;
- }
-
- PhoneGlobals.getInstance().wakeUpScreen();
-
-
-
- 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: null or empty number, setting callNow=true...");
- callNow = true;
- }
- }
-
- if (callNow) {
- Log.i(TAG, "onCreate(): callNow case! Calling placeCall(): " + intent);
-
-
- PhoneGlobals.getInstance().callController.placeCall(intent);
- }
-
-
- final String callOrigin = intent.getStringExtra(PhoneGlobals.EXTRA_CALL_ORIGIN);
- if (callOrigin != null) {
- if (DBG) Log.v(TAG, " - Call origin is passed (" + callOrigin + ")");
- PhoneGlobals.getInstance().setLatestActiveCallOrigin(callOrigin);
- } else {
- if (DBG) Log.v(TAG, " - Call origin is not passed. Reset current one.");
- PhoneGlobals.getInstance().resetLatestActiveCallOrigin();
- }
-
-
-
-
-
-
-
-
-
-
- Uri uri = intent.getData();
- String scheme = uri.getScheme();
- if (Constants.SCHEME_SIP.equals(scheme) || PhoneNumberUtils.isUriNumber(number)) {
- Log.i(TAG, "The requested number was detected as SIP call.");
- startSipCallOptionHandler(this, intent, uri, number);
- finish();
- return;
- }
-
- Intent broadcastIntent = new Intent(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());
-
-
- broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- if (DBG) Log.v(TAG, " - Broadcasting intent: " + broadcastIntent + ".");
-
- mHandler.sendEmptyMessageDelayed(EVENT_OUTGOING_CALL_TIMEOUT,
- OUTGOING_CALL_TIMEOUT_THRESHOLD);
-
- sendOrderedBroadcastAsUser(broadcastIntent, UserHandle.OWNER,
- PERMISSION, new OutgoingCallReceiver(),
- null,
- Activity.RESULT_OK,
- number,
- null);
- }
首先获取Intent对象,获取拨出的号码。接着判断号码是否为紧急号码,如果是紧急号码,将callNow变量赋值为true,启动InCallScreen,并发送Intent.ACTION_NEW_OUTGOING_CALL广播。
- public void onReceive(Context context, Intent intent) {
- mHandler.removeMessages(EVENT_OUTGOING_CALL_TIMEOUT);
- doReceive(context, intent);
- if (DBG) Log.v(TAG, "OutgoingCallReceiver is going to finish the Activity itself.");
- finish();
- }
直接调用函数doReceive函数来处理ntent.ACTION_NEW_OUTGOING_CALL广播
- public void doReceive(Context context, Intent intent) {
- if (DBG) Log.v(TAG, "doReceive: " + intent);
- boolean alreadyCalled;
- String number;
- String originalUri;
- alreadyCalled = intent.getBooleanExtra(OutgoingCallBroadcaster.EXTRA_ALREADY_CALLED, false);
- if (alreadyCalled) {
- if (DBG) Log.v(TAG, "CALL already placed -- returning.");
- return;
- }
- number = getResultData();
- if (VDBG) Log.v(TAG, "- got number from resultData: '" + number + "'");
-
- final PhoneGlobals app = PhoneGlobals.getInstance();
-
- if (TelephonyCapabilities.supportsOtasp(app.phone)) {
- boolean activateState = (app.cdmaOtaScreenState.otaScreenState
- == OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION);
- boolean dialogState = (app.cdmaOtaScreenState.otaScreenState
- == OtaUtils.CdmaOtaScreenState.OtaScreenState
- .OTA_STATUS_SUCCESS_FAILURE_DLG);
- boolean isOtaCallActive = 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) {
-
-
-
-
-
- if (dialogState) app.dismissOtaDialogs();
- app.clearOtaState();
- app.clearInCallScreenMode();
- } else if (isOtaCallActive) {
-
-
- Log.w(TAG, "OTASP call is active: disallowing a new outgoing call.");
- return;
- }
- }
- if (number == null) {
- if (DBG) Log.v(TAG, "CALL cancelled (null number), returning...");
- return;
- } else if (TelephonyCapabilities.supportsOtasp(app.phone)
- && (app.phone.getState() != PhoneConstants.State.IDLE)
- && (app.phone.isOtaSpNumber(number))) {
- if (DBG) Log.v(TAG, "Call is active, a 2nd OTA call cancelled -- returning.");
- return;
- } else if (PhoneNumberUtils.isPotentialLocalEmergencyNumber(number, context)) {
- Log.w(TAG, "Cannot modify outgoing call to emergency number " + number + ".");
- return;
- }
- originalUri = intent.getStringExtra(OutgoingCallBroadcaster.EXTRA_ORIGINAL_URI);
- if (originalUri == null) {
- Log.e(TAG, "Intent is missing EXTRA_ORIGINAL_URI -- returning.");
- return;
- }
- Uri uri = Uri.parse(originalUri);
- number = PhoneNumberUtils.convertKeypadLettersToDigits(number);
- number = PhoneNumberUtils.stripSeparators(number);
-
- if (DBG) Log.v(TAG, "doReceive: proceeding with call...");
- if (VDBG) Log.v(TAG, "- uri: " + uri);
- if (VDBG) Log.v(TAG, "- actual number to dial: '" + 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技术的应用,使得移动通信不仅可以提供语音和数据服务,而且还能提供新业务下载。
- private void startSipCallOptionHandler(Context context, Intent intent,
- Uri uri, String number) {
- if (VDBG) {
- Log.i(TAG, "startSipCallOptionHandler...");
- Log.i(TAG, "- intent: " + intent);
- Log.i(TAG, "- uri: " + uri);
- Log.i(TAG, "- number: " + number);
- }
-
- Intent newIntent = new Intent(Intent.ACTION_CALL, uri);
- newIntent.putExtra(EXTRA_ACTUAL_NUMBER_TO_DIAL, number);
- PhoneUtils.checkAndCopyPhoneProviderExtras(intent, newIntent);
-
- Intent selectPhoneIntent = new Intent(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(): " +
- "calling startActivity: " + selectPhoneIntent);
- }
-
- context.startActivity(selectPhoneIntent);
- }
电话类型选择处理:1.读取用户设置;2.弹出对话框让用户选择
src\com\android\phone\SipCallOptionHandler.java
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- Intent intent = getIntent();
- String action = intent.getAction();
- if (!OutgoingCallBroadcaster.ACTION_SIP_SELECT_PHONE.equals(action)) {
- Log.wtf(TAG, "onCreate: got intent action '" + action + "', expected "
- + OutgoingCallBroadcaster.ACTION_SIP_SELECT_PHONE);
- finish();
- return;
- }
-
- mIntent = (Intent) intent.getParcelableExtra(OutgoingCallBroadcaster.EXTRA_NEW_CALL_INTENT);
- if (mIntent == null) {
- finish();
- return;
- }
-
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
-
-
-
-
-
-
- boolean voipSupported = PhoneUtils.isVoipSupported();
- if (DBG) Log.v(TAG, "voipSupported: " + voipSupported);
- mSipProfileDb = new SipProfileDb(this);
- mSipSharedPreferences = new SipSharedPreferences(this);
- mCallOption = mSipSharedPreferences.getSipCallOption();
- if (DBG) Log.v(TAG, "Call option: " + mCallOption);
- Uri uri = mIntent.getData();
- String scheme = uri.getScheme();
- mNumber = PhoneNumberUtils.getNumberFromIntent(mIntent, this);
- boolean isInCellNetwork = PhoneGlobals.getInstance().phoneMgr.isRadioOn();
- boolean isKnownCallScheme = Constants.SCHEME_TEL.equals(scheme)
- || Constants.SCHEME_SIP.equals(scheme);
- boolean isRegularCall = Constants.SCHEME_TEL.equals(scheme)
- && !PhoneNumberUtils.isUriNumber(mNumber);
-
- if (!isKnownCallScheme) {
- setResultAndFinish();
- return;
- }
-
- 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) {
-
-
- if ((mSipProfileDb.getProfilesCount() > 0) || !isRegularCall) {
- startGetPrimarySipPhoneThread();
- return;
- } else {
- mUseSipPhone = false;
- }
- }
- setResultAndFinish();
选择SIP拨号还是PSTN拨号
- private void setResultAndFinish() {
- runOnUiThread(new Runnable() {
- public void run() {
- if (mOutgoingSipProfile != null) {
- if (!isNetworkConnected()) {
- showDialog(DIALOG_NO_INTERNET_ERROR);
- return;
- }
- if (DBG) Log.v(TAG, "primary SIP URI is " +
- mOutgoingSipProfile.getUriString());
- createSipPhoneIfNeeded(mOutgoingSipProfile);
- mIntent.putExtra(OutgoingCallBroadcaster.EXTRA_SIP_PHONE_URI,
- mOutgoingSipProfile.getUriString());
- if (mMakePrimary) {
- mSipSharedPreferences.setPrimaryAccount(
- mOutgoingSipProfile.getUriString());
- }
- }
-
- if (mUseSipPhone && mOutgoingSipProfile == null) {
- showDialog(DIALOG_START_SIP_SETTINGS);
- return;
- } else {
-
- PhoneGlobals.getInstance().callController.placeCall(mIntent);
- }
- finish();
- }
- });
- }
src\com\android\phone\CallController.java
- public void placeCall(Intent intent) {
- log("placeCall()... intent = " + intent);
- if (VDBG) log("extras = " + intent.getExtras());
- final InCallUiState inCallUiState = mApp.inCallUiState;
-
- if (intent == null) {
- Log.wtf(TAG, "placeCall: called with null intent");
- throw new IllegalArgumentException("placeCall: called with null intent");
- }
-
- String action = intent.getAction();
- Uri uri = intent.getData();
- if (uri == null) {
- Log.wtf(TAG, "placeCall: intent had no data");
- throw new IllegalArgumentException("placeCall: intent had no data");
- }
-
- String scheme = uri.getScheme();
- String number = 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: unexpected intent action " + action);
- throw new IllegalArgumentException("Unexpected action: " + action);
- }
-
-
-
- Phone phone = mApp.mCM.getDefaultPhone();
- if (TelephonyCapabilities.supportsOtasp(phone)) {
- checkForOtaspCall(intent);
- }
- mApp.setRestoreMuteOnInCallResume(false);
-
-
-
- if (PhoneUtils.hasPhoneProviderExtras(intent)) {
- inCallUiState.setProviderInfo(intent);
- } else {
- inCallUiState.clearProviderInfo();
- }
-
- CallStatusCode status = placeCallInternal(intent);
-
- switch (status) {
- case SUCCESS:
- case EXITED_ECM:
- if (DBG) log("==> placeCall(): success from placeCallInternal(): " + status);
- if (status == CallStatusCode.EXITED_ECM) {
-
-
- inCallUiState.setPendingCallStatusCode(CallStatusCode.EXITED_ECM);
- } else {
-
-
-
- inCallUiState.clearPendingCallStatusCode();
- }
-
-
-
- mApp.setBeginningCall(true);
- break;
-
- default:
-
- log("==> placeCall(): failure code from placeCallInternal(): " + status);
-
-
-
-
- handleOutgoingCallError(status);
- break;
- }
- mApp.displayCallScreen();
- }
该函数首先得到拨打的电话号码及默认的Phone对象,调用placeCallInternal发起拨号请求,同时启动电话呼叫界面InCallScreen。
1.拨号流程
- private CallStatusCode placeCallInternal(Intent intent) {
- final InCallUiState inCallUiState = mApp.inCallUiState;
- final Uri uri = intent.getData();
- final String scheme = (uri != null) ? uri.getScheme() : null;
- String number;
- Phone phone = null;
-
- CallStatusCode okToCallStatus = checkIfOkToInitiateOutgoingCall(
- mCM.getServiceState());
- try {
- number = PhoneUtils.getInitialNumber(intent);
- if (VDBG) log("- actual number to dial: '" + number + "'");
- String sipPhoneUri = intent.getStringExtra(OutgoingCallBroadcaster.EXTRA_SIP_PHONE_URI);
- phone = PhoneUtils.pickPhoneBasedOnNumber(mCM, scheme, number, sipPhoneUri);
- if (VDBG) log("- got Phone instance: " + phone + ", class = " + phone.getClass());
- okToCallStatus = checkIfOkToInitiateOutgoingCall(phone.getServiceState().getState());
- } catch (PhoneUtils.VoiceMailNumberMissingException ex) {
- if (okToCallStatus != CallStatusCode.SUCCESS) {
- if (DBG) log("Voicemail number not reachable in current SIM card state.");
- return okToCallStatus;
- }
- if (DBG) log("VoiceMailNumberMissingException from getInitialNumber()");
- return CallStatusCode.VOICEMAIL_NUMBER_MISSING;
- }
-
- if (number == null) {
- Log.w(TAG, "placeCall: couldn't get a phone number from Intent " + intent);
- return CallStatusCode.NO_PHONE_NUMBER_SUPPLIED;
- }
- boolean isEmergencyNumber = PhoneNumberUtils.isLocalEmergencyNumber(number, mApp);
- boolean isPotentialEmergencyNumber =PhoneNumberUtils.isPotentialLocalEmergencyNumber(number, mApp);
- boolean isEmergencyIntent = Intent.ACTION_CALL_EMERGENCY.equals(intent.getAction());
-
- if (isPotentialEmergencyNumber && !isEmergencyIntent) {
- Log.e(TAG, "Non-CALL_EMERGENCY Intent " + intent+ " attempted to call potential emergency number " + number + ".");
- return CallStatusCode.CALL_FAILED;
- } else if (!isPotentialEmergencyNumber && isEmergencyIntent) {
- Log.e(TAG, "Received CALL_EMERGENCY Intent " + intent
- + " with non-potential-emergency number " + number
- + " -- failing call.");
- return CallStatusCode.CALL_FAILED;
- }
-
-
-
-
-
-
-
- if (isEmergencyNumber
- && ((okToCallStatus == CallStatusCode.EMERGENCY_ONLY)
- || (okToCallStatus == CallStatusCode.OUT_OF_SERVICE))) {
- if (DBG) log("placeCall: Emergency number detected with status = " + okToCallStatus);
- okToCallStatus = CallStatusCode.SUCCESS;
- if (DBG) log("==> UPDATING status to: " + okToCallStatus);
- }
-
- if (okToCallStatus != CallStatusCode.SUCCESS) {
-
-
- if (isEmergencyNumber && (okToCallStatus == CallStatusCode.POWER_OFF)) {
- Log.i(TAG, "placeCall: Trying to make emergency call while POWER_OFF!");
-
- synchronized (this) {
- if (mEmergencyCallHelper == null) {
- mEmergencyCallHelper = new EmergencyCallHelper(this);
- }
- }
-
- mEmergencyCallHelper.startEmergencyCallFromAirplaneModeSequence(number);
- return CallStatusCode.SUCCESS;
- } else {
- if (DBG) log("==> placeCallInternal(): non-success status: " + okToCallStatus);
- return okToCallStatus;
- }
- }
-
- inCallUiState.needToShowCallLostDialog = false;
- inCallUiState.clearProgressIndication();
- Uri contactUri = intent.getData();
-
- int callStatus = PhoneUtils.placeCall(mApp,
- phone,
- number,
- contactUri,
- (isEmergencyNumber || isEmergencyIntent),
- inCallUiState.providerGatewayUri);
-
- switch (callStatus) {
- case PhoneUtils.CALL_STATUS_DIALED:
- if (VDBG) log("placeCall: PhoneUtils.placeCall() succeeded for regular call '"
- + number + "'.");
- if (VDBG) log ("- inCallUiState.inCallScreenMode = "
- + inCallUiState.inCallScreenMode);
- if (inCallUiState.inCallScreenMode == InCallScreenMode.OTA_NORMAL) {
- if (VDBG) log ("==> OTA_NORMAL note: switching to OTA_STATUS_LISTENING.");
- mApp.cdmaOtaScreenState.otaScreenState =
- CdmaOtaScreenState.OtaScreenState.OTA_STATUS_LISTENING;
- }
-
- boolean voicemailUriSpecified = scheme != null && scheme.equals("voicemail");
-
-
-
-
-
- inCallUiState.showDialpad = voicemailUriSpecified;
-
-
-
-
- inCallUiState.dialpadContextText = voicemailUriSpecified ?
- phone.getVoiceMailAlphaTag() : "";
-
-
-
-
-
-
-
-
-
-
-
- inCallUiState.dialpadDigits = null;
-
-
-
-
-
-
- boolean exitedEcm = false;
- if (PhoneUtils.isPhoneInEcm(phone) && !isEmergencyNumber) {
- Log.i(TAG, "About to exit ECM because of an outgoing non-emergency call");
- exitedEcm = true;
- }
-
- if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
-
- if (mApp.cdmaPhoneCallState.getCurrentCallState()
- == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE) {
-
- PhoneUtils.setMute(false);
-
-
-
-
-
-
-
-
-
-
-
- mApp.cdmaPhoneCallState.setThreeWayCallOrigState(true);
-
-
- sendEmptyMessageDelayed(THREEWAY_CALLERINFO_DISPLAY_DONE,
- THREEWAY_CALLERINFO_DISPLAY_TIME);
- }
- }
-
-
- if (exitedEcm) {
- return CallStatusCode.EXITED_ECM;
- } else {
- return CallStatusCode.SUCCESS;
- }
-
- case PhoneUtils.CALL_STATUS_DIALED_MMI:
- if (DBG) log("placeCall: specified number was an MMI code: '" + number + "'.");
-
-
-
-
-
-
-
-
-
- return CallStatusCode.DIALED_MMI;
-
- case PhoneUtils.CALL_STATUS_FAILED:
- Log.w(TAG, "placeCall: PhoneUtils.placeCall() FAILED for number '"
- + number + "'.");
-
-
- return CallStatusCode.CALL_FAILED;
-
- default:
- Log.wtf(TAG, "placeCall: unknown callStatus " + callStatus
- + " from PhoneUtils.placeCall() for number '" + number + "'.");
- return CallStatusCode.SUCCESS;
- }
- }
该函数通过调用PhoneUtils类的placeCall函数进入Framework层异步完成电话呼叫
src\com\android\phone\PhoneUtils.java
- public static int placeCall(Context context, Phone phone,
- String number, Uri contactRef, boolean isEmergencyCall,
- Uri gatewayUri) {
- final PhoneGlobals app = PhoneGlobals.getInstance();
- boolean useGateway = false;
- if (null != gatewayUri &&
- !isEmergencyCall &&
- PhoneUtils.isRoutableViaGateway(number)) {
- useGateway = true;
- }
-
- int status = CALL_STATUS_DIALED;
- Connection connection;
- String numberToDial;
- if (useGateway) {
- if (null == gatewayUri || !Constants.SCHEME_TEL.equals(gatewayUri.getScheme())) {
- Log.e(LOG_TAG, "Unsupported URL:" + gatewayUri);
- return CALL_STATUS_FAILED;
- }
-
-
-
-
-
- numberToDial = gatewayUri.getSchemeSpecificPart();
- } else {
- numberToDial = number;
- }
-
-
-
- final boolean initiallyIdle = app.mCM.getState() == PhoneConstants.State.IDLE;
-
- try {
- connection = app.mCM.dial(phone, numberToDial);
- } catch (CallStateException ex) {
-
-
-
- Log.w(LOG_TAG, "Exception from app.mCM.dial()", ex);
- return CALL_STATUS_FAILED;
-
-
-
-
- }
-
- int phoneType = phone.getPhoneType();
-
-
- if (null == connection) {
- if (phoneType == PhoneConstants.PHONE_TYPE_GSM && gatewayUri == null) {
- if (DBG) log("dialed MMI code: " + number);
- status = CALL_STATUS_DIALED_MMI;
- } else {
- status = CALL_STATUS_FAILED;
- }
- } else {
- if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
- updateCdmaCallStateOnNewOutgoingCall(app);
- }
-
-
- 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) {
-
-
-
-
-
- String content = context.getContentResolver().SCHEME_CONTENT;
- if ((contactRef != null) && (contactRef.getScheme().equals(content))) {
- Object userDataObject = connection.getUserData();
- if (userDataObject == null) {
- connection.setUserData(contactRef);
- } else {
-
-
-
- if (userDataObject instanceof CallerInfo) {
- ((CallerInfo) userDataObject).contactRefUri = contactRef;
- } else {
- ((CallerInfoToken) userDataObject).currentInfo.contactRefUri =
- contactRef;
- }
- }
- }
- } else {
-
-
-
- CallerInfo info = null;
- String content = phone.getContext().getContentResolver().SCHEME_CONTENT;
- if ((contactRef != null) && (contactRef.getScheme().equals(content))) {
- info = CallerInfo.getCallerInfo(context, contactRef);
- }
-
-
-
-
- if (null == info) {
- info = CallerInfo.getCallerInfo(context, number);
- }
- info.phoneNumber = number;
- connection.setUserData(info);
- }
- setAudioMode();
-
- if (DBG) log("about to activate speaker");
-
- final boolean speakerActivated = activateSpeakerIfDocked(phone);
-
-
- if (initiallyIdle && !speakerActivated && isSpeakerOn(app)
- && !app.isBluetoothHeadsetAudioOn()) {
-
- Log.i(LOG_TAG, "Forcing speaker off when initiating a new outgoing call...");
- PhoneUtils.turnOnSpeaker(app, false, true);
- }
- }
- return status;
- }
该函数调用framework层的CallManager的dial函数。
- public Connection dial(Phone phone, String dialString) throws CallStateException {
- Phone basePhone = getPhoneBase(phone);
- Connection result;
- if (VDBG) {
- Log.d(LOG_TAG, " dial(" + basePhone + ", "+ dialString + ")");
- Log.d(LOG_TAG, this.toString());
- }
- if (!canDial(phone)) {
- throw new CallStateException("cannot dial in current state");
- }
- if (hasActiveFgCall() ) {
- Phone activePhone = getActiveFgCall().getPhone();
- boolean hasBgCall = !(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, "End dial(" + basePhone + ", "+ dialString + ")");
- Log.d(LOG_TAG, this.toString());
- }
- return result;
- }
函数首先取得相应类型的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
- public Connection dial(String dialString) throws CallStateException {
- return dial(dialString, null);
- }
- public Connection dial (String dialString, UUSInfo uusInfo) throws CallStateException {
-
- String newDialString = PhoneNumberUtils.stripSeparators(dialString);
-
- if (handleInCallMmiCommands(newDialString)) {
- return null;
- }
-
- String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString);
- GsmMmiCode mmi = GsmMmiCode.newFromDialString(networkPortion, this);
- if (mmi == null) {
- return mCT.dial(newDialString, uusInfo);
- } else if (mmi.isTemporaryModeCLIR()) {
- return mCT.dial(mmi.dialingNumber, mmi.getCLIRMode(), uusInfo);
- } else {
- mPendingMMIs.add(mmi);
- mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
- mmi.processCode();
-
- return null;
- }
- }
GSMPhone又通过CallTracker来向RIL发送请求,关于CallTracker的类关系请参阅 Android电话Phone设计框架介绍
./telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java
- synchronized Connection dial (String dialString, int clirMode, UUSInfo uusInfo) throws CallStateException {
-
- clearDisconnected();
-
- if (!canDial()) {
- throw new CallStateException("cannot dial in current state");
- }
-
-
-
-
- if (foregroundCall.getState() == GsmCall.State.ACTIVE) {
-
-
-
-
- switchWaitingOrHoldingAndActive();
-
-
-
-
-
- fakeHoldForegroundBeforeDial();
- }
-
- if (foregroundCall.getState() != GsmCall.State.IDLE) {
-
- throw new CallStateException("cannot dial in current state");
- }
-
-
-
- boolean isStkCall = getStkCall();
- log("GsmCallTracker dial: isStkCall=" + isStkCall);
- pendingMO = new GsmConnection(phone.getContext(), dialString, this, foregroundCall, isStkCall, false);
- hangupPendingMO = false;
-
- if (pendingMO.address == null || pendingMO.address.length() == 0
- || pendingMO.address.indexOf(PhoneNumberUtils.WILD) >= 0
- ) {
-
- pendingMO.cause = Connection.DisconnectCause.INVALID_NUMBER;
-
-
-
- pollCallsWhenSafe();
- } else {
-
- setMute(false);
-
-
-
- String tmpAddr = pendingMO.address;
- if (PhoneNumberUtils.isCustomEmergencyNumber(pendingMO.address)) {
- Log.d(LOG_TAG,"Pending MO is Custom Emergency call");
- tmpAddr = tmpAddr + "/1";
- }
-
- cm.dial(tmpAddr, clirMode, uusInfo, isStkCall, obtainCompleteMessage());
-
- }
-
- updatePhoneState();
- phone.notifyPreciseCallStateChanged();
-
- return pendingMO;
- }
cm的类型为CommandsInterface,RIL.java实现了CommandsInterface接口,因此GsmCallTracker最终是通过RIL来发送拨号请求的。
./telephony/java/com/android/internal/telephony/RIL.java
- public void dial(String address, int clirMode, UUSInfo uusInfo, boolean isStkCall, Message result) {
- RILRequest rr;
-
- 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);
- if (uusInfo == null) {
- rr.mp.writeInt(0);
- } else {
- rr.mp.writeInt(1);
- 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送回到调用者并由其处理。
- protected void send(RILRequest rr) {
- Message msg;
- if (mSocket == null) {
- rr.onError(RADIO_NOT_AVAILABLE, null);
- rr.release();
- return;
- }
- msg = mSender.obtainMessage(EVENT_SEND, rr);
- acquireWakeLock();
- msg.sendToTarget();
- }
RILSender消息处理过程
- class RILSender extends Handler implements Runnable {
- public RILSender(Looper looper) {
- super(looper);
- }
- byte[] dataLength = new byte[4];
- public void
- run() {
-
- }
-
- @Override public void
- handleMessage(Message msg) {
- RILRequest rr = (RILRequest)(msg.obj);
- RILRequest req = null;
- switch (msg.what) {
- case EVENT_SEND:
-
-
-
-
-
- boolean alreadySubtracted = false;
- try {
- LocalSocket s;
- 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) {
- throw new RuntimeException("Parcel larger than max bytes allowed! "+ data.length);
- }
-
- dataLength[0] = dataLength[1] = 0;
- dataLength[2] = (byte)((data.length >> 8) & 0xff);
- dataLength[3] = (byte)((data.length) & 0xff);
-
- s.getOutputStream().write(dataLength);
- s.getOutputStream().write(data);
- } catch (IOException ex) {
- Log.e(LOG_TAG, "IOException", ex);
- req = findAndRemoveRequestFromList(rr.mSerial);
-
-
- if (req != null || !alreadySubtracted) {
- rr.onError(RADIO_NOT_AVAILABLE, null);
- rr.release();
- }
- } catch (RuntimeException exc) {
- Log.e(LOG_TAG, "Uncaught exception ", exc);
- req = findAndRemoveRequestFromList(rr.mSerial);
-
-
- if (req != null || !alreadySubtracted) {
- rr.onError(GENERIC_FAILURE, null);
- rr.release();
- }
- } finally {
-
-
-
- releaseWakeLockIfDone();
- }
- if (!alreadySubtracted && mRequestMessagesPending > 0) {
- mRequestMessagesPending--;
- }
- break;
- }
- }
- }
在处理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
- void displayCallScreen() {
- if (VDBG) Log.d(LOG_TAG, "displayCallScreen()...");
-
-
- if (!sVoiceCapable) {
- Log.w(LOG_TAG, "displayCallScreen() not allowed: non-voice-capable device",new Throwable("stack dump"));
- return;
- }
-
- try {
- startActivity(createInCallIntent());
- } catch (ActivityNotFoundException e) {
- Log.w(LOG_TAG, "displayCallScreen: transition to InCallScreen failed: " + e);
- }
- Profiler.callScreenRequested();
- }
- static Intent createInCallIntent() {
- Intent intent = new Intent(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());
- return intent;
- }
-
- private static String getCallScreenClassName() {
- return InCallScreen.class.getName();
- }
第一次启动InCallScreen,首先调用其onCreate函数
src\com\android\phone\InCallScreen.java
- protected void onCreate(Bundle icicle) {
- Log.i(LOG_TAG, "onCreate()... this = " + this);
-
- Profiler.callScreenOnCreate();
- super.onCreate(icicle);
-
- if (!PhoneGlobals.sVoiceCapable) {
- Log.wtf(LOG_TAG, "onCreate() reached on non-voice-capable device");
- finish();
- return;
- }
- mApp = PhoneGlobals.getInstance();
- mApp.setInCallScreenInstance(this);
-
- int flags = 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.LayoutParams lp = getWindow().getAttributes();
- lp.flags |= flags;
- if (!mApp.proximitySensorModeEnabled()) {
-
-
-
- lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;
- }
-
- getWindow().setAttributes(lp);
- setPhone(mApp.phone);
- mCM = mApp.mCM;
- log("- onCreate: phone state = " + mCM.getState());
- mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
- if (mBluetoothAdapter != null) {
- mBluetoothAdapter.getProfileProxy(getApplicationContext(), mBluetoothProfileServiceListener,
- BluetoothProfile.HEADSET);
- }
-
- requestWindowFeature(Window.FEATURE_NO_TITLE);
-
- setContentView(R.layout.incall_screen);
- final ViewStub touchUiStub = (ViewStub) findViewById(
- mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA
- ? R.id.inCallTouchUiCdmaStub : R.id.inCallTouchUiStub);
- if (touchUiStub != null) touchUiStub.inflate();
-
- initInCallScreen();
-
- registerForPhoneStates();
-
- if (icicle == null) {
- if (DBG) log("onCreate(): this is our very first launch, checking intent...");
- internalResolveIntent(getIntent());
- }
-
- Profiler.callScreenCreated();
- if (DBG) log("onCreate(): exit");
- }
UI初始化过程,为了能更好的理解UI初始化过程,请查看 Android电话Phone UI分析一文了解电话呼叫界面的UI布局。
- private void initInCallScreen() {
- if (VDBG) log("initInCallScreen()...");
-
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES);
-
- mCallCard = (CallCard) findViewById(R.id.callCard);
- if (VDBG) log(" - mCallCard = " + mCallCard);
- mCallCard.setInCallScreenInstance(this);
-
- initInCallTouchUi();
-
- mInCallControlState = new InCallControlState(this, mCM);
-
- mManageConferenceUtils = new ManageConferenceUtils(this, mCM);
-
- ViewStub stub = (ViewStub) findViewById(R.id.dtmf_twelve_key_dialer_stub);
- mDialer = new DTMFTwelveKeyDialer(this, stub);
- mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
- }
- private void internalResolveIntent(Intent intent) {
- if (intent == null || intent.getAction() == null) {
- return;
- }
- String action = intent.getAction();
- if (DBG) log("internalResolveIntent: action=" + action);
- if (action.equals(intent.ACTION_MAIN)) {
- if (intent.hasExtra(SHOW_DIALPAD_EXTRA)) {
- boolean showDialpad = intent.getBooleanExtra(SHOW_DIALPAD_EXTRA, false);
- if (VDBG) log("- internalResolveIntent: SHOW_DIALPAD_EXTRA: " + showDialpad);
- mApp.inCallUiState.showDialpad = showDialpad;
- final boolean hasActiveCall = mCM.hasActiveFgCall();
- final boolean hasHoldingCall = mCM.hasActiveBgCall();
- if (showDialpad && !hasActiveCall && hasHoldingCall) {
- PhoneUtils.switchHoldingAndActive(mCM.getFirstActiveBgCall());
- }
- }
- return;
- }
- if (action.equals(OtaUtils.ACTION_DISPLAY_ACTIVATION_SCREEN)) {
- if (!TelephonyCapabilities.supportsOtasp(mPhone)) {
- throw new IllegalStateException(
- "Received ACTION_DISPLAY_ACTIVATION_SCREEN intent on non-OTASP-capable device: "
- + 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)) {
- throw new IllegalStateException(
- "Unexpected ACTION_PERFORM_CDMA_PROVISIONING received by InCallScreen: "+ intent);
- } else if (action.equals(Intent.ACTION_CALL) || action.equals(Intent.ACTION_CALL_EMERGENCY)) {
-
-
-
- throw new IllegalStateException("Unexpected CALL action received by InCallScreen: "+ intent);
- } else if (action.equals(ACTION_UNDEFINED)) {
-
-
- Log.wtf(LOG_TAG, "internalResolveIntent: got launched with ACTION_UNDEFINED");
- return;
- } else {
- Log.wtf(LOG_TAG, "internalResolveIntent: unexpected intent action: " + action);
-
-
- return;
- }
- }