android sip通话实现流程分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhanxiaoqi/article/details/68066057

sip协议的核心是SipManager,我在前篇博客中已经讲述了sip通话的代码建立过程,点击打开链接

会创建一个SipManager实例,它的构造代码如下:

public static SipManager newInstance(Context context) {
        return (isApiSupported(context) ? new SipManager(context) : null);
    }


private SipManager(Context context) {
        mContext = context;
        createSipService();
    }


private void createSipService() {
        IBinder b = ServiceManager.getService(Context.SIP_SERVICE); //获取sip服务的代理类,返回一个BinderProxy对象.
        mSipService = ISipService.Stub.asInterface(b);
    }

从上面代码可以看出,SipManager的构造过程中会去获取sip服务(实际就是SipService)的代理类对象,SipManager的很多方法最终是调用到这个服务端的相应方法来实现的.

那么下面我们先看看SipService是如何注册到ServiceManager中去的.

它的注册与一般服务的注册不同,绝大多数Service都是直接在SysetmServer进程中进行注册,而SipService是在TeleService apk中注册,也就是在phone进程中注册的(部分phone相关的服务也是在phone进程中注册的),虽然最终都是注册到ServiceManager中,但这个服务是运行在phone进程中的.

具体实现代码在PhoneGlobals.java中.

Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            PhoneConstants.State phoneState;
            switch (msg.what) {
                // Starts the SIP service. It's a no-op if SIP API is not supported
                // on the deivce.
                // TODO: Having the phone process host the SIP service is only
                // temporary. Will move it to a persistent communication process
                // later.
                case EVENT_START_SIP_SERVICE:
                    SipService.start(getApplicationContext());
                    break;

..//

}

在PhoneGlobals的onCreate()中

if (mCM == null) {

....//

 mHandler.sendEmptyMessage(EVENT_START_SIP_SERVICE);

}


SipService的start方法如下:

    public static void start(Context context) {
        if (SipManager.isApiSupported(context)) {
            ServiceManager.addService("sip", new SipService(context));
            context.sendBroadcast(new Intent(SipManager.ACTION_SIP_SERVICE_UP));
            if (DBG) slog("start:");
        }
    }

这样就将一个SipService注册到ServiceManager中了.

我在前面博客已分析过,拨打一个网络电话是调用SipManager的makeAudioCall()方法实现的.

下面我以拨打一个网络电话为例进行说明具体实现流程:

public SipAudioCall makeAudioCall(SipProfile localProfile,
            SipProfile peerProfile, SipAudioCall.Listener listener, int timeout)
            throws SipException {
        if (!isVoipSupported(mContext)) {
            throw new SipException("VOIP API is not supported");
        }
        SipAudioCall call = new SipAudioCall(mContext, localProfile);
        call.setListener(listener);
        SipSession s = createSipSession(localProfile, null);
        call.makeCall(peerProfile, s, timeout);
        return call;
    }

该方法中首先创建了一个SipAudioCall对象,然后调用createSipSession()创建一个sip会话SipSession对象.

public SipSession createSipSession(SipProfile localProfile,
            SipSession.Listener listener) throws SipException {
        try {
            ISipSession s = mSipService.createSession(localProfile, null);
            if (s == null) {
                throw new SipException(
                        "Failed to create SipSession; network unavailable?");
            }
            return new SipSession(s, listener);
        } catch (RemoteException e) {
            throw new SipException("createSipSession()", e);
        }
    }


mSipService.createSession(localProfile, null);对应实现在SipService中,代码如下:

 public synchronized ISipSession createSession(SipProfile localProfile,
            ISipSessionListener listener) {
        if (DBG) log("createSession: profile" + localProfile);
        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.USE_SIP, null);
        localProfile.setCallingUid(Binder.getCallingUid());
        if (mNetworkType == -1) {
            if (DBG) log("createSession: mNetworkType==-1 ret=null");
            return null;
        }
        try {
            SipSessionGroupExt group = createGroup(localProfile);
            return group.createSession(listener);
        } catch (SipException e) {
            if (DBG) loge("createSession;", e);
            return null;
        }
    }


    public ISipSession createSession(ISipSessionListener listener) {
        return (isClosed() ? null : new SipSessionImpl(listener));
    }

根据上面代码分析,mSipService.createSession()在服务端创建了一个SipSessionImpl对象.

我们再看SipAudioCall的makeCall()方法实现.

public void makeCall(SipProfile peerProfile, SipSession sipSession,
            int timeout) throws SipException {
        if (DBG) log("makeCall: " + peerProfile + " session=" + sipSession + " timeout=" + timeout);
        if (!SipManager.isVoipSupported(mContext)) {
            throw new SipException("VOIP API is not supported");
        }


        synchronized (this) {
            mSipSession = sipSession;
            try {
                mAudioStream = new AudioStream(InetAddress.getByName(
                        getLocalIp()));
                sipSession.setListener(createListener());
                sipSession.makeCall(peerProfile, createOffer().encode(),
                        timeout);
            } catch (IOException e) {
                loge("makeCall:", e);
                throw new SipException("makeCall()", e);
            }
        }
    }

上面代码中创建了一个AudioStream对象(这个类是传输音频流使用的,我会在后面一篇博客中进行分析音频流是如何传输的),并调用sipSession.makeCall()方法,

SipSession类的makeCall()方法如下:

public void makeCall(SipProfile callee, String sessionDescription,
            int timeout) {
        try {
            mSession.makeCall(callee, sessionDescription, timeout);
        } catch (RemoteException e) {
            loge("makeCall:", e);
        }
    }

此处的mSession就是我们之前分析的SipSessionImpl在客户端的代理类,参数callee就是目的主机对应的SipProfile,我们直接看它的服务端SipSessionImpl的实现.对应代码在SipSessionGroup.java中.

// process the command in a new thread
        private void doCommandAsync(final EventObject command) {
            new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            processCommand(command);
                        } catch (Throwable e) {
                            loge("command error: " + command + ": "
                                    + mLocalProfile.getUriString(),
                                    getRootCause(e));
                            onError(e);
                        }
                    }
            }, "SipSessionAsyncCmdThread").start();
        }


        @Override
        public void makeCall(SipProfile peerProfile, String sessionDescription,
                int timeout) {
            doCommandAsync(new MakeCallCommand(peerProfile, sessionDescription,
                    timeout));
        }

从上面代码中我们可以看出它是在一个子线程中调用processCommand()来处理命令消息的.

private void processCommand(EventObject command) throws SipException {
            if (isLoggable(command)) log("process cmd: " + command);
            if (!process(command)) {
                onError(SipErrorCode.IN_PROGRESS,
                        "cannot initiate a new transaction to execute: "
                        + command);
            }
        }


public boolean process(EventObject evt) throws SipException {
            if (isLoggable(this, evt)) log(" ~~~~~   " + this + ": "
                    + SipSession.State.toString(mState) + ": processing "
                    + logEvt(evt));
            synchronized (SipSessionGroup.this) {
                if (isClosed()) return false;


                if (mSipKeepAlive != null) {
                    // event consumed by keepalive process
                    if (mSipKeepAlive.process(evt)) return true;
                }


                Dialog dialog = null;
                if (evt instanceof RequestEvent) {
                    dialog = ((RequestEvent) evt).getDialog();
                } else if (evt instanceof ResponseEvent) {
                    dialog = ((ResponseEvent) evt).getDialog();
                    extractExternalAddress((ResponseEvent) evt);
                }
                if (dialog != null) mDialog = dialog;


                boolean processed;


                switch (mState) {
                case SipSession.State.REGISTERING:
                case SipSession.State.DEREGISTERING:
                    processed = registeringToReady(evt);
                    break;
                case SipSession.State.READY_TO_CALL:
                    processed = readyForCall(evt);
                    break;
                case SipSession.State.INCOMING_CALL:
                    processed = incomingCall(evt);
                    break;
                case SipSession.State.INCOMING_CALL_ANSWERING:
                    processed = incomingCallToInCall(evt);
                    break;
                case SipSession.State.OUTGOING_CALL:
                case SipSession.State.OUTGOING_CALL_RING_BACK:
                    processed = outgoingCall(evt);
                    break;
                case SipSession.State.OUTGOING_CALL_CANCELING:
                    processed = outgoingCallToReady(evt);
                    break;
                case SipSession.State.IN_CALL:
                    processed = inCall(evt);
                    break;
                case SipSession.State.ENDING_CALL:
                    processed = endingCall(evt);
                    break;
                default:
                    processed = false;
                }
                return (processed || processExceptions(evt));
            }
        }

从上面方法中可以看出,依据mState的值走不同的分支,我们看打电话时是调用outgoingCall(evt);

private boolean outgoingCall(EventObject evt) throws SipException {
            if (expectResponse(Request.INVITE, evt)) {
                ResponseEvent event = (ResponseEvent) evt;
                Response response = event.getResponse();


                int statusCode = response.getStatusCode();
                switch (statusCode) {
                case Response.RINGING:
                case Response.CALL_IS_BEING_FORWARDED:
                case Response.QUEUED:
                case Response.SESSION_PROGRESS:
                    // feedback any provisional responses (except TRYING) as
                    // ring back for better UX
                    if (mState == SipSession.State.OUTGOING_CALL) {
                        mState = SipSession.State.OUTGOING_CALL_RING_BACK;
                        cancelSessionTimer();
                        mProxy.onRingingBack(this);
                    }
                    return true;
                case Response.OK:
                    if (mReferSession != null) {
                        mSipHelper.sendReferNotify(mReferSession.mDialog,
                                getResponseString(Response.OK));
                        // since we don't need to remember the session anymore.
                        mReferSession = null;
                    }
                    mSipHelper.sendInviteAck(event, mDialog);
                    mPeerSessionDescription = extractContent(response);
                    establishCall(true);
                    return true;
                case Response.UNAUTHORIZED:
                case Response.PROXY_AUTHENTICATION_REQUIRED:
                    if (handleAuthentication(event)) {
                        addSipSession(this);
                    }
                    return true;
                case Response.REQUEST_PENDING:
                    // TODO: rfc3261#section-14.1; re-schedule invite
                    return true;
                default:
                    if (mReferSession != null) {
                        mSipHelper.sendReferNotify(mReferSession.mDialog,
                                getResponseString(Response.SERVICE_UNAVAILABLE));
                    }
                    if (statusCode >= 400) {
                        // error: an ack is sent automatically by the stack
                        onError(response);
                        return true;
                    } else if (statusCode >= 300) {
                        // TODO: handle 3xx (redirect)
                    } else {
                        return true;
                    }
                }
                return false;
            } else if (END_CALL == evt) {
                // RFC says that UA should not send out cancel when no
                // response comes back yet. We are cheating for not checking
                // response.
                mState = SipSession.State.OUTGOING_CALL_CANCELING;
                mSipHelper.sendCancel(mClientTransaction);
                startSessionTimer(CANCEL_CALL_TIMER);
                return true;
            } else if (isRequestEvent(Request.INVITE, evt)) {
                // Call self? Send BUSY HERE so server may redirect the call to
                // voice mailbox.
                RequestEvent event = (RequestEvent) evt;
                mSipHelper.sendInviteBusyHere(event,
                        event.getServerTransaction());

                return true;
            }
            return false;
        }

实际上调用了mSipHelper.sendInviteBusyHere(event,event.getServerTransaction());此处的mSipHelper就是一个SipHelper类实例.

SipHelper是一个辅助类,它对sip协议栈操作的相关封装,用于建立sip会话的流程封装.例如发送INVITE消息给目的主机.

整个sip流程的建立,核心类是SipService,SipSessionGroup,这篇文章讲述了sip流程的建立过程.

其实SIP协议规定了会话的发起过程,但没有规定会话的内容及格式。会话内容可以是文本、语音、视频等。因此,SIP协议要结合其它协议,如:用SDP协议描述要传递的内容格式,用RTP,RTSP流媒体协议传输媒体,才能完成整个通信过程。 SIP协议这样做为了简化协议,留下扩展的灵活性。

,那么当一个sip会话建立之后,整个音频数据流是如何从一端传递到另一端的呢,请参考我的下一篇博客.

  

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页