5. Android 5.0 呼叫流程5.1 MO call我们先给出MO起呼过程的示例流程图,然后结合该图做代码流程的分析: 5.1.1 拨号请求(CallActivity)我们从CallActivity来开始看MO呼叫流程,在他的processIntent里处理收到的呼叫intent,目前支持3种类型的起呼呼叫,之后processOutgoingCallIntent给CallReceiver发送一个广播,完成本阶段的处理。 private void processIntent(Intent intent) { // Ensure call intents are not processed on devices that are not capable of calling. if (!isVoiceCapable()) { return; }
verifyCallAction(intent); String action = intent.getAction();
if (Intent.ACTION_CALL.equals(action) || Intent.ACTION_CALL_PRIVILEGED.equals(action) || Intent.ACTION_CALL_EMERGENCY.equals(action)) { processOutgoingCallIntent(intent); } else if (TelecomManager.ACTION_INCOMING_CALL.equals(action)) { processIncomingCallIntent(intent); } }
5.1.2 拨号请求(CallReceiver)CallReceiver是一个广播接收器,处理所有的来电和去电广播,具体来讲,处理3个广播,来电、拨号盘去电、SIM卡去电,这里分析一下拨号盘呼叫的流程,收到拨号盘的拨号请求后,调用processOutgoingCallIntent进行后续处理。 public void onReceive(Context context, Intent intent) { final boolean isUnknownCall = intent.getBooleanExtra(KEY_IS_UNKNOWN_CALL, false); final boolean isIncomingCall = intent.getBooleanExtra(KEY_IS_INCOMING_CALL, false); Log.i(this, "onReceive - isIncomingCall: %s isUnknownCall: %s", isIncomingCall, isUnknownCall);
if (isUnknownCall) { processUnknownCallIntent(intent); } else if (isIncomingCall) { processIncomingCallIntent(intent); } else { processOutgoingCallIntent(context, intent); } }
processOutgoingCallIntent实现: 1) 获取到CallsManager的实例,并调用其方法startOutgoingCall,这里会创建一个Call实例,并通过addCall进行相关的状态维护; 2) 再新建一个NewOutgoingCallIntentBroadcaster实例,调用其方法processIntent,改变intent的一些参数,使用mCallsManager.placeOutgoingCall发起呼叫,再调用broadcastIntent发一个广播消息ACTION_NEW_OUTGOING_CALL出去。
呼叫发起的主要流程在placeOutgoingCall里面处理; ACTION_NEW_OUTGOING_CALL广播的发送流程和接收器如下: private void broadcastIntent( Intent originalCallIntent, String number, boolean receiverRequired) { Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL); mContext.sendOrderedBroadcastAsUser( broadcastIntent, UserHandle.CURRENT, PERMISSION, receiverRequired ? new NewOutgoingCallBroadcastIntentReceiver() : null, null, // scheduler Activity.RESULT_OK, // initialCode number, // initialData: initial value for the result data (number to be modified) null); // initialExtras }
Intent.ACTION_NEW_OUTGOING_CALL的定义如下, @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_NEW_OUTGOING_CALL = "android.intent.action.NEW_OUTGOING_CALL";
通过AndroidManifest.xml,我们可以看出有两个receiver接收这个intent, ProcessOutgoingCallTest.java (packages\services\telephony\src\com\android\phone): UndemoteOutgoingCallReceiver.java (packages\apps\dialer\src\com\android\dialer\interactions):
继续分析呼叫的处理流程,在CallsManager.placeOutgoingCall里,使用之前创建的Call实例的方法call.startCreateConnection, void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) { Preconditions.checkState(mCreateConnectionProcessor == null); mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this, phoneAccountRegistrar, mContext); mCreateConnectionProcessor.process(); } 这里会创建一个CreateConnectionProcessor实例,并调用其process,通过attemptNextPhoneAccount,调用到service.createConnection,其中service的类型是ConnectionServiceWrapper,它是IConnectionService的子类: private void attemptNextPhoneAccount() {… ConnectionServiceWrapper service = mRepository.getService( attempt.connectionManagerPhoneAccount.getComponentName());
service.createConnection(mCall, new Response(service)); …}
final class ConnectionServiceWrapper extends ServiceBinder<IConnectionService> {…}
这里又涉及到binder通信,ConnectionServiceWrapper是客户端,ConnectionService是服务端,MO起呼使用了createConnection这个接口方法。
在ConnectionService端,onCreateOutgoingConnection会被调用到,这个方法被TelephonyConnectionService重写,TelephonyConnectionService是ConnectionService的子类和最终要实例化的类,所以ConnectionService实例的onCreateOutgoingConnection方法在TelephonyConnectionService执行,这个方法代码较多,最终它会调用placeOutgoingConnection(), placeOutgoingConnection代码如下,它通过Phone.dial进行拨号,这个就是我们熟悉的流程了,在Android2.3、4.0、4.4都有这个拨号过程,之后将连接信息存放起来。 private void placeOutgoingConnection( TelephonyConnection connection, Phone phone, ConnectionRequest request) {… originalConnection = phone.dial(number, request.getVideoState()); … if (originalConnection == null) {… } else { connection.setOriginalConnection(originalConnection); } }
5.1.3 拨号请求(Phone)Phone.dial是一个接口,实现的原型在basePhone.dial(dialString),basePhone是GSMPhone的父类,实际上是一个实例是GSMPhone,所以最终调用GSMPhone.dial。代码中mCT则是GsmCallTracker的实例,它在GSMPhone创建的时候被创建,GSMPhone通过它来完成呼叫相关的处理。 public Connection dial (String dialString, UUSInfo uusInfo) throws CallStateException { GsmMmiCode mmi = GsmMmiCode.newFromDialString(networkPortion, this, mUiccApplication.get()); if (LOCAL_DEBUG) Cclog("dialing w/ mmi '" + mmi + "'..."); //MTK-END [mtk04070][111118][ALPS00093395]Add Cclog
if (mmi == null) { return mCT.dial(newDialString, uusInfo); } else if (mmi.isTemporaryModeCLIR()) { return mCT.dial(mmi.dialingNumber, 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; } }
在GsmCallTracker的dial方法中,会先将音频通道mute,再通过mCi.dial进行拨号,之后再将状态信息更新到应用。 synchronized Connection dial (String dialString, int clirMode, UUSInfo uusInfo) throws CallStateException { clearDisconnected(); … setMute(false); if (PhoneNumberUtils.isEmergencyNumber(dialString) cm.emergencyDial(ret.toString(), clirMode, uusInfo, obtainCompleteMessage(EVENT_DIAL_CALL_RESULT)); } else { cm.dial(ret.toString(), clirMode, uusInfo, obtainCompleteMessage(EVENT_DIAL_CALL_RESULT)); } updatePhoneState(); phone.notifyPreciseCallStateChanged(); }
其中cm是RIL.java的实例,它在PhoneFactory.makeDefaultPhone里被实例化,然后在GsmPhone实例创建的时候,引用被传递给GsmPhone,在GsmPhone的构造函数里,调用其父类PhoneBase的构造方法,给mCi赋值为RIL的引用值,之后GsmPhone就可以使用父类的mCi了,所以mCi.dial即RIL.dial()。 public static void makeDefaultPhone(Context context) { … int numPhones = TelephonyManager.getDefault().getPhoneCount(); sCommandsInterfaces = new RIL[numPhones]; for (int i = 0; i < numPhones; i++) { sCommandsInterfaces[i] = new RIL(context, networkModes[i], cdmaSubscription, i); }
for (int i = 0; i < numPhones; i++) { PhoneBase phone = null; int phoneType = TelephonyManager.getPhoneType(networkModes[i]); if (phoneType == PhoneConstants.PHONE_TYPE_GSM) { phone = new GSMPhone(context, sCommandsInterfaces[i], sPhoneNotifier, i); } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) { phone = new CDMALTEPhone(context, sCommandsInterfaces[i], sPhoneNotifier, i); } Rlog.i(LOG_TAG, "Creating Phone with type = " + phoneType + " sub = " + i);
sProxyPhones[i] = new PhoneProxy(phone); } …}
GSMPhone (Context context, CommandsInterface ci, PhoneNotifier notifier, boolean unitTestMode) { super("GSM", notifier, context, ci, unitTestMode); … mCi.setPhoneType(PhoneConstants.PHONE_TYPE_GSM); }
protected PhoneBase(String name, PhoneNotifier notifier, Context context, CommandsInterface ci, boolean unitTestMode, int phoneId) { … mCi = ci; } RIL.dial实现如下,封装一个RIL_REQUEST_DIAL类型的消息发送出去,将拨号请求发送给RILD, 如果是高通平台,RILD会通过QCRIL,以及QMI,和modem进行通信,完成呼叫起呼过程;如果是MTK平台,RILD使用AT指令向modem发送呼叫请求。 对于RILD之后的处理我们暂不进行分析了。(AT\QMI及modem这部分是feature phone很常规的呼叫流程过程,不是android特色的代码。) public void dial(String address, int clirMode, UUSInfo uusInfo, Message result) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);
rr.mp.writeString(address); rr.mp.writeInt(clirMode); rr.mp.writeInt(0); // UUS information is absent
if (uusInfo == null) { rr.mp.writeInt(0); // UUS information is absent } else { rr.mp.writeInt(1); // UUS information is present rr.mp.writeInt(uusInfo.getType()); rr.mp.writeInt(uusInfo.getDcs()); rr.mp.writeByteArray(uusInfo.getUserData()); } send(rr); } 在GsmCallTracker的dial方法中,MTK平台还封装了一个事件为EVENT_DIAL_CALL_RESULT消息,但RIL层响应拨号请求后,被自身的handler处理。(clearDisconnected()和canDial()清空过去的非连接状态的Connections,然后检查是否可以拨打电话。接着检查foregroundCall是否处于Active状态,若是则调用switchWaitingOrHoldingAndActive将它们切换到后台,调用fakeHoldForegroundBeforeDial将前台中的连接全部切换到后台,并且状态变为HOLDING。在进行这些前期检查和准备后,创建一个GsmConnection实例即pendingMO,检查传递过来的电话号码是否有效合法,若不合法则调用pollCallsWhenSafe(),目的是将其标为dropped;若合法则设置为非静音后,调用RIL.dial进行拨号。最后,更新Phone状态并通知给注册者。) 5. Android 5.0 呼叫流程5.1 MO call我们先给出MO起呼过程的示例流程图,然后结合该图做代码流程的分析: 5.1.1 拨号请求(CallActivity)我们从CallActivity来开始看MO呼叫流程,在他的processIntent里处理收到的呼叫intent,目前支持3种类型的起呼呼叫,之后processOutgoingCallIntent给CallReceiver发送一个广播,完成本阶段的处理。 private void processIntent(Intent intent) { // Ensure call intents are not processed on devices that are not capable of calling. if (!isVoiceCapable()) { return; }
verifyCallAction(intent); String action = intent.getAction();
if (Intent.ACTION_CALL.equals(action) || Intent.ACTION_CALL_PRIVILEGED.equals(action) || Intent.ACTION_CALL_EMERGENCY.equals(action)) { processOutgoingCallIntent(intent); } else if (TelecomManager.ACTION_INCOMING_CALL.equals(action)) { processIncomingCallIntent(intent); } }
CallReceiver是一个广播接收器,处理所有的来电和去电广播,具体来讲,处理3个广播,来电、拨号盘去电、SIM卡去电,这里分析一下拨号盘呼叫的流程,收到拨号盘的拨号请求后,调用processOutgoingCallIntent进行后续处理。 public void onReceive(Context context, Intent intent) { final boolean isUnknownCall = intent.getBooleanExtra(KEY_IS_UNKNOWN_CALL, false); final boolean isIncomingCall = intent.getBooleanExtra(KEY_IS_INCOMING_CALL, false); Log.i(this, "onReceive - isIncomingCall: %s isUnknownCall: %s", isIncomingCall, isUnknownCall);
if (isUnknownCall) { processUnknownCallIntent(intent); } else if (isIncomingCall) { processIncomingCallIntent(intent); } else { processOutgoingCallIntent(context, intent); } }
processOutgoingCallIntent实现: 1) 获取到CallsManager的实例,并调用其方法startOutgoingCall,这里会创建一个Call实例,并通过addCall进行相关的状态维护; 2) 再新建一个NewOutgoingCallIntentBroadcaster实例,调用其方法processIntent,改变intent的一些参数,使用mCallsManager.placeOutgoingCall发起呼叫,再调用broadcastIntent发一个广播消息ACTION_NEW_OUTGOING_CALL出去。
呼叫发起的主要流程在placeOutgoingCall里面处理; ACTION_NEW_OUTGOING_CALL广播的发送流程和接收器如下: private void broadcastIntent( Intent originalCallIntent, String number, boolean receiverRequired) { Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL); mContext.sendOrderedBroadcastAsUser( broadcastIntent, UserHandle.CURRENT, PERMISSION, receiverRequired ? new NewOutgoingCallBroadcastIntentReceiver() : null, null, // scheduler Activity.RESULT_OK, // initialCode number, // initialData: initial value for the result data (number to be modified) null); // initialExtras }
Intent.ACTION_NEW_OUTGOING_CALL的定义如下, @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_NEW_OUTGOING_CALL = "android.intent.action.NEW_OUTGOING_CALL";
通过AndroidManifest.xml,我们可以看出有两个receiver接收这个intent, ProcessOutgoingCallTest.java (packages\services\telephony\src\com\android\phone): UndemoteOutgoingCallReceiver.java (packages\apps\dialer\src\com\android\dialer\interactions):
继续分析呼叫的处理流程,在CallsManager.placeOutgoingCall里,使用之前创建的Call实例的方法call.startCreateConnection, void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) { Preconditions.checkState(mCreateConnectionProcessor == null); mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this, phoneAccountRegistrar, mContext); mCreateConnectionProcessor.process(); } 这里会创建一个CreateConnectionProcessor实例,并调用其process,通过attemptNextPhoneAccount,调用到service.createConnection,其中service的类型是ConnectionServiceWrapper,它是IConnectionService的子类: private void attemptNextPhoneAccount() {… ConnectionServiceWrapper service = mRepository.getService( attempt.connectionManagerPhoneAccount.getComponentName());
service.createConnection(mCall, new Response(service)); …}
final class ConnectionServiceWrapper extends ServiceBinder<IConnectionService> {…}
这里又涉及到binder通信,ConnectionServiceWrapper是客户端,ConnectionService是服务端,MO起呼使用了createConnection这个接口方法。
在ConnectionService端,onCreateOutgoingConnection会被调用到,这个方法被TelephonyConnectionService重写,TelephonyConnectionService是ConnectionService的子类和最终要实例化的类,所以ConnectionService实例的onCreateOutgoingConnection方法在TelephonyConnectionService执行,这个方法代码较多,最终它会调用placeOutgoingConnection(), placeOutgoingConnection代码如下,它通过Phone.dial进行拨号,这个就是我们熟悉的流程了,在Android2.3、4.0、4.4都有这个拨号过程,之后将连接信息存放起来。 private void placeOutgoingConnection( TelephonyConnection connection, Phone phone, ConnectionRequest request) {… originalConnection = phone.dial(number, request.getVideoState()); … if (originalConnection == null) {… } else { connection.setOriginalConnection(originalConnection); } }
Phone.dial是一个接口,实现的原型在basePhone.dial(dialString),basePhone是GSMPhone的父类,实际上是一个实例是GSMPhone,所以最终调用GSMPhone.dial。代码中mCT则是GsmCallTracker的实例,它在GSMPhone创建的时候被创建,GSMPhone通过它来完成呼叫相关的处理。 public Connection dial (String dialString, UUSInfo uusInfo) throws CallStateException { GsmMmiCode mmi = GsmMmiCode.newFromDialString(networkPortion, this, mUiccApplication.get()); if (LOCAL_DEBUG) Cclog("dialing w/ mmi '" + mmi + "'..."); //MTK-END [mtk04070][111118][ALPS00093395]Add Cclog
if (mmi == null) { return mCT.dial(newDialString, uusInfo); } else if (mmi.isTemporaryModeCLIR()) { return mCT.dial(mmi.dialingNumber, 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; } }
在GsmCallTracker的dial方法中,会先将音频通道mute,再通过mCi.dial进行拨号,之后再将状态信息更新到应用。 synchronized Connection dial (String dialString, int clirMode, UUSInfo uusInfo) throws CallStateException { clearDisconnected(); … setMute(false); if (PhoneNumberUtils.isEmergencyNumber(dialString) cm.emergencyDial(ret.toString(), clirMode, uusInfo, obtainCompleteMessage(EVENT_DIAL_CALL_RESULT)); } else { cm.dial(ret.toString(), clirMode, uusInfo, obtainCompleteMessage(EVENT_DIAL_CALL_RESULT)); } updatePhoneState(); phone.notifyPreciseCallStateChanged(); }
其中cm是RIL.java的实例,它在PhoneFactory.makeDefaultPhone里被实例化,然后在GsmPhone实例创建的时候,引用被传递给GsmPhone,在GsmPhone的构造函数里,调用其父类PhoneBase的构造方法,给mCi赋值为RIL的引用值,之后GsmPhone就可以使用父类的mCi了,所以mCi.dial即RIL.dial()。 public static void makeDefaultPhone(Context context) { … int numPhones = TelephonyManager.getDefault().getPhoneCount(); sCommandsInterfaces = new RIL[numPhones]; for (int i = 0; i < numPhones; i++) { sCommandsInterfaces[i] = new RIL(context, networkModes[i], cdmaSubscription, i); }
for (int i = 0; i < numPhones; i++) { PhoneBase phone = null; int phoneType = TelephonyManager.getPhoneType(networkModes[i]); if (phoneType == PhoneConstants.PHONE_TYPE_GSM) { phone = new GSMPhone(context, sCommandsInterfaces[i], sPhoneNotifier, i); } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) { phone = new CDMALTEPhone(context, sCommandsInterfaces[i], sPhoneNotifier, i); } Rlog.i(LOG_TAG, "Creating Phone with type = " + phoneType + " sub = " + i);
sProxyPhones[i] = new PhoneProxy(phone); } …}
GSMPhone (Context context, CommandsInterface ci, PhoneNotifier notifier, boolean unitTestMode) { super("GSM", notifier, context, ci, unitTestMode); … mCi.setPhoneType(PhoneConstants.PHONE_TYPE_GSM); }
protected PhoneBase(String name, PhoneNotifier notifier, Context context, CommandsInterface ci, boolean unitTestMode, int phoneId) { … mCi = ci; }
RIL.dial实现如下,封装一个RIL_REQUEST_DIAL类型的消息发送出去,将拨号请求发送给RILD, 如果是高通平台,RILD会通过QCRIL,以及QMI,和modem进行通信,完成呼叫起呼过程;如果是MTK平台,RILD使用AT指令向modem发送呼叫请求。 对于RILD之后的处理我们暂不进行分析了。(AT\QMI及modem这部分是feature phone很常规的呼叫流程过程,不是android特色的代码。) public void dial(String address, int clirMode, UUSInfo uusInfo, Message result) { RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);
rr.mp.writeString(address); rr.mp.writeInt(clirMode); rr.mp.writeInt(0); // UUS information is absent
if (uusInfo == null) { rr.mp.writeInt(0); // UUS information is absent } else { rr.mp.writeInt(1); // UUS information is present rr.mp.writeInt(uusInfo.getType()); rr.mp.writeInt(uusInfo.getDcs()); rr.mp.writeByteArray(uusInfo.getUserData()); } send(rr); } 在GsmCallTracker的dial方法中,MTK平台还封装了一个事件为EVENT_DIAL_CALL_RESULT消息,但RIL层响应拨号请求后,被自身的handler处理。(clearDisconnected()和canDial()清空过去的非连接状态的Connections,然后检查是否可以拨打电话。接着检查foregroundCall是否处于Active状态,若是则调用switchWaitingOrHoldingAndActive将它们切换到后台,调用fakeHoldForegroundBeforeDial将前台中的连接全部切换到后台,并且状态变为HOLDING。在进行这些前期检查和准备后,创建一个GsmConnection实例即pendingMO,检查传递过来的电话号码是否有效合法,若不合法则调用pollCallsWhenSafe(),目的是将其标为dropped;若合法则设置为非静音后,调用RIL.dial进行拨号。最后,更新Phone状态并通知给注册者。) |
|