Android4.0(Phone)拨号启动过程分析(二)

来源:互联网 发布:淘宝卖家怎样申诉 编辑:程序博客网 时间:2024/05/21 18:48
接上:Android4.0(Phone)拨号启动过程分析(一)   

InCallScreen处理来电和拨号的界面,接通电话也是这个界面,接下来分析InCallScreen类是如何处理拨号流程的;

@Overrideprotected void onCreate(Bundle icicle) {Log.i(LOG_TAG, "onCreate()...  this = " + this);Profiler.callScreenOnCreate();super.onCreate(icicle);// Make sure this is a voice-capable device.if (!PhoneApp.sVoiceCapable) {// There should be no way to ever reach the InCallScreen on a// non-voice-capable device, since this activity is not exported by// our manifest, and we explicitly disable any other external APIs// like the CALL intent and ITelephony.showCallScreen().// So the fact that we got here indicates a phone app bug.Log.wtf(LOG_TAG, "onCreate() reached on non-voice-capable device");finish();return;}// 获取PhoneApp实例mApp = PhoneApp.getInstance();// 设置通话界面mApp.setInCallScreenInstance(this);// 添加这个标记可以让Activity显示在锁屏的上方int flags = WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;if (mApp.getPhoneState() == Phone.State.OFFHOOK) {// While we are in call, the in-call screen should dismiss the// keyguard.// This allows the user to press Home to go directly home without// going through// an insecure lock screen.// But we do not want to do this if there is no active call so we do// not// bypass the keyguard if the call is not answered or declined.// 解除锁屏。只有锁屏界面不是加密的才能解锁。如果锁屏界面是加密的,那么用户解锁之后才能看到此窗口,除非设置了FLAG_SHOW_WHEN_LOCKED选项。flags |= WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;}getWindow().addFlags(flags);// Also put the system bar (if present on this device) into// "lights out" mode any time we're the foreground activity.WindowManager.LayoutParams params = getWindow().getAttributes();params.systemUiVisibility = View.SYSTEM_UI_FLAG_LOW_PROFILE;getWindow().setAttributes(params);setPhone(mApp.phone); // Sets mPhonemCM = mApp.mCM;log("- onCreate: phone state = " + mCM.getState());mBluetoothHandsfree = mApp.getBluetoothHandsfree();if (VDBG)log("- mBluetoothHandsfree: " + mBluetoothHandsfree);if (mBluetoothHandsfree != null) {// The PhoneApp only creates a BluetoothHandsfree instance in the// first place if BluetoothAdapter.getDefaultAdapter()// succeeds. So at this point we know the device is BT-capable.mAdapter = BluetoothAdapter.getDefaultAdapter();mAdapter.getProfileProxy(getApplicationContext(),mBluetoothProfileServiceListener, BluetoothProfile.HEADSET);}requestWindowFeature(Window.FEATURE_NO_TITLE);// Inflate everything in incall_screen.xml and add it to the screen.setContentView(R.layout.incall_screen);// 初始化CallCard以及InCallTouchUi等截面initInCallScreen();// 注册关于Phone状态改变的监听事件,这也就是为什么Phone状态改变之后InCallScreen能够收到变化消息的原因,这一点我们在来电流程中也有提及;registerForPhoneStates();// No need to change wake state here; that happens in onResume() when we// are actually displayed.// Handle the Intent we were launched with, but only if this is the// the very first time we're being launched (ie. NOT if we're being// re-initialized after previously being shut down.)// Once we're up and running, any future Intents we need// to handle will come in via the onNewIntent() method.if (icicle == null) {if (DBG)log("onCreate(): this is our very first launch, checking intent...");// 该方法用于处理InCallScreen收到的Intent信息internalResolveIntent(getIntent());}Profiler.callScreenCreated();if (DBG)log("onCreate(): exit");}
只要分析三个函数:initInCallScreen、registerForPhoneStates、internalResolveIntent
private void initInCallScreen() {if (VDBG)log("initInCallScreen()...");// Have the WindowManager filter out touch events that are "too fat".getWindow().addFlags(WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES);mInCallPanel = (ViewGroup) findViewById(R.id.inCallPanel);// Initialize the CallCard.mCallCard = (CallCard) findViewById(R.id.callCard);if (VDBG)log("  - mCallCard = " + mCallCard);mCallCard.setInCallScreenInstance(this);//初始化界面的UI布局initInCallTouchUi();// 助手类跟踪enabledness / UI控件的状态mInCallControlState = new InCallControlState(this, mCM);//助手类运行“Manage conference”的用户界面mManageConferenceUtils = new ManageConferenceUtils(this, mCM);// The DTMF Dialpad.// TODO: Don't inflate this until the first time it's needed.ViewStub stub = (ViewStub) findViewById(R.id.dtmf_twelve_key_dialer_stub);stub.inflate();//DTMF拨号盘初始化  mDialerView = (DTMFTwelveKeyDialerView) findViewById(R.id.dtmf_twelve_key_dialer_view);if (DBG)log("- Found dialerView: " + mDialerView);// Sanity-check that (regardless of the device) at least the// dialer view is present:if (mDialerView == null) {Log.e(LOG_TAG, "onCreate: couldn't find dialerView",new IllegalStateException());}//创建DTMFTwelveKeyDialer实例mDialer = new DTMFTwelveKeyDialer(this, mDialerView);}
以下函数是通过CallManager类向Framework层注册一些状态,只要Framework层的状态改变就会通知上层应用修改UI;如果是来电就会在Handler收到PHONE_INCOMING_RING标记。实际上为观察者模式的运用
private void registerForPhoneStates() {if (!mRegisteredForPhoneStates) {mCM.registerForPreciseCallStateChanged(mHandler,PHONE_STATE_CHANGED, null);mCM.registerForDisconnect(mHandler, PHONE_DISCONNECT, null);mCM.registerForMmiInitiate(mHandler, PhoneApp.MMI_INITIATE, null);// register for the MMI complete message. Upon completion,// PhoneUtils will bring up a system dialog instead of the// message display class in PhoneUtils.displayMMIComplete().// We'll listen for that message too, so that we can finish// the activity at the same time.mCM.registerForMmiComplete(mHandler, PhoneApp.MMI_COMPLETE, null);mCM.registerForCallWaiting(mHandler, PHONE_CDMA_CALL_WAITING, null);mCM.registerForPostDialCharacter(mHandler, POST_ON_DIAL_CHARS, null);mCM.registerForSuppServiceFailed(mHandler, SUPP_SERVICE_FAILED,null);mCM.registerForIncomingRing(mHandler, PHONE_INCOMING_RING, null);mCM.registerForNewRingingConnection(mHandler,PHONE_NEW_RINGING_CONNECTION, null);mRegisteredForPhoneStates = true;}}
internalResolveIntent是恢复Activity保存的状态
private void internalResolveIntent(Intent intent) {if (intent == null || intent.getAction() == null) {return;}String action = intent.getAction();if (DBG)log("internalResolveIntent: action=" + action);// In gingerbread and earlier releases, the InCallScreen used to// directly handle certain intent actions that could initiate phone// calls, namely ACTION_CALL and ACTION_CALL_EMERGENCY, and also// OtaUtils.ACTION_PERFORM_CDMA_PROVISIONING.//// But it doesn't make sense to tie those actions to the InCallScreen// (or especially to the *activity lifecycle* of the InCallScreen).// Instead, the InCallScreen should only be concerned with running the// onscreen UI while in a call. So we've now offloaded the call-control// functionality to a new module called CallController, and OTASP calls// are now launched from the OtaUtils startInteractiveOtasp() or// startNonInteractiveOtasp() methods.//// So now, the InCallScreen is only ever launched using the ACTION_MAIN// action, and (upon launch) performs no functionality other than// displaying the UI in a state that matches the current telephony// state.if (action.equals(intent.ACTION_MAIN)) {// This action is the normal way to bring up the in-call UI.//// Most of the interesting work of updating the onscreen UI (to// match the current telephony state) happens in the// syncWithPhoneState() => updateScreen() sequence that happens in// onResume().//// But we do check here for one extra that can come along with the// ACTION_MAIN intent:if (intent.hasExtra(SHOW_DIALPAD_EXTRA)) {// SHOW_DIALPAD_EXTRA can be used here to specify whether the// DTMF// dialpad should be initially visible. If the extra isn't// present at all, we just leave the dialpad in its previous// state.boolean showDialpad = intent.getBooleanExtra(SHOW_DIALPAD_EXTRA, false);if (VDBG)log("- internalResolveIntent: SHOW_DIALPAD_EXTRA: "+ showDialpad);// If SHOW_DIALPAD_EXTRA is specified, that overrides whatever// the previous state of inCallUiState.showDialpad was.mApp.inCallUiState.showDialpad = showDialpad;}// ...and in onResume() we'll update the onscreen dialpad state to// match the InCallUiState.return;}if (action.equals(OtaUtils.ACTION_DISPLAY_ACTIVATION_SCREEN)) {// Bring up the in-call UI in the OTASP-specific "activate" state;// see OtaUtils.startInteractiveOtasp(). Note that at this point// the OTASP call has not been started yet; we won't actually make// the call until the user presses the "Activate" button.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;}// Various intent actions that should no longer come here directly:if (action.equals(OtaUtils.ACTION_PERFORM_CDMA_PROVISIONING)) {// This intent is now handled by the InCallScreenShowActivation// activity, which translates it into a call to// OtaUtils.startInteractiveOtasp().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)) {// ACTION_CALL* intents go to the OutgoingCallBroadcaster, which now// translates them into CallController.placeCall() calls rather than// launching the InCallScreen directly.throw new IllegalStateException("Unexpected CALL action received by InCallScreen: "+ intent);} else if (action.equals(ACTION_UNDEFINED)) {// This action is only used for internal bookkeeping; we should// never actually get launched with it.Log.wtf(LOG_TAG,"internalResolveIntent: got launched with ACTION_UNDEFINED");return;} else {Log.wtf(LOG_TAG,"internalResolveIntent: unexpected intent action: "+ action);// But continue the best we can (basically treating this case// like ACTION_MAIN...)return;}}
拨号到界面显示出来到此就分析完了,但没有涉及到Framework层,后面会再分析Framework;在手机端挂断电话后,拨号界面也会关闭,那么这个过程是怎么走的,下面也来分析下在前面一篇界面了PhoneApp类的初始化,在onCreate()时会对CallNotifier类进行初始化
// Create the CallNotifer singleton, which handles// asynchronous events from the telephony layer (like// launching the incoming-call UI when an incoming call comes// in.)notifier = CallNotifier.init(this, phone, ringer, mBtHandsfree,new CallLogAsync());
创建CallNotifier,使用单例
/**     * Initialize the singleton CallNotifier instance.     * This is only done once, at startup, from PhoneApp.onCreate().     */    /* package */ static CallNotifier init(PhoneApp app, Phone phone, Ringer ringer,                                           BluetoothHandsfree btMgr, CallLogAsync callLog) {        synchronized (CallNotifier.class) {            if (sInstance == null) {                sInstance = new CallNotifier(app, phone, ringer, btMgr, callLog);            } else {                Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);            }            return sInstance;        }    }
在构造函数里做一些初始化工作

/** Private constructor; @see init() */    private CallNotifier(PhoneApp app, Phone phone, Ringer ringer,                         BluetoothHandsfree btMgr, CallLogAsync callLog) {        mApplication = app;        mCM = app.mCM;        mCallLog = callLog;        mAudioManager = (AudioManager) mApplication.getSystemService(Context.AUDIO_SERVICE);        //跟CallManager注册通知,跟Framework通訊        registerForNotifications();        // Instantiate the ToneGenerator for SignalInfo and CallWaiting        // TODO: We probably don't need the mSignalInfoToneGenerator instance        // around forever. Need to change it so as to create a ToneGenerator instance only        // when a tone is being played and releases it after its done playing.        try {            mSignalInfoToneGenerator = new ToneGenerator(AudioManager.STREAM_VOICE_CALL,                    TONE_RELATIVE_VOLUME_SIGNALINFO);        } catch (RuntimeException e) {            Log.w(LOG_TAG, "CallNotifier: Exception caught while creating " +                    "mSignalInfoToneGenerator: " + e);            mSignalInfoToneGenerator = null;        }        mRinger = ringer;        mBluetoothHandsfree = btMgr;        TelephonyManager telephonyManager = (TelephonyManager)app.getSystemService(                Context.TELEPHONY_SERVICE);        telephonyManager.listen(mPhoneStateListener,                PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR                | PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR);    }
注册消息,跟Framework层通讯

private void registerForNotifications() {        mCM.registerForNewRingingConnection(this, PHONE_NEW_RINGING_CONNECTION, null);        mCM.registerForPreciseCallStateChanged(this, PHONE_STATE_CHANGED, null);        mCM.registerForDisconnect(this, PHONE_DISCONNECT, null);        mCM.registerForUnknownConnection(this, PHONE_UNKNOWN_CONNECTION_APPEARED, null);        mCM.registerForIncomingRing(this, PHONE_INCOMING_RING, null);        mCM.registerForCdmaOtaStatusChange(this, EVENT_OTA_PROVISION_CHANGE, null);        mCM.registerForCallWaiting(this, PHONE_CDMA_CALL_WAITING, null);        mCM.registerForDisplayInfo(this, PHONE_STATE_DISPLAYINFO, null);        mCM.registerForSignalInfo(this, PHONE_STATE_SIGNALINFO, null);        mCM.registerForInCallVoicePrivacyOn(this, PHONE_ENHANCED_VP_ON, null);        mCM.registerForInCallVoicePrivacyOff(this, PHONE_ENHANCED_VP_OFF, null);        mCM.registerForRingbackTone(this, PHONE_RINGBACK_TONE, null);        mCM.registerForResendIncallMute(this, PHONE_RESEND_MUTE, null);    }
消息处理部分

@Override    public void handleMessage(Message msg) {        switch (msg.what) {            case PHONE_NEW_RINGING_CONNECTION:                log("RINGING... (new)");                onNewRingingConnection((AsyncResult) msg.obj);                mSilentRingerRequested = false;                break;            case PHONE_INCOMING_RING:                // repeat the ring when requested by the RIL, and when the user has NOT                // specifically requested silence.                if (msg.obj != null && ((AsyncResult) msg.obj).result != null) {                    PhoneBase pb =  (PhoneBase)((AsyncResult)msg.obj).result;                    if ((pb.getState() == Phone.State.RINGING)                            && (mSilentRingerRequested == false)) {                        if (DBG) log("RINGING... (PHONE_INCOMING_RING event)");                        mRinger.ring();                    } else {                        if (DBG) log("RING before NEW_RING, skipping");                    }                }                break;            case PHONE_STATE_CHANGED:                onPhoneStateChanged((AsyncResult) msg.obj);                break;            case PHONE_DISCONNECT:                if (DBG) log("DISCONNECT");                onDisconnect((AsyncResult) msg.obj);                break;            case PHONE_UNKNOWN_CONNECTION_APPEARED:                onUnknownConnectionAppeared((AsyncResult) msg.obj);                break;            case RINGER_CUSTOM_RINGTONE_QUERY_TIMEOUT:                // CallerInfo query is taking too long!  But we can't wait                // any more, so start ringing NOW even if it means we won't                // use the correct custom ringtone.                Log.w(LOG_TAG, "CallerInfo query took too long; manually starting ringer");                // In this case we call onCustomRingQueryComplete(), just                // like if the query had completed normally.  (But we're                // going to get the default ringtone, since we never got                // the chance to call Ringer.setCustomRingtoneUri()).                onCustomRingQueryComplete();                break;            case PHONE_MWI_CHANGED:                onMwiChanged(mApplication.phone.getMessageWaitingIndicator());                break;            case PHONE_BATTERY_LOW:                onBatteryLow();                break;            case PHONE_CDMA_CALL_WAITING:                if (DBG) log("Received PHONE_CDMA_CALL_WAITING event");                onCdmaCallWaiting((AsyncResult) msg.obj);                break;            case CDMA_CALL_WAITING_REJECT:                Log.i(LOG_TAG, "Received CDMA_CALL_WAITING_REJECT event");                onCdmaCallWaitingReject();                break;            case CALLWAITING_CALLERINFO_DISPLAY_DONE:                Log.i(LOG_TAG, "Received CALLWAITING_CALLERINFO_DISPLAY_DONE event");                mCallWaitingTimeOut = true;                onCdmaCallWaitingReject();                break;            case CALLWAITING_ADDCALL_DISABLE_TIMEOUT:                if (DBG) log("Received CALLWAITING_ADDCALL_DISABLE_TIMEOUT event ...");                // Set the mAddCallMenuStateAfterCW state to true                mApplication.cdmaPhoneCallState.setAddCallMenuStateAfterCallWaiting(true);                mApplication.updateInCallScreen();                break;            case PHONE_STATE_DISPLAYINFO:                if (DBG) log("Received PHONE_STATE_DISPLAYINFO event");                onDisplayInfo((AsyncResult) msg.obj);                break;            case PHONE_STATE_SIGNALINFO:                if (DBG) log("Received PHONE_STATE_SIGNALINFO event");                onSignalInfo((AsyncResult) msg.obj);                break;            case DISPLAYINFO_NOTIFICATION_DONE:                if (DBG) log("Received Display Info notification done event ...");                CdmaDisplayInfo.dismissDisplayInfoRecord();                break;            case EVENT_OTA_PROVISION_CHANGE:                if (DBG) log("EVENT_OTA_PROVISION_CHANGE...");                mApplication.handleOtaspEvent(msg);                break;            case PHONE_ENHANCED_VP_ON:                if (DBG) log("PHONE_ENHANCED_VP_ON...");                if (!mVoicePrivacyState) {                    int toneToPlay = InCallTonePlayer.TONE_VOICE_PRIVACY;                    new InCallTonePlayer(toneToPlay).start();                    mVoicePrivacyState = true;                    // Update the VP icon:                    if (DBG) log("- updating notification for VP state...");                    mApplication.notificationMgr.updateInCallNotification();                }                break;            case PHONE_ENHANCED_VP_OFF:                if (DBG) log("PHONE_ENHANCED_VP_OFF...");                if (mVoicePrivacyState) {                    int toneToPlay = InCallTonePlayer.TONE_VOICE_PRIVACY;                    new InCallTonePlayer(toneToPlay).start();                    mVoicePrivacyState = false;                    // Update the VP icon:                    if (DBG) log("- updating notification for VP state...");                    mApplication.notificationMgr.updateInCallNotification();                }                break;            case PHONE_RINGBACK_TONE:                onRingbackTone((AsyncResult) msg.obj);                break;            case PHONE_RESEND_MUTE:                onResendMute();                break;            case UPDATE_IN_CALL_NOTIFICATION:                mApplication.notificationMgr.updateInCallNotification();                break;            default:                // super.handleMessage(msg);        }    }
主要分析通话状态的改变标记:PHONE_STATE_CHANGED
case PHONE_STATE_CHANGED:                onPhoneStateChanged((AsyncResult) msg.obj);                break;
根据不同的状态处理

/**     * Updates the phone UI in response to phone state changes.     *     * Watch out: certain state changes are actually handled by their own     * specific methods:     *   - see onNewRingingConnection() for new incoming calls     *   - see onDisconnect() for calls being hung up or disconnected     */    private void onPhoneStateChanged(AsyncResult r) {        Phone.State state = mCM.getState();        if (VDBG) log("onPhoneStateChanged: state = " + state);        // Turn status bar notifications on or off depending upon the state        // of the phone.  Notification Alerts (audible or vibrating) should        // be on if and only if the phone is IDLE.        mApplication.notificationMgr.statusBarHelper                .enableNotificationAlerts(state == Phone.State.IDLE);        Phone fgPhone = mCM.getFgPhone();        if (fgPhone.getPhoneType() == Phone.PHONE_TYPE_CDMA) {            if ((fgPhone.getForegroundCall().getState() == Call.State.ACTIVE)                    && ((mPreviousCdmaCallState == Call.State.DIALING)                    ||  (mPreviousCdmaCallState == Call.State.ALERTING))) {                if (mIsCdmaRedialCall) {                    int toneToPlay = InCallTonePlayer.TONE_REDIAL;                    new InCallTonePlayer(toneToPlay).start();                }                // Stop any signal info tone when call moves to ACTIVE state                stopSignalInfoTone();            }            mPreviousCdmaCallState = fgPhone.getForegroundCall().getState();        }        // Have the PhoneApp recompute its mShowBluetoothIndication        // flag based on the (new) telephony state.        // There's no need to force a UI update since we update the        // in-call notification ourselves (below), and the InCallScreen        // listens for phone state changes itself.        mApplication.updateBluetoothIndication(false);        // Update the proximity sensor mode (on devices that have a        // proximity sensor).        mApplication.updatePhoneState(state);        if (state == Phone.State.OFFHOOK) {            // stop call waiting tone if needed when answering            if (mCallWaitingTonePlayer != null) {                mCallWaitingTonePlayer.stopTone();                mCallWaitingTonePlayer = null;            }            if (VDBG) log("onPhoneStateChanged: OFF HOOK");            // make sure audio is in in-call mode now            PhoneUtils.setAudioMode(mCM);            // if the call screen is showing, let it handle the event,            // otherwise handle it here.            if (!mApplication.isShowingCallScreen()) {                mApplication.setScreenTimeout(PhoneApp.ScreenTimeoutDuration.DEFAULT);                mApplication.requestWakeState(PhoneApp.WakeState.SLEEP);            }            // Since we're now in-call, the Ringer should definitely *not*            // be ringing any more.  (This is just a sanity-check; we            // already stopped the ringer explicitly back in            // PhoneUtils.answerCall(), before the call to phone.acceptCall().)            // TODO: Confirm that this call really *is* unnecessary, and if so,            // remove it!            if (DBG) log("stopRing()... (OFFHOOK state)");            mRinger.stopRing();            // Post a request to update the "in-call" status bar icon.            //            // We don't call NotificationMgr.updateInCallNotification()            // directly here, for two reasons:            // (1) a single phone state change might actually trigger multiple            //   onPhoneStateChanged() callbacks, so this prevents redundant            //   updates of the notification.            // (2) we suppress the status bar icon while the in-call UI is            //   visible (see updateInCallNotification()).  But when launching            //   an outgoing call the phone actually goes OFFHOOK slightly            //   *before* the InCallScreen comes up, so the delay here avoids a            //   brief flicker of the icon at that point.            if (DBG) log("- posting UPDATE_IN_CALL_NOTIFICATION request...");            // Remove any previous requests in the queue            removeMessages(UPDATE_IN_CALL_NOTIFICATION);            final int IN_CALL_NOTIFICATION_UPDATE_DELAY = 1000;  // msec            sendEmptyMessageDelayed(UPDATE_IN_CALL_NOTIFICATION,                                    IN_CALL_NOTIFICATION_UPDATE_DELAY);        }        if (fgPhone.getPhoneType() == Phone.PHONE_TYPE_CDMA) {            Connection c = fgPhone.getForegroundCall().getLatestConnection();            if ((c != null) && (PhoneNumberUtils.isLocalEmergencyNumber(c.getAddress(),                                                                        mApplication))) {                if (VDBG) log("onPhoneStateChanged: it is an emergency call.");                Call.State callState = fgPhone.getForegroundCall().getState();                if (mEmergencyTonePlayerVibrator == null) {                    mEmergencyTonePlayerVibrator = new EmergencyTonePlayerVibrator();                }                if (callState == Call.State.DIALING || callState == Call.State.ALERTING) {                    mIsEmergencyToneOn = Settings.System.getInt(                            mApplication.getContentResolver(),                            Settings.System.EMERGENCY_TONE, EMERGENCY_TONE_OFF);                    if (mIsEmergencyToneOn != EMERGENCY_TONE_OFF &&                        mCurrentEmergencyToneState == EMERGENCY_TONE_OFF) {                        if (mEmergencyTonePlayerVibrator != null) {                            mEmergencyTonePlayerVibrator.start();                        }                    }                } else if (callState == Call.State.ACTIVE) {                    if (mCurrentEmergencyToneState != EMERGENCY_TONE_OFF) {                        if (mEmergencyTonePlayerVibrator != null) {                            mEmergencyTonePlayerVibrator.stop();                        }                    }                }            }        }        if ((fgPhone.getPhoneType() == Phone.PHONE_TYPE_GSM)                || (fgPhone.getPhoneType() == Phone.PHONE_TYPE_SIP)) {            Call.State callState = mCM.getActiveFgCallState();            if (!callState.isDialing()) {                // If call get activated or disconnected before the ringback                // tone stops, we have to stop it to prevent disturbing.                if (mInCallRingbackTonePlayer != null) {                    mInCallRingbackTonePlayer.stopTone();                    mInCallRingbackTonePlayer = null;                }            }        }    }                    
如果需要对不能的通话状态处理不同的事情可以获取以下状态
Phone.State state = mCM.getState();
有以下状态

Phone.State.OFFHOOK    挂断

Phone.State.RINGING      正在来电铃声状态

Phone.State.IDLE               空间状态
例如在通过结束后关闭InCallScreen界面可以添加以下代码

if(Phone.State.OFFHOOK == state){    finish();}
后面再分析InCallScreen的UI显示和Framework层

0 1