四.GsmCallTracker.java与RIL.java交互完成物理拨号
1).在上面的实例化Phone过程我们知道了最后调用的GSMPhone类的方法,而上面实例化Phone的同时我们看到参数有一个sCommandsInterfaces[i],这个sCommandsInterfaces[i]是什么类型的数组呢,我们看上面的定义即可知:
sCommandsInterfaces = newRIL[numPhones];
在这里已经很明显了,这个sCommandsInterfaces是RIL 类型的,我们知道最终的物理层的拨号动作是由这个RIL.java(frameworks/opt/telephony)来完成的,在这里赋值可是别有用意啊,我们先跟进去看一下构造方法 GSMPhone():
super(context, ci, notifier,unitTestMode);
((CommandsInterface)mCi).setOnVPFallBack(this, EVENT_FALLBACK, null);
((CommandsInterface)mCi).setOnVPFail(this, EVENT_VIDEOCALLFAIL,null);
((CommandsInterface)mCi).setOnVPCodec(this, EVENT_VIDEOCALLCODEC, null);
((CommandsInterface)mCi).setOnSimSmsReady(this,EVENT_SIM_SMS_READY, null);
这里又调用到了其父类GSMPhone的构造方法,我们先看一下这个 mCi是什么类型的,这个mCi又是定义在GSMPhone的父类PhoneBase.java(frameworks/opt/telephony)中:
public CommandsInterface mCi;
可以看到 mCi是CommandsInterface.java(frameworks/opt/telephony)类型的,我们继续看上面的super方法,调用的是GSMPhone构造方法:
super("GSM", notifier, context,ci, unitTestMode);
if (ciinstanceof SimulatedRadioControl) {
mSimulatedRadioControl = (SimulatedRadioControl) ci;
}
mCi.setPhoneType(PhoneConstants.PHONE_TYPE_GSM);
mCT = new GsmCallTracker(this);
mSST = new GsmServiceStateTracker(this);
mDcTracker =new DcTracker(this);
这里继续调用其父类PhoneBase的构造方法,同时的实例化了一个GsmCallTracker 对象mCT,我们先记住这个,继续,我们看到关键的一行:
mCi = ci;
看到这里一切都明朗了,ci即是我们通过:
phone = new GSMPhone(context,
sCommandsInterfaces[i], sPhoneNotifier, i);
里面的第二个参数一级级传递过来的,上面我们特意还提到了这一点,现在看来不是毫无用处的,相反,还是特别重要的,但是sCommandsInterfaces[i]貌似是RIL类型的,而mCi是CommandsInterface类型的,这两者为什么能够互相转换,查看RIL类即可知,RIL实现了CommandsInterface借口,而CommandsInterface是CommandsInterface的子接口,所以这两者是同一类型的,好的延伸到这里,我们了解到,传递的mCi是最终传递过来的sCommandsInterfaces[i],这个就够了;
回到最初,通过上面的一系列分析我们得知了两个很重要的信息,第一,我们知道TelephonyConnectionService类的onCreateOutgoingConnection去拨号时需要用到的phone实例,也就是以下语句:
final Phone phone =getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber);
这个phone最终拿到的是GSMPhone类的对象,第二,mCi最终是传递过来RIL 类型的对象,好了,我们继续回到拨号动作,会调用一下方法:
originalConnection = phone.dial(number,request.getVideoState());
好了,看到了dial方法,此方法即是拨号方法的关键入口,我们已知phone是GSMPhone实例,所以,我们跟踪到GSMPhone中去查看dial方法,最后会去调用dialInternal方法:
if (mmi == null) {
return ((GsmCallTracker)mCT).dial(newDialString, CLIR_DEFAULT, uusInfo,videoState);
} else if(mmi.isTemporaryModeCLIR()) {
return mCT.dial(mmi.mDialingNumber, mmi.getCLIRMode(), uusInfo);
} else {
mPendingMMIs.add(mmi);
mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
mmi.processCode();
// FIXME should this return null or something else?
return null;
}
到这里,我们看到十分重要的一句:
return((GsmCallTracker)mCT).dial(newDialString, CLIR_DEFAULT, uusInfo,videoState);
这个mCT实例我们看上面的红色文字部分,在调用GSMPhone构造方法时已经创建了,所以在这里可以直接使用,我们继续跟踪,调用的是 GsmCallTracker.java(frameworks/opt/)类的dial方法:
String origNumber = dialString;
dialString =convertNumberIfNecessary(mPhone, dialString);
// The newcall must be assigned to the foreground call.
// That callmust be idle, so place anything that's
// there onhold
if(mForegroundCall.getState() == GsmCall.State.ACTIVE) {
// this will probably be done by the radio anyway
// but the dial might fail before this happens
// and we need to make sure the foreground call is clear
// for the newly dialed connection
switchWaitingOrHoldingAndActive();
// Fake local state so that
// a) foregroundCall is empty for the newly dialed connection
// b) hasNonHangupStateChanged remains false in the
// next poll, so that we don't clear a failed dialing call
fakeHoldForegroundBeforeDial();
}
if(mForegroundCall.getState() != GsmCall.State.IDLE) {
//we should have failed in !canDial() above before we get here
throw new CallStateException("cannot dial in current state");
}
mPendingMO =new GsmConnection(mPhone.getContext(),checkForTestEmergencyNumber(dialString),
this, mForegroundCall,VideoProfile.VideoState.isBidirectional(videoState));
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();
}
if(mNumberConverted) {
mPendingMO.setConverted(origNumber);
mNumberConverted = false;
}
updatePhoneState();
mPhone.notifyPreciseCallStateChanged();
returnmPendingMO;
在这里我们看到:
mCi.dial(tmpAddr, clirMode, uusInfo,obtainCompleteMessage());
这是多么的熟悉,上面特意解析了这个mCi最终是RIL 类型的,而我们知道RIL在我们的用户空间的拨号流程中已经走到最后一层了,接下来,就回去调用底层的JNI或者modem层去实现物理拨号发射信号了,经过上面的种种分析,我们找出了整个拨号流程的最终走向,先去建立连接,等待连接建立成功后就去调用RIL层去做真正的拨号动作,虽然看上去比较复杂,但是仔细去分析还是能够看出,整个框架是十分稳固的,流程清晰,同时做好了各种状态的保存,贯穿上下层,每个模块分工明确细致并且相对于之前的平台大大降低了模块之间的耦合性,可见框架的重要性,以上就是拨号的整个流程了,涉及到了各个层次的各个模块,从上到下,横向涉及的功能类十分之多,整个大的框架十分的复杂,但是仔细梳理就能发现贯穿其中的两条线就是我们上面分析的两路进程,顺着这两条线往下走就能走通整个流程,接下来我们会继续就通话来电这一块继续探索分析。