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

CallCardFragmentonCreateView加载布局文件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实例,并且这个Callandroid.telecom.Call类型的,需要注意的是,它是通过Package目录下InCallUI应用的Call,来间接获取到framework目录下telecommCall类,有点抓狂的是下面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  Calldisconnect,它又调用InCallAdapter.disconnectCallInCallAdapter也有两个文件,

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创建一个实例,这里又两点需要注意,

1Connection类存在多个,我们要能识别出使用的是哪个类文件,这个方法和前面找Call类的方法一下,通过包名和import的类名找到,是android.telecom.Connection这个类;

2Connection是一个抽象类,但在下面的代码里面我们看到代码里面使用了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又是一个抽象对象,其子类是GsmConnectionGsmConnection文件有两个,当前是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,此时必须看清Connectioncom.android.internal.telephony.Connection这个包下面的类,对应路径在(frameworks\opt\telephony\src\java\com\android\internal\telephony);所以上面的Package目录下的ConnectiononDisconnect调用自身的hangup,再调用到framework telephony目录下的Connection.hangup,其关联方法就是Packages目录下的GsmConnection创建时完成的。

 

Framework目录下的Connection也是一个抽象类,其子类是GsmConnection.java (frameworks\opt\telephony\src\java\com\android\internal\telephony\gsm)这个目录下的类文件,GsmConnection. hangup代码如下,其中mOwnerGsmCallTracker的实例引用,

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调用GsmCallTrackerhangup,再进一步调用mCi.hangupConnection,如前所述,mCiRIL的实例引用,所以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    这里进行本地变量更新,如果mPendingOperations0,则会再封装一个类型为EVENT_POLL_CALLS_RESULTMessage,并调用RILgetCurrentCalls,进行当前通话的查询,

    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将挂起状态逐步通知到应用。

 

本小段的调用流程如下,


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值