Android4.0(Phone)拨号启动过程分析(二)
来源:互联网 发布:淘宝卖家怎样申诉 编辑:程序博客网 时间:2024/05/21 18:48
接上:Android4.0(Phone)拨号启动过程分析(一)
Phone.State state = mCM.getState();
有以下状态
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
- Android4.0(Phone)拨号启动过程分析(二)
- Android4.0(Phone)拨号启动过程分析(一)
- Android4.0(Phone)拨号启动过程分析(三)与Framework层通信
- Android4.0(Phone)来电过程分析
- Android6.0的phone应用源码分析(3)——phone 拨号UI分析
- Android6.0的phone应用源码分析(4)——phone拨号流程分析
- Android6.0的phone应用源码分析(3)——phone 拨号UI分析
- Android6.0的phone应用源码分析(4)——phone拨号流程分析
- Windows Phone开发(二)-- 框架结构和启动过程
- uClinux 启动过程详细分析(二)
- Arm linxu启动过程分析(二)
- SurfaceFlinger启动过程分析(二)
- ARM linxu启动过程分析(二)
- SurfaceFlinger启动过程分析(二)
- SurfaceFlinger启动过程分析(二)
- SurfaceFlinger启动过程分析(二)
- linux内核启动过程分析(二)
- 【分析】dalvik虚拟机启动过程(二)
- log4j
- 1407291801-hd-Palindromes _easy version.cpp
- hdu 1425 hash入门
- HDU 1789 贪心
- Vim 常用快捷键
- Android4.0(Phone)拨号启动过程分析(二)
- Prime Path(POJ 3126)解题报告
- 设计模式之策略模式
- Wargames:io.smashthestack Level 1
- 工作周记
- Pots(POJ 3414)解题报告(虫前,有两个痛)
- HDOJ--4893--Wow! Such Sequence!【线段树+单点、区间更新】
- 找球号二----138
- UVA - 657 The die is cast(二重dfs)