Android Sipdroid 对外通话建立过程分析,以及监听通话接通时刻
来源:互联网 发布:淄博最专业的seo公司 编辑:程序博客网 时间:2024/04/27 05:12
感谢博友 @我闹 问题(怎么获得通话建立的时间点,不用广播)的提出,才促使我继续写这篇分析文章。
这篇文是上一篇Sipdorid的分析,进一步说明对外呼出的建立过程,如有错误,请指出。
CallCard中有两类注意一下,一个是更新通话界面的方法
//对外暴露的方法:更新如通话时间,通话状态public void displayMainCallStatus(Phone phone, Call call){}
第二个私有
/*** Updates the "upper" and "lower" titles based on the* current state of this call.*/private void updateCardTitleWidgets(Phone phone, Call call){}
在私有方法里面你可以看到Call.State state = call.getState();
通话状态的判断,当Call.State.ACTIVE
时,计时器mElapsedTime
开始计时,从某种状态来说,这个已经算正式接通的状态。所以,更进一步是要对Call
状态的实时监听。
调用处:
CallCard的displayMainCallStatus:
CallCard的updateCardTitleWidgets:
Call通话状态枚举
IDLE, ACTIVE, HOLDING, DIALING, ALERTING, INCOMING, WAITING, DISCONNECTED
我们只注意ACTIVE
(通话中)这个。
在管理器Receiver
中,你可以查看到这里有对Call
状态的设置ccCall.setState(...)
,在我们向外拨打电话同时会发送OFFHOLD
广播(摘机)过程,你也可以通过这个广播同时判断Call
状态即可获取对外通话正式接通的时刻。
注:OFFHOLD
广播会发送两个,一次是开始拨打电话(按了拨号键)状态为Call.State.DIALING
,第二次是接通(建立通话),状态为Call.State.ACTIVE
再进一步分析
在Receiver
中,你可以看到call
状态的改变是在public static void onState(int state,String caller)
,而这个方法的调用时是依赖UserAgent
中的protected synchronized void changeStatus(int state,String caller)
Receiver的onState(int state,String caller)部分代码:
public static void onState(int state,String caller) { ... if (call_state != state) { if (state != UserAgent.UA_STATE_IDLE) call_end_reason = -1; call_state = state; switch(call_state) { ... case UserAgent.UA_STATE_OUTGOING_CALL: broadcastCallStateChanged("OFFHOOK", caller); ccCall.setState(Call.State.DIALING); ccConn.setUserData(null); ccConn.setAddress(caller,caller); ccConn.setIncoming(false); ccConn.date = System.currentTimeMillis(); ccCall.base = 0; moveTop(); Checkin.checkin(true); break; case UserAgent.UA_STATE_INCALL: broadcastCallStateChanged("OFFHOOK", null); if (ccCall.base == 0) { ccCall.base = SystemClock.elapsedRealtime(); } progress(); ccCall.setState(Call.State.ACTIVE); stopRingtone(); if (wl != null && wl.isHeld()) wl.release(); mContext.startActivity(createIntent(InCallScreen.class)); break; ... } pos(true); RtpStreamReceiver.ringback(false); } }
在UserAgent
中,通话状态由变量call_state
保持,而值UserAgent.UA_STATE_INCALL
即值3代表通话中,具体见代码,这里不详说,其中建立会话成功是接收到报文”2xx”后被调用onCallReInviteAccepted()
(实现CallListener
接口)通话建立,这里就开始一系列的变化,改变UserAgent
的通话状态call_state
,调用Receiver.onState
,改变Call.state
,发送广播,更新通话界面。
那么,CallListener
接口是什么时候创建实现的呢?
在对外呼出,UserAgent
会创建一个ExtendedCall
对象持有,UserAgent
可以理解为通话的一个配置,而ExtendedCall
是一个具体是通话实现,他处理报文,将接收的信息继续下发,让其它类解析。
所以让我们回到起点,当我们对外拨打电话时,调用Receiver.engine(mContext).call(xxx,true);
,构建一个UserAgent
,又构造了ExtendedCall
,又创建了InviteDialog
,调用invite
,最后启动
Message invite = MessageFactory.createInviteRequest(sip_provider, request_uri, to_url, from_url, contact_url, session_descriptor, icsi);invite(invite);
向服务器请求建立通话连接,接下来就是等服务器数据的返回,报文解析,这里不详述(因为很久前抓的报文文件删了,找不到)。
从上面分析,一次通话的拨出就已经很清晰了,那么我们想获取到通话正式接通那个时间点,该怎么处理?
- 用广播,最简单,原始代码都不用改动,上面已经说了“OFFHOLD”
- 自定义监听器
很明显,在你拨出界面的做监听,简单点,就是在你调用Receiver.engine(mContext).call(xxx,true);
时做监听,这里没有监听方法,你可以在SipdroidEngine
增加构造方法,将监听器下传到UserAgent
,在UserAgent
的onCallReInviteAccepted
实现你的回调,如yourListener.onCallReInviteAccepted
,假如你要继续下传到ExtendedCall
甚至InviteDialog
再回调出来也没有关系,你开心就好。
好了,写到这也就够了,下面放构造的代码块,我没有在github上放这个库,反正有开源代码,自己clone后爱怎么改怎么改,it all on you.
SipdroidEngine 代码块:
public boolean call(String target_url,boolean force,InviteListener listener) { int p = pref; boolean found = false; if (isRegistered(p) && Receiver.isFast(p)) found = true; else { for (p = 0; p < LINES; p++) if (isRegistered(p) && Receiver.isFast(p)) { found = true; break; } if (!found && force) { p = pref; if (Receiver.isFast(p)) found = true; else for (p = 0; p < LINES; p++) if (Receiver.isFast(p)) { found = true; break; } } } if (!found || (ua = uas[p]) == null) { if (PreferenceManager.getDefaultSharedPreferences(getUIContext()).getBoolean(Settings.PREF_CALLBACK, Settings.DEFAULT_CALLBACK) && PreferenceManager.getDefaultSharedPreferences(getUIContext()).getString(Settings.PREF_POSURL, Settings.DEFAULT_POSURL).length() > 0) { Receiver.url("n="+Uri.encode(target_url)); return true; } return false; } ua.printLog("UAC: CALLING " + target_url); if (!ua.user_profile.audio && !ua.user_profile.video) { ua.printLog("ONLY SIGNALING, NO MEDIA"); } return ua.call(target_url, false,listener); }
UserAgent 代码块:
private InviteListener listener = null; public boolean call(String target_url, boolean send_anonymous, InviteListener listener) { this.listener = listener; return call(target_url, send_anonymous); } /** * Callback function called when arriving a 2xx (re-invite/modify accepted) */ public void onCallReInviteAccepted(Call call, String sdp, Message resp) { printLog("onCallReInviteAccepted()", LogLevel.LOW); if (call != this.call) { printLog("NOT the current call", LogLevel.LOW); return; } printLog("RE-INVITE-ACCEPTED/CALL", LogLevel.HIGH); if (statusIs(UA_STATE_HOLD)) { changeStatus(UA_STATE_INCALL); if(listener!=null){ listener.onInviteAccepted(); } }else changeStatus(UA_STATE_HOLD); }
监听器:
public interface InviteListener { void onInviteAccepted();}
use this:
Receiver.engine(mContext).call(xxx,true,this);//interface InviteListener //here do your job public void onInviteAccepted(){}
更新20170916
增加监听器有效是在同一个Activity中操作,但是调用call之后往往会启动另外一个会话界面,如InCallCard,此时的监听器可以说无效了,除非利用eventbus或者广播或者其他方式将该事件传递,否则这个监听是无用的。
- Android Sipdroid 对外通话建立过程分析,以及监听通话接通时刻
- Android通话接通监听
- Android通话接通震动
- Sipdroid源码初探(二):监听来电,通话建立流程
- [Android实例]通话接通后震动提示
- [Android实例]通话接通后震动提示
- [Android实例]通话接通后震动提示
- Android 实现通话监听
- android通话监听类
- Android Sipdroid 语音通话项目简单使用
- Sipdroid中通话问题
- android源代码 锁屏灭屏来电接通后立即息屏、通话过程中息屏对方挂断不亮屏
- android源代码 锁屏灭屏来电接通后立即息屏、通话过程中息屏对方挂断不亮屏
- Android 实现通话监听功能
- Android实现通话呼叫转移与监听通话录音功能
- android通话过程RIL层详细分析(代码)
- android通话过程RIL层详细分析(代码)
- 【通话】通话模块代码分析
- HDOJ HDU 1068 Girls and Boys
- C# 文件过滤器filter
- hash属性
- 674. Longest Continuous Increasing Subsequence
- Integer to Roman 解法
- Android Sipdroid 对外通话建立过程分析,以及监听通话接通时刻
- 【Hibernate】检索数据的五种方式
- Linux:Linux操作系统Centos7.2版本搭建Apache+PHP+Mysql环境
- Leetcode | Longest Substring Without Repeating Characters
- es6-js
- 关于sublime中文问题
- DPDK学习笔记1--基础概念篇(UIO,大页内存,CPU亲和性,NUMA机制等)
- linux 命令中grep 命令得个人总结(一)
- 【Hibernate】Session清空缓存与清理缓存