Android 5.1 Phone MO(去电)流程分析(Framework层)

2019-04-14 17:07发布

写在前面的话

本文主要分析MO(去电)的流程,研究的代码是Android 5.1的,目前只关注Framework层,以CDMA为例。
关于应用层的流程,请看《Android 5.1 Phone MO(去电)流程分析(应用层)
这里写图片描述
(如果图片看不清的话,可以右键选择在新标签中打开图片,或者把图片另存到自己电脑再查看。)
http://blog.csdn.net/linyongan
步骤1和2:紧接着应用层的流程,在TelephonyConnectionService.java的placeOutgoingConnection方法里调用了phone.dial(),我们这里研究的是CDMA,所以下面会进入CDMAPhone.java的dial方法: public Connection dial (String dialString, int videoState, Bundle extras){ ... return dialInternal(dialString, null, videoState); } protected Connection dialInternal (String dialString, UUSInfo uusInfo,int videoState) throws CallStateException { // Need to make sure dialString gets parsed properly String newDialString = PhoneNumberUtils.stripSeparators(dialString); return mCT.dial(newDialString); } 步骤3和4: mCT是CdmaCallTracker类型的,所以我们进入CdmaCallTracker.java的dial方法里: /** * clirMode is one of the CLIR_ constants */ Connection dial (String dialString, int clirMode) throws CallStateException { // note that this triggers call state changed notif clearDisconnected(); if (!canDial()) { throw new CallStateException("cannot dial in current state"); } String origNumber = dialString; String operatorIsoContry = mPhone.getSystemProperty( TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, ""); String simIsoContry = mPhone.getSystemProperty( TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY, ""); boolean internationalRoaming = !TextUtils.isEmpty(operatorIsoContry) && !TextUtils.isEmpty(simIsoContry) && !simIsoContry.equals(operatorIsoContry); if (internationalRoaming) { if ("us".equals(simIsoContry)) { internationalRoaming = internationalRoaming && !"vi".equals(operatorIsoContry); } else if ("vi".equals(simIsoContry)) { internationalRoaming = internationalRoaming && !"us".equals(operatorIsoContry); } } if (internationalRoaming) { dialString = convertNumberIfNecessary(mPhone, dialString); } String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false"); boolean isPhoneInEcmMode = inEcm.equals("true"); boolean isEmergencyCall = PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(), dialString); // Cancel Ecm timer if a second emergency call is originating in Ecm mode if (isPhoneInEcmMode && isEmergencyCall) { handleEcmTimer(CDMAPhone.CANCEL_ECM_TIMER); } // We are initiating a call therefore even if we previously // didn't know the state (i.e. Generic was true) we now know // and therefore can set Generic to false. mForegroundCall.setGeneric(false); // The new call must be assigned to the foreground call. // That call must be idle, so place anything that's // there on hold if (mForegroundCall.getState() == CdmaCall.State.ACTIVE) { return dialThreeWay(dialString); } mPendingMO = new CdmaConnection(mPhone.getContext(), checkForTestEmergencyNumber(dialString), this, mForegroundCall); mHangupPendingMO = false; if ( mPendingMO.getAddress() == null || mPendingMO.getAddress().length() == 0 || mPendingMO.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0 ) { // Phone number is invalid mPendingMO.mCause = DisconnectCause.INVALID_NUMBER; // handlePollCalls() will notice this call not present // and will mark it as dropped. pollCallsWhenSafe(); } else { // Always unmute when initiating a new call setMute(false); // Check data call disableDataCallInEmergencyCall(dialString); // In Ecm mode, if another emergency call is dialed, Ecm mode will not exit. if(!isPhoneInEcmMode || (isPhoneInEcmMode && isEmergencyCall)) { mCi.dial(mPendingMO.getAddress(), clirMode, obtainCompleteMessage()); } else { mPhone.exitEmergencyCallbackMode(); mPhone.setOnEcbModeExitResponse(this,EVENT_EXIT_ECM_RESPONSE_CDMA, null); mPendingCallClirMode=clirMode; mPendingCallInEcm=true; } } if (mNumberConverted) { mPendingMO.setConverted(origNumber); mNumberConverted = false; } //更新phone状态 updatePhoneState(); //发起phone状态变化通知 mPhone.notifyPreciseCallStateChanged(); //返回通话连接 return mPendingMO; } /** * Obtain a message to use for signalling "invoke getCurrentCalls() when * this operation and all other pending operations are complete */ private Message obtainCompleteMessage() { return obtainCompleteMessage(EVENT_OPERATION_COMPLETE); } /** * Obtain a message to use for signalling "invoke getCurrentCalls() when * this operation and all other pending operations are complete */ private Message obtainCompleteMessage(int what) { mPendingOperations++; mLastRelevantPoll = null; mNeedsPoll = true; if (DBG_POLL) log("obtainCompleteMessage: pendingOperations=" + mPendingOperations + ", needsPoll=" + mNeedsPoll); return obtainMessage(what); } 步骤5:先看obtainCompleteMessage方法,这里调用obtainCompleteMessage带一个参数的方法创建了一个消息类型为EVENT_OPERATION_COMPLETE的Message,这个Message也会传入到mCi.dial方法里,在这里是CdmaCallTracker主动向RILJ(mCi是RILJ的实例对象,后面再解释为什么)发送消息,等RILJ处理完之后通过回调通知CdmaCallTracker,CdmaCallTracker就在handleMessage方法处理。
知识点解析:至于mCi,它是CommandsInterface类型的,在CdmaCallTracker的构造方法里通过mCi = phone.mCi;获取: public CdmaCallTracker(CDMAPhone phone) { ... mCi = phone.mCi; ... } phone是CDMAPhone类型的,在CDMAPhone的构造方法里并没有关于mCi的定义和创建代码,只有super这一行有联系 public CDMAPhone(Context context, CommandsInterface ci, PhoneNotifier notifier, boolean unitTestMode) { super("CDMA", notifier, context, ci, unitTestMode); ... } 我们再进入CDMAPhone的父类PhoneBase的构造方法里, protected PhoneBase(String name, PhoneNotifier notifier, Context context, CommandsInterface ci,boolean unitTestMode) { ... mCi = ci; ... } 找到mCi的定义和初始化信息:mCi = ci;ci是在CDMAPhone的构造方法里的传递对象,因此,如果找到CDMAPhone创建phone对象的地方,也许可以找到ci的创建。
我们要找CDMAPhone被调用的地方,(在Source Insight这个编译器中,选中CDMAPhone,然后按快捷键Ctrl+/),我们找到PhoneFactory.java的makeDefaultPhone方法里: public static void makeDefaultPhone(Context context) { ... sCommandsInterface = new RIL(context, networkMode, cdmaSubscription); UiccController.make(context, sCommandsInterface); int phoneType = TelephonyManager.getPhoneType(networkMode); if (phoneType == PhoneConstants.PHONE_TYPE_GSM) { Rlog.i(LOG_TAG, "Creating GSMPhone"); sProxyPhone = new PhoneProxy( new GSMPhone(context,sCommandsInterface, sPhoneNotifier)); } } 在创建Phone对象时,传入RILJ类型的sCommandsInterface对象作为参数,因此,我们可以确定CdmaCallTracker.java中使用mCi.dial进行拨号,其实就是调用了RIL.java的dial方法。
步骤6: RIL.java的dial方法 public void dial(String address, int clirMode, UUSInfo uusInfo, Message result) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result); rr.mParcel.writeString(address); rr.mParcel.writeInt(clirMode); if (uusInfo == null) { rr.mParcel.writeInt(0); // UUS information is absent } else { rr.mParcel.writeInt(1); // UUS information is present rr.mParcel.writeInt(uusInfo.getType()); rr.mParcel.writeInt(uusInfo.getDcs()); rr.mParcel.writeByteArray(uusInfo.getUserData()); } //打印日志 if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } 打印出来的log: 08-11 09:39:24.313 D/RILJ ( 2904): [5503]> DIAL 在这里创建了RILRequest 对象rr,将CdmaCallTracker传递过来的消息类型为EVENT_OPERATION_COMPLETE的Message赋值给rr.mResult,然后RILJ向RILD发送了RIL_REQUEST_DIAL请求。
步骤7:等底层处理完之后,会发送RIL_REQUEST_DIAL消息给RILJ,在RILJ的processSolicited方法里接收并处理该消息: private RILRequest processSolicited (Parcel p) { ... case RIL_REQUEST_DIAL: ret = responseVoid(p); break; ... //打印log日志 if (RILJ_LOGD) riljLog(rr.serialString() + "< " + requestToString(rr.mRequest) + " " + retToString(rr.mRequest, ret)); if (rr.mResult != null) { AsyncResult.forMessage(rr.mResult, null, tr); rr.mResult.sendToTarget();//发出handler消息通知 } 步骤8,9,10: rr.mResult就是在CdmaCallTracker中创建的消息类型为EVENT_OPERATION_COMPLETE的Message,所以调用sendToTarget方法,就会把消息发送给CdmaCallTracker,然后在CdmaCallTracker.java的handleMessage方法中有对EVENT_OPERATION_COMPLETE的逻辑处理: public void handleMessage (Message msg) { ... switch (msg.what) { case EVENT_OPERATION_COMPLETE: operationComplete(); break; ... } private void operationComplete() { mPendingOperations--; if (DBG_POLL) log("operationComplete: pendingOperations=" + mPendingOperations + ", needsPoll=" + mNeedsPoll); if (mPendingOperations == 0 && mNeedsPoll) { mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT); mCi.getCurrentCalls(mLastRelevantPoll); } else if (mPendingOperations < 0) { // this should never happen Rlog.e(LOG_TAG,"CdmaCallTracker.pendingOperations < 0"); mPendingOperations = 0; } } 步骤11和12:在这里通过obtainMessage方法生成一个消息类型为EVENT_POLL_CALLS_RESULT的Message并且作为getCurrentCalls方法的参数传递到mCi,mCi是RIL.java的实例对象,这也就回到了RIL.java的getCurrentCalls方法里,getCurrentCalls将RIL_REQUEST_GET_CURRENT_CALLS 消息封装成RILRequest 类型并发送。 public void getCurrentCalls (Message result) { //注意rr对象的消息类型,后面会用到 RILRequest rr = RILRequest.obtain( RIL_REQUEST_GET_CURRENT_CALLS, result); //打印log日志 if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); } 打印出相应的log是 09-09 17:32:02.179 D/RILJ ( 2795): [5656]> GET_CURRENT_CALLS 步骤13: RILJ向底层请求查询Call List状态列表,等底层处理完之后,就把结果返回给RILJ,向RILJ发送RIL_REQUEST_GET_CURRENT_CALLS消息,RILJ在processSolicited方法里处理它。 private RILRequest processSolicited (Parcel p) { ... case RIL_REQUEST_GET_CURRENT_CALLS: ret = responseCallList(p); break; ... //打印log日志 if (RILJ_LOGD) riljLog(rr.serialString() + "< " + requestToString(rr.mRequest) + " " + retToString(rr.mRequest, ret)); if (rr.mResult != null) { AsyncResult.forMessage(rr.mResult, null, tr); rr.mResult.sendToTarget();//发出handler消息通知 } 步骤14和15:还是跟上面一样,rr.mRe