拨打电话流程(Phone--App层)



 这里还是以MTK4.0代码为基础。

Contacts流程:http://h529820165.iteye.com/blog/1677877

Phone.apk.

OutgoingCallReceiver.java

接受到Contacts传过来的广播后

 public void onReceive(Context context, Intent intent) {
                  ...
  Uri uri = intent.getData();;
  Intent newIntent = new Intent(Intent.ACTION_CALL, uri);
            newIntent.putExtra(Constants.EXTRA_IS_VIDEO_CALL, intent.getBooleanExtra(Constants.EXTRA_IS_VIDEO_CALL, false));
 if((PhoneNumberUtils.isUriNumber(number) && intent.getIntExtra(Constants.EXTRA_SLOT_ID, -1) == -1)
                    || Constants.SCHEME_SIP.equals(uri.getScheme())) {
                startSipCallOptionHandler(context, newIntent);
            } else {
                newIntent.putExtra(OutgoingCallBroadcaster.EXTRA_ACTUAL_NUMBER_TO_DIAL, number);
                newIntent.putExtra(Constants.EXTRA_SLOT_ID, intent.getIntExtra(Constants.EXTRA_SLOT_ID, 0));
                PhoneApp.getInstance().callController.placeCall(newIntent);
            }
 }

 这里将获取的uri,是否是video_call,number,slot封装在intent,此时action的值是:Intent.ACTION_CALL中,然后调用PhoneApp.getInstance().callController.placeCall。很容易就知道这里使用了单例模式。【因为这里有 intent.getIntExtra(Constants.EXTRA_SLOT_ID, -1) == -1这段代码,所以笔者猜测slot=-1时,是sip电话】

public void placeCall(Intent intent) {
      ....
      inCallUiState.clearProviderOverlayInfo();
      ...
      CallStatusCode status = placeCallInternal(intent);
     ...
      switch (status) {
        case SUCCESS:
        case EXITED_ECM:
                if (status == CallStatusCode.EXITED_ECM) {
                    inCallUiState.setPendingCallStatusCode(CallStatusCode.EXITED_ECM);
                } else {
                    inCallUiState.clearPendingCallStatusCode();
                }
                mApp.setBeginningCall(true);
          break;                                                                                                                                                     
           .....
      case DROP_VOICECALL/        default: handleOutgoingCallError(status);
                break;
}
   inCallUiState.latestDisconnectCall = null;
    inCallUiState.delayFinished = false;
   mApp.displayCallScreen(!intent.getBooleanExtra(Constants.EXTRA_IS_VIDEO_CALL, false));
}

 这里会根据状态码status 设置inCallUiState的一些属性值.如果返回Success/Exited_ecm,则开启PSonsor。不论返回何种状态,都会执行displayCallScreen,该方法会显示InCallScreen界面。该方法中有个极其重要的方法placeCallInternal(intent),该方法会去和ril层通信,执行真正的拨打电话的功能。

 private CallStatusCode placeCallInternal(Intent intent) {
 int callStatus;
if(PhoneApp.sVideoCallSupport && isVideoCall) {
            callStatus = VTCallUtils.placeVTCall(phone, number, contactUri, slot);
        } else {
            if(PhoneApp.sGemini) {
                callStatus = PhoneUtils.placeCallGemini(mApp,
                                                        phone,
                                                        number,
                                                        contactUri,
                                                        (isEmergencyNumber || isEmergencyIntent),
                                                        inCallUiState.providerGatewayUri,
                                                        slot);
                Profiler.trace(Profiler.CallControllerLeavePlaceCallInternal);
            } else {
                callStatus = PhoneUtils.placeCall(mApp,
                                                  phone,
                                                  number,
                                                  contactUri,
                                                  (isEmergencyNumber || isEmergencyIntent),
                                                  inCallUiState.providerGatewayUri);
            }
        }
    switch (callStatus) {
case Constants.CALL_STATUS_DIALED:
         ...
           if (exitedEcm) {
                    return CallStatusCode.EXITED_ECM;
                } else {
                    return CallStatusCode.SUCCESS;
                }
       break;
case Constants.CALL_STATUS_DIALED_MMI:
    return CallStatusCode.DIALED_MMI;
          .....
}
}

 由于PhoneUtils.placeCallGemini/ PhoneUtils.placeCall会调用framework层函数,所以不再这里展开,将在后面分析如何和ril通信。

所以这里总结一下placeCall的功能:首先会和ril通信拨打电话,然后返回状态码,根据状态码的不同设置不同的inCallUiState属性值。  然后displayCallScreen启动InCallScreen界面。都是都是有CallController完成的。

void displayCallScreen(final boolean isVoiceOrVTCall) {
        ...
        try {
            Intent intent = isVoiceOrVTCall ? createInCallIntent() : createVTInCallIntent();
            startActivity(intent);
        } catch (ActivityNotFoundException e) {
}
    
    }

 createInCallIntent()和createVTInCallIntent()的差别在于createVTInCallIntent()中多了intent.putExtra(Constants.EXTRA_IS_VIDEO_CALL, true);

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.FLAG_ACTIVITY_NO_ANIMATION);
        intent.setClassName("com.android.phone", getCallScreenClassName());

        if (FeatureOption.MTK_TB_APP_CALL_FORCE_SPEAKER_ON == true)
        {
          intent.putExtra(InCallScreen.EXTRA_FORCE_SPEAKER_ON, true);
        }
        
        return intent;
    }

此时的action为Intent.ACTION_MAIN。然后启动InCallScreen activity.让我们来看看onCreat函数

 protected void onCreate(Bundle icicle) {
     ....
     registerForPhoneStates();
     if (icicle == null) {
            internalResolveIntent(getIntent());
        }
      if (VTInCallScreenFlags.getInstance().mVTIsInflate
                && null != mVTInCallScreen ) {
            mVTInCallScreen.initVTInCallScreen();
        }
     ....
     registerReceiver(.......)
     ....
}

 分别讲下这几个重要函数的作用:

registerForPhoneStates();监听电话的状态变化,如果变化则根据中间层的返回值,在inCallScreen中的handler中做出相应的处理。

mVTInCallScreen.initVTInCallScreen();实例化视频通话的界面,在onResume中,会根据当前状态决定是否关闭或者显示mVTInCallScreen。

registerReceiver(....) 这里会注册Intent.ACTION_LOCALE_CHANGED,Intent.ACTION_HEADSET_PLUG,以及mDMLockReceiver是否被运营商锁定的广播.

internalResolveIntent(getIntent());  该方法会在onCreat和onNewIntent中调用

private void internalResolveIntent(Intent intent) {
    if (action.equals(intent.ACTION_MAIN)) {
                .....

         if (FeatureOption.MTK_VT3G324M_SUPPORT == true) {
                if (getInVoiceAnswerVideoCall()) {
                    setInVoiceAnswerVideoCall(false);
                }
                if (mCM.getState() == Phone.State.RINGING) {
                    // When VT call incoming, use voice call incoming call GUI
                    mVTInCallScreen.setVTVisible(false);
                    mVTInCallScreen.setVTScreenMode(VTCallUtils.VTScreenMode.VT_SCREEN_CLOSE);
                } else if (intent.getBooleanExtra(Constants.EXTRA_IS_VIDEO_CALL, false)) {
                    // When dialing VT call, inflate VTInCallScreen
                    mVTInCallScreen.initVTInCallScreen();
                    // When dialed a VT call, but dialed failed, needs not init state for dialing
                    if (CallStatusCode.SUCCESS == mApp.inCallUiState.getPendingCallStatusCode()) {
                        mVTInCallScreen.initDialingSuccessVTState();
                    }
                    mVTInCallScreen.initDialingVTState();
                    mVTInCallScreen.initCommonVTState();
                    if (Phone.State.IDLE != PhoneApp.getInstance().mCM.getState() &&
                                !VTCallUtils.isVTCall(mCM.getActiveFgCall())) {
                        // When voice is connected and place a VT call, need close VT GUI
                        mVTInCallScreen.setVTScreenMode(VTCallUtils.VTScreenMode.VT_SCREEN_CLOSE);
                    } else {
                        mVTInCallScreen.setVTScreenMode(VTCallUtils.VTScreenMode.VT_SCREEN_OPEN);
                    }
                } else {
                    // set VT open or close according the active foreground call
                    if (mCM.getState() != Phone.State.IDLE && PhoneUtils.isVideoCall(mCM.getActiveFgCall())) {
                        if (DBG) log("receive ACTION_MAIN, but active foreground call is video call");
                        mVTInCallScreen.initVTInCallScreen();
                        mVTInCallScreen.initCommonVTState();
                        mVTInCallScreen.setVTScreenMode(VTCallUtils.VTScreenMode.VT_SCREEN_OPEN);
                    } else {
                        mVTInCallScreen.setVTScreenMode(VTCallUtils.VTScreenMode.VT_SCREEN_CLOSE);
                    }
                } 
                mVTInCallScreen.updateVTScreen(mVTInCallScreen.getVTScreenMode());
            }
        }
}

 笔者省略掉了该方法中会判断是否打开键盘,是否开启扬声器的代码。  这里会根据是否是VIDEO_CALL和电话State给mVTInCallScreen设置不同的setVTScreenMode(这里有2种:VT_SCREEN_OPEN、VT_SCREEN_CLOSE),然后调用mVTInCallScreen.updateVTScreen()方法。onCreat执行完成后,紧接着是onResume()

 protected void onResume() {
        ...
        final InCallUiState inCallUiState = mApp.inCallUiState;
        ...
        if (inCallUiState.showDialpad) {
            showDialpadInternal(false);  // no "opening" animation
        } else {
            hideDialpadInternal(false);  // no "closing" animation
        }
        ...
       if (bluetoothConnected) {
            setVolumeControlStream     AudioManager.STREAM_BLUETOOTH_SCO);
        } else {
            setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
        }
    SyncWithPhoneStateStatus status = syncWithPhoneState();
     ......


}

 在该方法中,会获取单例的InCallUiState,然后根据属性值决定相应的动作,如:是否打开拨号键盘等。这里说下比较重要的一个方法syncWithPhoneState(),该方法可以更新CallCard,就是说多路通话的时候,界面的更新入口就是syncWithPhoneState。【当第二路通话来的时候,和第一路通话的流程一模一样】

 private SyncWithPhoneStateStatus syncWithPhoneState() {
            ...
    if (mCM.hasActiveFgCall() || mCM.hasActiveBgCall() || mCM.hasActiveRingingCall()
                || hasPendingMmiCodes || hasPendingMmiCodes2 || showProgressIndication) {
            if (VDBG) log("syncWithPhoneState: it's ok to be here; update the screen...");
            updateScreen();
            return SyncWithPhoneStateStatus.SUCCESS;
        }
       ...
}

 mCM是CallManager的对象。
hasActiveFgCall()   Return true if there is at least one active foreground call 

hasActiveBgCall()  Return true if there is at least one active background call

hasActiveRingingCall()   Return true if there is at least one active ringing call

这里可以知道,如果有电话则更新屏幕updateScreen(),该方法又会调用updateScreen(false)

 private void updateScreen(boolean force) {
     ...
      if (!isDialerOpened()) {
            mCallCard.updateState(mCM);
        }
    ....
}

 
如果打开了键盘,则不更新callcard。mCallCard为CallCard的对象。

 

void updateState(CallManager cm) {
     ...
      Phone.State state = cm.getState();  // IDLE, RINGING, or OFFHOOK
        Call ringingCall = cm.getFirstActiveRingingCall();
        Call fgCall = cm.getActiveFgCall();
        Call bgCall = cm.getFirstActiveBgCall();
if ((ringingCall.getState() != Call.State.IDLE)
                && !fgCall.getState().isDialing()) {
    ...
    updateRingingCall(cm);
}else if ((fgCall.getState() != Call.State.IDLE)
                || (bgCall.getState() != Call.State.IDLE)) {
   ...
    updateForegroundCall(cm);
 }else {
   ...
   updateNoCall(cm);
}
}

 无论是updateRingingCall,updateForegroundCall,updateNoCall都会执行displayOnHoldCallStatus,该方法决定是否显示第二路通话。这里会执行updateForegroundCall()

 private void updateForegroundCall(CallManager cm) {
   ...
   Call fgCall = cm.getActiveFgCall();
   Call bgCall = cm.getFirstActiveBgCall();
   ...
    displayMainCallStatus(cm, fgCall);
    Phone phone = fgCall.getPhone();

        int phoneType = phone.getPhoneType();
        if (phoneType == Phone.PHONE_TYPE_CDMA) {
            if ((mApplication.cdmaPhoneCallState.getCurrentCallState()
                    == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE)
                    && mApplication.cdmaPhoneCallState.IsThreeWayCallOrigStateDialing()) {
                displayOnHoldCallStatus(cm, fgCall);
            } else {
                //This is required so that even if a background call is not present
                // we need to clean up the background call area.
                displayOnHoldCallStatus(cm, bgCall);
            }
        } else if ((phoneType == Phone.PHONE_TYPE_GSM)
                || (phoneType == Phone.PHONE_TYPE_SIP)) {
            displayOnHoldCallStatus(cm, bgCall);
        }
}

 displayMainCallStatus(cm, fgCall);  //Updates the main block of caller info on the CallCard该方法会执行updateSimInfo(call)等main block info

displayOnHoldCallStatus(cm, bgCall);  //该方法会决定是否显示第二路通话


 

猜你喜欢

转载自h529820165.iteye.com/blog/1677946