您的位置:首页 > 移动开发 > Android开发

android sip通话实现流程分析

2017-03-29 16:03 956 查看
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会话建立之后,整个音频数据流是如何从一端传递到另一端的呢,请参考我的下一篇博客.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: