5.2 MO terminate
MO方主动挂断电话有不同的方式,常规的是在通话界面点击挂断按钮挂断电话,还有一种是通过硬件挂断,如POWER键或其他物理按键挂断,这个要看厂家自行的设计。
对于使用按钮的挂断流程图如下,
下面将对这两种情况做一个挂断流程的代码分析。
5.2.1 挂断请求(App)
5.2.1.1 挂断请求(Button)
如果通过按键挂断,则我们需要找到对应的按钮控件。
InCallActivity.java是用来显示通话界面的,在其OnCreate方法里加载布局文件incall_screen.xml,并通过initializeInCall,初始了CallCardFragment。
在CallCardFragment的onCreateView加载布局文件call_card_content.xml,这个布局里有一个按钮控件floating_end_call_action_button,这个就是用来挂断电话的。
public void onViewCreated(View view, Bundle savedInstanceState) {…
mFloatingActionButton = (ImageButton) view.findViewById(
R.id.floating_end_call_action_button);
mFloatingActionButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getPresenter().endCallClicked();
}
});
…}
其onClick方法里获取到CallCardPresenter的实例,调用endCallClicked方法,
public void endCallClicked() {
mPrimary.setState(Call.State.DISCONNECTING);
CallList.getInstance().onUpdate(mPrimary);
TelecomAdapter.getInstance().disconnectCall(mPrimary.getId());
}
之后又获取到TelecomAdapter的实例,调用其disconnectCall,这个方法里将再得到一个Call实例,并且这个Call是android.telecom.Call类型的,需要注意的是,它是通过Package目录下InCallUI应用的Call,来间接获取到framework目录下telecomm的Call类,有点抓狂的是下面call变量的定义类型和返回值类型是不一样的,这是多态性的应用吗?Telecomm Call是在InCallUI Call实例创建时传入的。
TelecomAdapter:
void disconnectCall(String callId) {
if (mPhone != null) {
getTelecommCallById(callId).disconnect();
} else {
Log.e(this, "error disconnectCall, mPhone is null");
}
}
private android.telecom.Call getTelecommCallById(String callId) {
final Call call = CallList.getInstance().getCallById(callId);
return call == null ? null : call.getTelecommCall();
}
之后就调用Telecomm Call的disconnect,它又调用InCallAdapter.disconnectCall,InCallAdapter也有两个文件,
InCallAdapter.java (frameworks\base\telecomm\java\android\telecom)
InCallAdapter.java (packages\services\telecomm\src\com\android\server\telecom)
我们使用framework目录下的那个,方法实现使用了mAdapter,这是一个IInCallAdapter的接口引用,既然是接口引用,那就又用到了binder,开始进程间通信,
private final IInCallAdapter mAdapter;
public void disconnectCall(String callId) {
try {
mAdapter.disconnectCall(callId);
} catch (RemoteException e) {
}
}
Binder的服务端就在package目录下的InCallAdapter文件中,
class InCallAdapter extends IInCallAdapter.Stub {
public void disconnectCall(String callId) {
Log.v(this, "disconnectCall: %s", callId);
if (mCallIdMapper.isValidCallId(callId)) {
mHandler.obtainMessage(MSG_DISCONNECT_CALL, callId).sendToTarget();
}
}
}
服务端发送一个消息MSG_DISCONNECT_CALL,其内部类InCallAdapterHandler会处理这个消息,
case MSG_DISCONNECT_CALL:
call = mCallIdMapper.getCall(msg.obj);
if (call != null) {
mCallsManager.disconnectCall(call);
} else {
Log.w(this, "disconnectCall, unknown call id: %s", msg.obj);
}
break;
CallsManager再调用到一个Call实例,进行进一步的处理。后面的章节将分析这个Call的挂断流程。下一节将分析物理按键挂断的初期流程,因为在后续两者会共用部分流程。
void disconnectCall(Call call) {
Log.v(this, "disconnectCall %s", call);
if (!mCalls.contains(call)) {
Log.w(this, "Unknown call (%s) asked to disconnect", call);
} else {
mLocallyDisconnectingCalls.add(call);
call.disconnect();
}
}
本小节调用流程如下:
5.2.1.2 挂断请求(Key)
如果是物理键挂断电话,则在PhoneWindowManager.java (frameworks\base\policy\src\com \android\internal\policy\impl)这个文件里,方法interceptKeyBeforeQueueing会在按键事件通知到控件前将其捕获,
如果是KEYCODE_ENDCALL,在其按下状态,调用telecomManager.endCall挂断电话,
case KeyEvent.KEYCODE_ENDCALL: {
result &= ~ACTION_PASS_TO_USER;
if (down) {
TelecomManager telecomManager = getTelecommService();
boolean hungUp = false;
if (telecomManager != null) {
hungUp = telecomManager.endCall();
}
interceptPowerKeyDown(!interactive || hungUp);
}
如果是KEYCODE_POWER键,则在其按下状态,如果是来电响铃,则静音,如果是通话中,也是调用telecomManager.endCall挂断电话,
case KeyEvent.KEYCODE_POWER: {
result &= ~ACTION_PASS_TO_USER;
if (down) {
TelecomManager telecomManager = getTelecommService();
boolean hungUp = false;
if (telecomManager != null) {
if (telecomManager.isRinging()) {
// Pressing Power while there's a ringing incoming
// call should silence the ringer.
telecomManager.silenceRinger();
} else if ((mIncallPowerBehavior
& Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
&& telecomManager.isInCall() && interactive) {
// Otherwise, if "Power button ends call" is enabled,
// the Power button will hang up any current active call.
hungUp = telecomManager.endCall();
}
}
TelecomManager.endCall通过getTelecomService获取到ITelecomService接口,远程调用调用的endCall,它发送一个MSG_END_CALL消息给内部类MainThreadHandler,处理消息的方法是endCallInternal,这里会根据当前的呼叫状态,调用Call的接口去挂断电话,
private boolean endCallInternal() {…
Call call = mCallsManager.getForegroundCall();
…
if (call != null) {
if (call.getState() == CallState.RINGING) {
call.reject(false /* rejectWithMessage */, null);
} else {
call.disconnect();
}
return true;
}
return false;
}
5.2.2 挂断请求(Telecomm)
需要注意的是,在系统里面有很多Call.java文件,我们要知道使用的是哪一个Call类,根据当前文件的包名com.android.server.telecom以及导入的类,我们知道是用的是Call.java (packages\services\telecomm\src\com\android\server\telecom),其disconnect()方法又调用
mConnectionService.disconnect(this)方法来挂断电话,mConnectionService类型是ConnectionServiceWrapper,根据前面的分析我们知道,它是IConnectionService接口的客户端,其调用服务端TelephonyConnectionService的父类ConnectionService的远程接口,代码实现如下,
private void disconnect(String callId) {
Log.d(this, "disconnect %s", callId);
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "disconnect").onDisconnect();
} else {
findConferenceForAction(callId, "disconnect").onDisconnect();
}
}
在findConnectionForAction里返回一个Connection的实例,如果通过callId找不到已经存在的实例,则要通过getNullConnection创建一个实例,这里又两点需要注意,
1)Connection类存在多个,我们要能识别出使用的是哪个类文件,这个方法和前面找Call类的方法一下,通过包名和import的类名找到,是android.telecom.Connection这个类;
2)Connection是一个抽象类,但在下面的代码里面我们看到代码里面使用了new来创建一个抽象类,根据我们的常识,抽象类是不能被实例化的。所以我们需要注意到Connection后面的大括号{},它表示用匿名类的方式重写了抽象类,只不过类里面没有重写任何方法,这样就“实现”了一个抽象类的实例化,在我们需要的时候,可以指向真正要操作的实例。
private Connection findConnectionForAction(String callId, String action) {
if (mConnectionById.containsKey(callId)) {
return mConnectionById.get(callId);
}
Log.w(this, "%s - Cannot find Connection %s", action, callId);
return getNullConnection();
}
static synchronized Connection getNullConnection() {
if (sNullConnection == null) {
sNullConnection = new Connection() {};
}
return sNullConnection;
public abstract class Connection {
…
}
实际上,根据包的包含关系,Connection有一个子类指向的实例对象是TelephonyConnection,(这里有多个Connection以及其子类,弄清这些类的继承关系和调用关系是很重要的,否则代码分析会误入歧途!!!),
package com.android.services.telephony;
import android.telecom.Connection;
abstract class TelephonyConnection extends Connection {
…
public void onDisconnect() {
Log.v(this, "onDisconnect");
hangup(android.telephony.DisconnectCause.LOCAL);
}…
}
private com.android.internal.telephony.Connection mOriginalConnection;
protected void hangup(int telephonyDisconnectCode) {
if (mOriginalConnection != null) {
if (isValidRingingCall()) {
Call call = getCall();
if (call != null) {
call.hangup();
}
} else {
mOriginalConnection.hangup();
}
}
TelephonyConnection又是一个抽象对象,其子类是GsmConnection,GsmConnection文件有两个,当前是GsmConnection.java (packages\services\telephony\src\com\android\services\ telephony),看清这点很重要。
package com.android.services.telephony;
import com.android.internal.telephony.Connection;
final class GsmConnection extends TelephonyConnection {
GsmConnection(Connection connection) {
super(connection);
}
…}
在GsmConnection类创建的时候,,有一个传入对象Connection,此时必须看清Connection是com.android.internal.telephony.Connection这个包下面的类,对应路径在(frameworks\opt\telephony\src\java\com\android\internal\telephony);所以上面的Package目录下的Connection的onDisconnect调用自身的hangup,再调用到framework telephony目录下的Connection.hangup,其关联方法就是Packages目录下的GsmConnection创建时完成的。
Framework目录下的Connection也是一个抽象类,其子类是GsmConnection.java (frameworks\opt\telephony\src\java\com\android\internal\telephony\gsm)这个目录下的类文件,GsmConnection. hangup代码如下,其中mOwner是GsmCallTracker的实例引用,
package com.android.internal.telephony.gsm;
import com.android.internal.telephony.*;//Connection类所在包
public class GsmConnection extends Connection {
…
public void hangup() throws CallStateException {
if (!mDisconnected) {
mOwner.hangup(this);
} else {
throw new CallStateException ("disconnected");
}
}
…
本小段的调用流程如下,
5.2.3 挂断请求(Phone&RIL)
如上,GsmConnection. hangup调用GsmCallTracker的hangup,再进一步调用mCi.hangupConnection,如前所述,mCi是RIL的实例引用,所以hangupConnection代码如下,它向下发送挂断请求:
hangupConnection (int gsmIndex, Message result) {
if (RILJ_LOGD) riljLog("hangupConnection: gsmIndex=" + gsmIndex);
RILRequest rr = RILRequest.obtain(RIL_REQUEST_HANGUP, result);
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " +
gsmIndex);
rr.mParcel.writeInt(1);
rr.mParcel.writeInt(gsmIndex);
send(rr);
}
RIL封装了一个RIL_REQUEST_HANGUP类型的消息,作为处理response消息的判断标识,当RIL收到RILD的响应时,它调用processResponse函数,根据类型RESPONSE_SOLICITED,执行processSolicited,这里就通过参数获取到之前封装的rr,再根据RIL_REQUEST_HANGUP处理相关case,最后将消息发送给rr. result对应的handler处理,也就是hangupConnection传递进来的Message参数。
再回头看GsmCallTracker.hangup,其第二个参数是obtainCompleteMessage生成的一个EVENT_OPERATION_COMPLETE消息,
mCi.hangupConnection (conn.getGSMIndex(), obtainCompleteMessage());
它会被发给其自身的handleMessage处理(因为GsmCallTracker本身是一个handler),处理函数是operationComplete, 这里进行本地变量更新,如果mPendingOperations为0,则会再封装一个类型为EVENT_POLL_CALLS_RESULT的Message,并调用RIL的getCurrentCalls,进行当前通话的查询,
private void operationComplete() {
mPendingOperations--;
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,"GsmCallTracker.pendingOperations < 0");
mPendingOperations = 0;
}
}
RIL层再将消息封装一下,类型为RIL_REQUEST_GET_CURRENT_CALLS,然后发送给RILD,当RILJ收到响应消息时,在processSolicited处理RIL_REQUEST_GET_CURRENT_CALLS消息是,使用responseCallList创建一个呼叫列表,然后将消息再发给GsmCallTracker处理,并根据之前的消息类型EVENT_POLL_CALLS_RESULT,调用到对应的处理函数handlePollCalls,进行呼叫列表和连接状态的更新,这个流程比较复杂,暂不深入分析。
handleMessage (Message msg) {
…
case EVENT_POLL_CALLS_RESULT:
ar = (AsyncResult)msg.obj;
if (msg == mLastRelevantPoll) {
if (DBG_POLL) log(
"handle EVENT_POLL_CALL_RESULT: set needsPoll=F");
mNeedsPoll = false;
mLastRelevantPoll = null;
handlePollCalls((AsyncResult)msg.obj);
}
break;
最后通过updatePhoneState将挂起状态逐步通知到应用。
本小段的调用流程如下,
5.2 MO terminate
MO方主动挂断电话有不同的方式,常规的是在通话界面点击挂断按钮挂断电话,还有一种是通过硬件挂断,如POWER键或其他物理按键挂断,这个要看厂家自行的设计。
对于使用按钮的挂断流程图如下,
下面将对这两种情况做一个挂断流程的代码分析。
5.2.1 挂断请求(App)
5.2.1.1 挂断请求(Button)
如果通过按键挂断,则我们需要找到对应的按钮控件。
InCallActivity.java是用来显示通话界面的,在其OnCreate方法里加载布局文件incall_screen.xml,并通过initializeInCall,初始了CallCardFragment。
在CallCardFragment的onCreateView加载布局文件call_card_content.xml,这个布局里有一个按钮控件floating_end_call_action_button,这个就是用来挂断电话的。
public void onViewCreated(View view, Bundle savedInstanceState) {…
mFloatingActionButton = (ImageButton) view.findViewById(
R.id.floating_end_call_action_button);
mFloatingActionButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getPresenter().endCallClicked();
}
});
…}
其onClick方法里获取到CallCardPresenter的实例,调用endCallClicked方法,
public void endCallClicked() {
mPrimary.setState(Call.State.DISCONNECTING);
CallList.getInstance().onUpdate(mPrimary);
TelecomAdapter.getInstance().disconnectCall(mPrimary.getId());
}
之后又获取到TelecomAdapter的实例,调用其disconnectCall,这个方法里将再得到一个Call实例,并且这个Call是android.telecom.Call类型的,需要注意的是,它是通过Package目录下InCallUI应用的Call,来间接获取到framework目录下telecomm的Call类,有点抓狂的是下面call变量的定义类型和返回值类型是不一样的,这是多态性的应用吗?Telecomm Call是在InCallUI Call实例创建时传入的。
TelecomAdapter:
void disconnectCall(String callId) {
if (mPhone != null) {
getTelecommCallById(callId).disconnect();
} else {
Log.e(this, "error disconnectCall, mPhone is null");
}
}
private android.telecom.Call getTelecommCallById(String callId) {
final Call call = CallList.getInstance().getCallById(callId);
return call == null ? null : call.getTelecommCall();
}
之后就调用Telecomm Call的disconnect,它又调用InCallAdapter.disconnectCall,InCallAdapter也有两个文件,
InCallAdapter.java (frameworks\base\telecomm\java\android\telecom)
InCallAdapter.java (packages\services\telecomm\src\com\android\server\telecom)
我们使用framework目录下的那个,方法实现使用了mAdapter,这是一个IInCallAdapter的接口引用,既然是接口引用,那就又用到了binder,开始进程间通信,
private final IInCallAdapter mAdapter;
public void disconnectCall(String callId) {
try {
mAdapter.disconnectCall(callId);
} catch (RemoteException e) {
}
}
Binder的服务端就在package目录下的InCallAdapter文件中,
class InCallAdapter extends IInCallAdapter.Stub {
public void disconnectCall(String callId) {
Log.v(this, "disconnectCall: %s", callId);
if (mCallIdMapper.isValidCallId(callId)) {
mHandler.obtainMessage(MSG_DISCONNECT_CALL, callId).sendToTarget();
}
}
}
服务端发送一个消息MSG_DISCONNECT_CALL,其内部类InCallAdapterHandler会处理这个消息,
case MSG_DISCONNECT_CALL:
call = mCallIdMapper.getCall(msg.obj);
if (call != null) {
mCallsManager.disconnectCall(call);
} else {
Log.w(this, "disconnectCall, unknown call id: %s", msg.obj);
}
break;
CallsManager再调用到一个Call实例,进行进一步的处理。后面的章节将分析这个Call的挂断流程。下一节将分析物理按键挂断的初期流程,因为在后续两者会共用部分流程。
void disconnectCall(Call call) {
Log.v(this, "disconnectCall %s", call);
if (!mCalls.contains(call)) {
Log.w(this, "Unknown call (%s) asked to disconnect", call);
} else {
mLocallyDisconnectingCalls.add(call);
call.disconnect();
}
}
本小节调用流程如下:
5.2.1.2 挂断请求(Key)
如果是物理键挂断电话,则在PhoneWindowManager.java (frameworks\base\policy\src\com \android\internal\policy\impl)这个文件里,方法interceptKeyBeforeQueueing会在按键事件通知到控件前将其捕获,
如果是KEYCODE_ENDCALL,在其按下状态,调用telecomManager.endCall挂断电话,
case KeyEvent.KEYCODE_ENDCALL: {
result &= ~ACTION_PASS_TO_USER;
if (down) {
TelecomManager telecomManager = getTelecommService();
boolean hungUp = false;
if (telecomManager != null) {
hungUp = telecomManager.endCall();
}
interceptPowerKeyDown(!interactive || hungUp);
}
如果是KEYCODE_POWER键,则在其按下状态,如果是来电响铃,则静音,如果是通话中,也是调用telecomManager.endCall挂断电话,
case KeyEvent.KEYCODE_POWER: {
result &= ~ACTION_PASS_TO_USER;
if (down) {
TelecomManager telecomManager = getTelecommService();
boolean hungUp = false;
if (telecomManager != null) {
if (telecomManager.isRinging()) {
// Pressing Power while there's a ringing incoming
// call should silence the ringer.
telecomManager.silenceRinger();
} else if ((mIncallPowerBehavior
& Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
&& telecomManager.isInCall() && interactive) {
// Otherwise, if "Power button ends call" is enabled,
// the Power button will hang up any current active call.
hungUp = telecomManager.endCall();
}
}
TelecomManager.endCall通过getTelecomService获取到ITelecomService接口,远程调用调用的endCall,它发送一个MSG_END_CALL消息给内部类MainThreadHandler,处理消息的方法是endCallInternal,这里会根据当前的呼叫状态,调用Call的接口去挂断电话,
private boolean endCallInternal() {…
Call call = mCallsManager.getForegroundCall();
…
if (call != null) {
if (call.getState() == CallState.RINGING) {
call.reject(false /* rejectWithMessage */, null);
} else {
call.disconnect();
}
return true;
}
return false;
}
5.2.2 挂断请求(Telecomm)
需要注意的是,在系统里面有很多Call.java文件,我们要知道使用的是哪一个Call类,根据当前文件的包名com.android.server.telecom以及导入的类,我们知道是用的是Call.java (packages\services\telecomm\src\com\android\server\telecom),其disconnect()方法又调用
mConnectionService.disconnect(this)方法来挂断电话,mConnectionService类型是ConnectionServiceWrapper,根据前面的分析我们知道,它是IConnectionService接口的客户端,其调用服务端TelephonyConnectionService的父类ConnectionService的远程接口,代码实现如下,
private void disconnect(String callId) {
Log.d(this, "disconnect %s", callId);
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "disconnect").onDisconnect();
} else {
findConferenceForAction(callId, "disconnect").onDisconnect();
}
}
在findConnectionForAction里返回一个Connection的实例,如果通过callId找不到已经存在的实例,则要通过getNullConnection创建一个实例,这里又两点需要注意,
1)Connection类存在多个,我们要能识别出使用的是哪个类文件,这个方法和前面找Call类的方法一下,通过包名和import的类名找到,是android.telecom.Connection这个类;
2)Connection是一个抽象类,但在下面的代码里面我们看到代码里面使用了new来创建一个抽象类,根据我们的常识,抽象类是不能被实例化的。所以我们需要注意到Connection后面的大括号{},它表示用匿名类的方式重写了抽象类,只不过类里面没有重写任何方法,这样就“实现”了一个抽象类的实例化,在我们需要的时候,可以指向真正要操作的实例。
private Connection findConnectionForAction(String callId, String action) {
if (mConnectionById.containsKey(callId)) {
return mConnectionById.get(callId);
}
Log.w(this, "%s - Cannot find Connection %s", action, callId);
return getNullConnection();
}
static synchronized Connection getNullConnection() {
if (sNullConnection == null) {
sNullConnection = new Connection() {};
}
return sNullConnection;
public abstract class Connection {
…
}
实际上,根据包的包含关系,Connection有一个子类指向的实例对象是TelephonyConnection,(这里有多个Connection以及其子类,弄清这些类的继承关系和调用关系是很重要的,否则代码分析会误入歧途!!!),
package com.android.services.telephony;
import android.telecom.Connection;
abstract class TelephonyConnection extends Connection {
…
public void onDisconnect() {
Log.v(this, "onDisconnect");
hangup(android.telephony.DisconnectCause.LOCAL);
}…
}
private com.android.internal.telephony.Connection mOriginalConnection;
protected void hangup(int telephonyDisconnectCode) {
if (mOriginalConnection != null) {
if (isValidRingingCall()) {
Call call = getCall();
if (call != null) {
call.hangup();
}
} else {
mOriginalConnection.hangup();
}
}
TelephonyConnection又是一个抽象对象,其子类是GsmConnection,GsmConnection文件有两个,当前是GsmConnection.java (packages\services\telephony\src\com\android\services\ telephony),看清这点很重要。
package com.android.services.telephony;
import com.android.internal.telephony.Connection;
final class GsmConnection extends TelephonyConnection {
GsmConnection(Connection connection) {
super(connection);
}
…}
在GsmConnection类创建的时候,,有一个传入对象Connection,此时必须看清Connection是com.android.internal.telephony.Connection这个包下面的类,对应路径在(frameworks\opt\telephony\src\java\com\android\internal\telephony);所以上面的Package目录下的Connection的onDisconnect调用自身的hangup,再调用到framework telephony目录下的Connection.hangup,其关联方法就是Packages目录下的GsmConnection创建时完成的。
Framework目录下的Connection也是一个抽象类,其子类是GsmConnection.java (frameworks\opt\telephony\src\java\com\android\internal\telephony\gsm)这个目录下的类文件,GsmConnection. hangup代码如下,其中mOwner是GsmCallTracker的实例引用,
package com.android.internal.telephony.gsm;
import com.android.internal.telephony.*;//Connection类所在包
public class GsmConnection extends Connection {
…
public void hangup() throws CallStateException {
if (!mDisconnected) {
mOwner.hangup(this);
} else {
throw new CallStateException ("disconnected");
}
}
…
本小段的调用流程如下,
5.2.3 挂断请求(Phone&RIL)
如上,GsmConnection. hangup调用GsmCallTracker的hangup,再进一步调用mCi.hangupConnection,如前所述,mCi是RIL的实例引用,所以hangupConnection代码如下,它向下发送挂断请求:
hangupConnection (int gsmIndex, Message result) {
if (RILJ_LOGD) riljLog("hangupConnection: gsmIndex=" + gsmIndex);
RILRequest rr = RILRequest.obtain(RIL_REQUEST_HANGUP, result);
if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " +
gsmIndex);
rr.mParcel.writeInt(1);
rr.mParcel.writeInt(gsmIndex);
send(rr);
}
RIL封装了一个RIL_REQUEST_HANGUP类型的消息,作为处理response消息的判断标识,当RIL收到RILD的响应时,它调用processResponse函数,根据类型RESPONSE_SOLICITED,执行processSolicited,这里就通过参数获取到之前封装的rr,再根据RIL_REQUEST_HANGUP处理相关case,最后将消息发送给rr. result对应的handler处理,也就是hangupConnection传递进来的Message参数。
再回头看GsmCallTracker.hangup,其第二个参数是obtainCompleteMessage生成的一个EVENT_OPERATION_COMPLETE消息,
mCi.hangupConnection (conn.getGSMIndex(), obtainCompleteMessage());
它会被发给其自身的handleMessage处理(因为GsmCallTracker本身是一个handler),处理函数是operationComplete, 这里进行本地变量更新,如果mPendingOperations为0,则会再封装一个类型为EVENT_POLL_CALLS_RESULT的Message,并调用RIL的getCurrentCalls,进行当前通话的查询,
private void operationComplete() {
mPendingOperations--;
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,"GsmCallTracker.pendingOperations < 0");
mPendingOperations = 0;
}
}
RIL层再将消息封装一下,类型为RIL_REQUEST_GET_CURRENT_CALLS,然后发送给RILD,当RILJ收到响应消息时,在processSolicited处理RIL_REQUEST_GET_CURRENT_CALLS消息是,使用responseCallList创建一个呼叫列表,然后将消息再发给GsmCallTracker处理,并根据之前的消息类型EVENT_POLL_CALLS_RESULT,调用到对应的处理函数handlePollCalls,进行呼叫列表和连接状态的更新,这个流程比较复杂,暂不深入分析。
handleMessage (Message msg) {
…
case EVENT_POLL_CALLS_RESULT:
ar = (AsyncResult)msg.obj;
if (msg == mLastRelevantPoll) {
if (DBG_POLL) log(
"handle EVENT_POLL_CALL_RESULT: set needsPoll=F");
mNeedsPoll = false;
mLastRelevantPoll = null;
handlePollCalls((AsyncResult)msg.obj);
}
break;
最后通过updatePhoneState将挂起状态逐步通知到应用。
本小段的调用流程如下,