Android5.0 呼叫流程--挂断
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将挂起状态逐步通知到应用。
本小段的调用流程如下,
Android5.0 呼叫流程--挂断相关推荐
- freeswitch系列四 通过实例学习sip协议的注册、呼叫、挂断流程
1. 概述 本文通过tcpdump对真实环境里的软电话的注册.呼叫.挂断流程进行抓包,通过真实的例子学习SIP协议.本文主要包括以下方面: A. 详解软电话的注册时的SIP包和流程图 B. 详解软电话 ...
- VoLTE技术(含IMS注册/去注册流程、IMS呼叫流程、呼叫保持流程、二次协商过程)
文章目录 VoLTE技术 IMS简介 SIP消息结构 IMS注册流程 IMS去注册流程 什么是Ghost Call 预留资源的呼叫流程 挂断电话流程(主动挂断.拒接) SDP协议,二次协商过程 呼叫保 ...
- 双卡双待手机[海信]挂断来电和指定卡去电实现
海双卡双待手机[海信]挂断来电和指定卡去电实现的技术调研信双卡手机拨打电话时,在启动拨打电话时,传递一个参数给系统,这样系统的可以根据该参数判断使用指定的卡: Intent i = new Inten ...
- Android7.0 Phone应用源码分析(四) phone挂断流程分析
电话挂断分为本地挂断和远程挂断,针对这两种情况各做分析 先来看下本地挂断电话的时序图:
- Android Telephony 9.0通话挂断连接处理机制(opt/Telephony)
前言:今天看了一下通话断开处理流程,所以做一个笔记来记录一下今天的学习成果. 通话断开连接一般有两种应用场景 本地主动挂通话 远端断开通话连接 (这里还包括网络挂断和对方挂断) 先处理本地挂断 本地主 ...
- 关于sip呼叫成功后,对方立马挂断的情况说明
最近在做SIP的接线员功能,类似于110这种,会有一些接线员提前上线:当外部人员拨打进来时,随机分配一个空闲的接线员来处理:若没有空闲的接线员,则系统自动发送一段系统正忙的声音给拨打方. 下面说说本人 ...
- Android5.0 netd架构流程
原址 Android5.0之后,网络的功能实现完全转移到netd上面,主要包括ip,路由配置,dns代理设置,带宽控制和流量统计等. 下面是Netd框架示意图,NetworkManagerServic ...
- freeswitch呼叫流程分析
今天翻文档时发现之前整理的关于freeswitch呼叫相关的内容,写成博文分享出来也方便我以后查阅. 整体结构图 FreeswitchCore 模块加载过程 freeswitch主程序初始化时会从mo ...
- android 关闭蓝牙打电话功能,Android蓝牙开发【八】hfp接听、挂断电话
继续研究hfp相关功能.蓝牙耳机可以控制手机接听.拒接.挂断电话,拨打电话等功能.本文主要分析下起这些操作的大致流程. 在系统应用Bluetooth中com_android_bluetooth.cpp ...
最新文章
- python里面temp是啥-Python tempfile模块学习笔记(临时文件)
- html游戏源妈简单,最简单的HTML5游戏——贪吃蛇
- 掌握11项技能,你就是优秀的前端开发工程师
- CodeForces - 1287C Garland(贪心)
- SSM+mybatis单元测试
- linux进程监控自动重启,Linux监控进程,进程关闭自动重启方案
- 在线翻译英文html文件,copy html是什么意思
- Java基础学习总结(141)——Cron 表达式使用再总结
- Unity打开的文件是杂项文件的处理方法
- 重庆航天职业技术学院计算机系在哪个校区,2020年重庆航天职业技术学院地址在哪里...
- Java多商户商城源码 PC+小程序+APP源码+H5 B2B2C商城源码
- Phase2 Day3 List
- 3dmax如何使用模型快速切片命令
- 共享服务器协议,3.5.7 文件共享服务及SMB协议
- 无线AP人员定位实现调研以及方案编写
- 由save is not vaild without active transcation引发的问题
- 一般熟练盲打需要多久_学会盲打要多长时间,每天要练多长时间 盲打要练多久...
- 基于工业智能网关的PLC远程控制解决方案
- 算法问题:Smith数问题
- matlab 多子图共x轴