Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android4.0(Phone)撥號啟動過程分析(二)

Android4.0(Phone)撥號啟動過程分析(二)

編輯:關於Android編程

接上:Android4.0(Phone)撥號啟動過程分析(一)

InCallScreen處理來電和撥號的界面,接通電話也是這個界面,接下來分析InCallScreen類是如何處理撥號流程的;

@Override
	protected 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 mPhone

		mCM = 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層

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved