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 呼叫流程--挂断相关推荐

  1. freeswitch系列四 通过实例学习sip协议的注册、呼叫、挂断流程

    1. 概述 本文通过tcpdump对真实环境里的软电话的注册.呼叫.挂断流程进行抓包,通过真实的例子学习SIP协议.本文主要包括以下方面: A. 详解软电话的注册时的SIP包和流程图 B. 详解软电话 ...

  2. VoLTE技术(含IMS注册/去注册流程、IMS呼叫流程、呼叫保持流程、二次协商过程)

    文章目录 VoLTE技术 IMS简介 SIP消息结构 IMS注册流程 IMS去注册流程 什么是Ghost Call 预留资源的呼叫流程 挂断电话流程(主动挂断.拒接) SDP协议,二次协商过程 呼叫保 ...

  3. 双卡双待手机[海信]挂断来电和指定卡去电实现

    海双卡双待手机[海信]挂断来电和指定卡去电实现的技术调研信双卡手机拨打电话时,在启动拨打电话时,传递一个参数给系统,这样系统的可以根据该参数判断使用指定的卡: Intent i = new Inten ...

  4. Android7.0 Phone应用源码分析(四) phone挂断流程分析

    电话挂断分为本地挂断和远程挂断,针对这两种情况各做分析 先来看下本地挂断电话的时序图:

  5. Android Telephony 9.0通话挂断连接处理机制(opt/Telephony)

    前言:今天看了一下通话断开处理流程,所以做一个笔记来记录一下今天的学习成果. 通话断开连接一般有两种应用场景 本地主动挂通话 远端断开通话连接 (这里还包括网络挂断和对方挂断) 先处理本地挂断 本地主 ...

  6. 关于sip呼叫成功后,对方立马挂断的情况说明

    最近在做SIP的接线员功能,类似于110这种,会有一些接线员提前上线:当外部人员拨打进来时,随机分配一个空闲的接线员来处理:若没有空闲的接线员,则系统自动发送一段系统正忙的声音给拨打方. 下面说说本人 ...

  7. Android5.0 netd架构流程

    原址 Android5.0之后,网络的功能实现完全转移到netd上面,主要包括ip,路由配置,dns代理设置,带宽控制和流量统计等. 下面是Netd框架示意图,NetworkManagerServic ...

  8. freeswitch呼叫流程分析

    今天翻文档时发现之前整理的关于freeswitch呼叫相关的内容,写成博文分享出来也方便我以后查阅. 整体结构图 FreeswitchCore 模块加载过程 freeswitch主程序初始化时会从mo ...

  9. android 关闭蓝牙打电话功能,Android蓝牙开发【八】hfp接听、挂断电话

    继续研究hfp相关功能.蓝牙耳机可以控制手机接听.拒接.挂断电话,拨打电话等功能.本文主要分析下起这些操作的大致流程. 在系统应用Bluetooth中com_android_bluetooth.cpp ...

最新文章

  1. python里面temp是啥-Python tempfile模块学习笔记(临时文件)
  2. html游戏源妈简单,最简单的HTML5游戏——贪吃蛇
  3. 掌握11项技能,你就是优秀的前端开发工程师
  4. CodeForces - 1287C Garland(贪心)
  5. SSM+mybatis单元测试
  6. linux进程监控自动重启,Linux监控进程,进程关闭自动重启方案
  7. 在线翻译英文html文件,copy html是什么意思
  8. Java基础学习总结(141)——Cron 表达式使用再总结
  9. Unity打开的文件是杂项文件的处理方法
  10. 重庆航天职业技术学院计算机系在哪个校区,2020年重庆航天职业技术学院地址在哪里...
  11. Java多商户商城源码 PC+小程序+APP源码+H5 B2B2C商城源码
  12. Phase2 Day3 List
  13. 3dmax如何使用模型快速切片命令
  14. 共享服务器协议,3.5.7 文件共享服务及SMB协议
  15. 无线AP人员定位实现调研以及方案编写
  16. 由save is not vaild without active transcation引发的问题
  17. 一般熟练盲打需要多久_学会盲打要多长时间,每天要练多长时间 盲打要练多久...
  18. 基于工业智能网关的PLC远程控制解决方案
  19. 算法问题:Smith数问题
  20. matlab 多子图共x轴

热门文章

  1. Agora声网-Uniapp拉流(Vue拉流)
  2. 让父母轻松享受智能生活 天猫精灵新品全测评
  3. WIN10使用内置Check Point Capsule
  4. 编程道路上的困难—怎么克服?
  5. Serialize的理解
  6. 亲自动手实现Python+pygame中国象棋游戏
  7. 手势识别Python-OpenCV
  8. 896. 最长上升子序列 II
  9. 【Baxter机器人末端轨迹坐标采集和轨迹还原】
  10. python期货程序化交易高手心得_位顶级高手谈期货心得