Android Sip学习(三)Android Voip实现

 

Android Sip学习(准备知识)SIP 协议完整的呼叫流程

Android Sip学习(一)Android 2.3 APIs SIP-based VoIP

Android Sip学习(二)Android VoIP系统实现原理

Android Sip学习(三)Android Voip实现

Android Sip学习(四)Android自带SipDemo详解

回顾下:

一、基本概念

1、VOIP基于SIP协议,SDK2.3包含一个SIP协议栈和框架API

2、VOIP位于android.net.sip包中,最重要的为SipManager类,可开发基于SIP的VOIP应用。使用时要包含android.permission.INTERNET和android.permission.USE_SIP权限

3、如果在market中显示仅支持VOIP API型号的手机的话,发布时需要在androidManifest.xml中加入<uses_feature android:name = "android.software.sip" android:required = "true">和<uses_feature android:name = "android.software.sip.voip">

4、要支持SIP API

(1)仅Android2.3或更高版本平台支持

(2)不是所有设备都提供SIP支持,确保你的APP只安装在支持SIP的装置上

5、根据GOOGLE官方DEMO项目来扩展的概念

二、类及方法描述

1、一个基本的VOIP项目至少需要三个类SIPSettings(对SIP的基本设置身份验证)、WalkieTalkieActivity(登 录到SIP设备供应商,注册device去处理来电,拨打电话,在通话过程中用户界面管理)、IncomingCallReceiver(监听传入的 SIP电话,然后传递这些SIP电话给WalkieTalkieActivity控制)

2、

 WalkieTalkieActivity

A、SipManager.newInstance()-->此方法中首先判断context是否支持SIP API,若支持则new SipManager。SipManager构造函数中,实例化了一个ISIPService(运用的公式:

Java Code复制内容到剪贴板
  1. IBinder b =ServiceManager.getService(Context.SIP_SERVICE);  //获取系统相应的服务
  2. ISipService service = ISipService.Stub.asInterface(bIBinder);)

上面这两句代码其实是使用了AIDL,就以SipService为例,步骤如下
Service端
1、编写aidl文件:ISipService.aidl,并定义使用的接口(就等同于interface一样)
2、使用makefile生成与之同名的JAVA文件,SipService.java,此类继承extends ISipService.Stub并实现接口定义的方法或者在SipService extends Service,并代码中加入
ISipService.stub sipImpl = new ISipService.stub(){
//实现其接口方法,在SipService.java中是实现了一个名为start()的方法,里面有句是
ServiceManager.addService("sip",newSipService(context));表示SipService已经交给
ServiceManager统一管理了
}

Client端
一(以SIPService为例)
1、而在需要用到SipService时,也就是我们构造SipManager的时候,就通过ServiceManager.getService(Context.SIP_SERVICE)获得SIP的服务(类型为IBinder)
2、并调用
ISipService.Stub.asInterface(IBinder);去获取一个SipService实例(前提是该Service一定是通过
ServiceManager.addService的方式添加进去管理的,这样才能找到此Service)

二(以普通Activity为例)
1、利用Intent intent = new
Intent(Activity.this,SipService.class);-->bindService(intent,
serviceConnection,
Context.BIND_AUTO_CREATE);来绑定SERVICE,在serviceConnection的
onServiceConnected方法中,使用IService.stub.asIntentface(IBinder);来获取实例

B、SipManager创建好后,先从SharedPreference中获取username,domain及pwd,如果第一次进来没有设置这些的
话则需要先创建账户,这里用EditTextPreference来保存用户信息,好处是当填写信息并返回后,EditTextPreference会自
动将值放入SharedPreference中。我们假设username="woody";domain="192.168.12.30";
pwd="910913"

C、这时,我们的SipManager以及用户信息已经设定好了,接下来使用了这句SipProfile.Builder builder = new
SipProfile.Builder(username, domain);我们去看看SipProfile.Builder中做了些什么:

Java Code复制内容到剪贴板
  1. SipURI mUri =mAddressFactory.createSipURI(username,serverDomain);
  2. SipProfile mProfile.mDomain=serverDomain; //设置domain
  3. (在mAddressFactory.createSipURl方法中,我选取了一些核心代码)
  4. StringBuffer uriString=new StringBuffer("sip:");
  5. uriString.append(user);
  6. uriString.append("@");
  7. //if host is an IPv6 string we should enclose it in sq brackets
  8. if(host.indexOf(':') !=host.lastIndexOf(':')&&host.trim().charAt(0) !='[')
  9. host='['+host+']';
  10. uriString.append(host);
  11. StringMsgParser smp=new StringMsgParser();
  12. SipUrl sipUri=smp.parseSIPUrl(uriString.toString());
  13. return sipUri;

从以上代码可以看出其实就是在Format SipURL罢了,里面多加了个if
host为IPV6的判断(IPv4为为32位,十进制;IPv6为128位,16进制)。urlString最后
为"sip:woody@192.168.12.30",smp.parseSIPUrl()方法中,有关于是如何parse的就不做阐述了,总之最后返
回了一个SipUri

D、接下来就是SipProfile sipProfile = SipProfile.Builder.build(); //返回一个SipProfile object
在SipProfile.Builder.build()中,设置了sipProfile的pwd值,删除了之前SipUrl对象里的
password(mUri.setUserPassword(null);)、将sipProfile的address属性设置为
AddressImpl类型的对象值、调用AddressFactory.createURI返回一个SipUri,并
sipProfile.mProxyAddress=sipUri.getHost();

E、创建PendingIntent对象:(Intent与PendingIntent区别在于Intent是及时启动,而PendingIntent是不立刻反应,在特定的情况或通知下才启动,适用于AlertClock等)

Java Code复制内容到剪贴板
  1. Intent i = new Intent();
  2. i.setAction("android.SipDemo.INCOMING_CALL");
  3. PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, Intent.FILL_IN_DATA);

F、
SipManager.open(sipProfile,PendingIntent,null);
//(实际是SIPService在做操作)设置localSIPProfile的callingID-->建立SIP连接(算是注册至SIP
Server)-->打开receiveCall
其中建立SIP连接,最后能追溯到是在SipSessionGroup.java的reset()方法中通过是注册服务器实现的,如下图

注册服务器的步骤为:
(1)设置服务器的属性,例如服务器的地址(IP_ADDRESS_PROP)、栈名(javax.sip.STACK_NAME)、发出去的路径
(localProfile中的javax.sip.OUTBOUND_PROXY)、线程池的大小
(gov.nist.javax.sip.THREAD_POOL_SIZE)等,并且将这些属性加载到服务器中.
(2)通过SipFactory的静态方法取得一个实例,然后通过SipFactory实例sipfactory
(3)创建一个SipStack实例sipstack(这一步获得IP_ADDRESS_PROP,String address = Properties.getProperty("javax.sip.IP_ADDRESS");)
(4)用sipstack创建一个SipProvider实例sipProvider
(5)注册SipListener

Android Sip学习(三)Android Voip实现

时间:2013-08-08 17:03:03  来源:束洋洋博客  作者:束洋洋

Android Sip学习(准备知识)SIP 协议完整的呼叫流程

Android Sip学习(一)Android 2.3 APIs SIP-based VoIP

Android Sip学习(二)Android VoIP系统实现原理

Android Sip学习(三)Android Voip实现

Android Sip学习(四)Android自带SipDemo详解

回顾下:

一、基本概念

1、VOIP基于SIP协议,SDK2.3包含一个SIP协议栈和框架API

2、VOIP位于android.net.sip包中,最重要的为SipManager类,可开发基于SIP的VOIP应用。使用时要包含android.permission.INTERNET和android.permission.USE_SIP权限

3、如果在market中显示仅支持VOIP API型号的手机的话,发布时需要在androidManifest.xml中加入<uses_feature android:name = "android.software.sip" android:required = "true">和<uses_feature android:name = "android.software.sip.voip">

4、要支持SIP API

(1)仅Android2.3或更高版本平台支持

(2)不是所有设备都提供SIP支持,确保你的APP只安装在支持SIP的装置上

5、根据GOOGLE官方DEMO项目来扩展的概念

二、类及方法描述

1、一个基本的VOIP项目至少需要三个类SIPSettings(对SIP的基本设置身份验证)、WalkieTalkieActivity(登 录到SIP设备供应商,注册device去处理来电,拨打电话,在通话过程中用户界面管理)、IncomingCallReceiver(监听传入的 SIP电话,然后传递这些SIP电话给WalkieTalkieActivity控制)

2、

 WalkieTalkieActivity

A、SipManager.newInstance()-->此方法中首先判断context是否支持SIP API,若支持则new SipManager。SipManager构造函数中,实例化了一个ISIPService(运用的公式:

Java Code复制内容到剪贴板
  1. IBinder b =ServiceManager.getService(Context.SIP_SERVICE);  //获取系统相应的服务
  2. ISipService service = ISipService.Stub.asInterface(bIBinder);)

上面这两句代码其实是使用了AIDL,就以SipService为例,步骤如下
Service端
1、编写aidl文件:ISipService.aidl,并定义使用的接口(就等同于interface一样)
2、使用makefile生成与之同名的JAVA文件,SipService.java,此类继承extends ISipService.Stub并实现接口定义的方法或者在SipService extends Service,并代码中加入
ISipService.stub sipImpl = new ISipService.stub(){
//实现其接口方法,在SipService.java中是实现了一个名为start()的方法,里面有句是
ServiceManager.addService("sip",newSipService(context));表示SipService已经交给
ServiceManager统一管理了
}

Client端
一(以SIPService为例)
1、而在需要用到SipService时,也就是我们构造SipManager的时候,就通过ServiceManager.getService(Context.SIP_SERVICE)获得SIP的服务(类型为IBinder)
2、并调用
ISipService.Stub.asInterface(IBinder);去获取一个SipService实例(前提是该Service一定是通过
ServiceManager.addService的方式添加进去管理的,这样才能找到此Service)

二(以普通Activity为例)
1、利用Intent intent = new
Intent(Activity.this,SipService.class);-->bindService(intent,
serviceConnection,
Context.BIND_AUTO_CREATE);来绑定SERVICE,在serviceConnection的
onServiceConnected方法中,使用IService.stub.asIntentface(IBinder);来获取实例

B、SipManager创建好后,先从SharedPreference中获取username,domain及pwd,如果第一次进来没有设置这些的
话则需要先创建账户,这里用EditTextPreference来保存用户信息,好处是当填写信息并返回后,EditTextPreference会自
动将值放入SharedPreference中。我们假设username="woody";domain="192.168.12.30";
pwd="910913"

C、这时,我们的SipManager以及用户信息已经设定好了,接下来使用了这句SipProfile.Builder builder = new
SipProfile.Builder(username, domain);我们去看看SipProfile.Builder中做了些什么:

Java Code复制内容到剪贴板
  1. SipURI mUri =mAddressFactory.createSipURI(username,serverDomain);
  2. SipProfile mProfile.mDomain=serverDomain; //设置domain
  3. (在mAddressFactory.createSipURl方法中,我选取了一些核心代码)
  4. StringBuffer uriString=new StringBuffer("sip:");
  5. uriString.append(user);
  6. uriString.append("@");
  7. //if host is an IPv6 string we should enclose it in sq brackets
  8. if(host.indexOf(':') !=host.lastIndexOf(':')&&host.trim().charAt(0) !='[')
  9. host='['+host+']';
  10. uriString.append(host);
  11. StringMsgParser smp=new StringMsgParser();
  12. SipUrl sipUri=smp.parseSIPUrl(uriString.toString());
  13. return sipUri;

从以上代码可以看出其实就是在Format SipURL罢了,里面多加了个if
host为IPV6的判断(IPv4为为32位,十进制;IPv6为128位,16进制)。urlString最后
为"sip:woody@192.168.12.30",smp.parseSIPUrl()方法中,有关于是如何parse的就不做阐述了,总之最后返
回了一个SipUri

D、接下来就是SipProfile sipProfile = SipProfile.Builder.build(); //返回一个SipProfile object
在SipProfile.Builder.build()中,设置了sipProfile的pwd值,删除了之前SipUrl对象里的
password(mUri.setUserPassword(null);)、将sipProfile的address属性设置为
AddressImpl类型的对象值、调用AddressFactory.createURI返回一个SipUri,并
sipProfile.mProxyAddress=sipUri.getHost();

E、创建PendingIntent对象:(Intent与PendingIntent区别在于Intent是及时启动,而PendingIntent是不立刻反应,在特定的情况或通知下才启动,适用于AlertClock等)

Java Code复制内容到剪贴板
  1. Intent i = new Intent();
  2. i.setAction("android.SipDemo.INCOMING_CALL");
  3. PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, Intent.FILL_IN_DATA);

F、
SipManager.open(sipProfile,PendingIntent,null);
//(实际是SIPService在做操作)设置localSIPProfile的callingID-->建立SIP连接(算是注册至SIP
Server)-->打开receiveCall
其中建立SIP连接,最后能追溯到是在SipSessionGroup.java的reset()方法中通过是注册服务器实现的,如下图

注册服务器的步骤为:
(1)设置服务器的属性,例如服务器的地址(IP_ADDRESS_PROP)、栈名(javax.sip.STACK_NAME)、发出去的路径
(localProfile中的javax.sip.OUTBOUND_PROXY)、线程池的大小
(gov.nist.javax.sip.THREAD_POOL_SIZE)等,并且将这些属性加载到服务器中.
(2)通过SipFactory的静态方法取得一个实例,然后通过SipFactory实例sipfactory
(3)创建一个SipStack实例sipstack(这一步获得IP_ADDRESS_PROP,String address = Properties.getProperty("javax.sip.IP_ADDRESS");)
(4)用sipstack创建一个SipProvider实例sipProvider
(5)注册SipListener

G、A~F步骤都是在做准备工作,大致的步骤如下:new SIPService-->new
SIPManager-->设定用户信息-->new SIPURI-->new SIPProfile-->new
PendingIntent-->set sipProfile callingID-->(if
profile.getAutoRegistation)open toReceiveCalls-->register SipService
现在是call
someone~呼叫的工作是SipAudioCall类来完成(可用sipManager.makeAudioCall或takeAudioCall来
实例化,SipAudioCall.startAudio时需要 RECORD_AUDIO, ACCESS_WIFI_STATE, and
WAKE_LOCK permissions,setSpeakerMode() 时需要MODIFY_AUDIO_SETTINGS
permission)
【1】当需要呼叫时,使用sipManager.makeAudioCall(String localProfileURI, String
peerProfileURI, SipAudioCall.listener,int
timeout);来创建一个SipAudioCall,其中timeout以seconds为单位,过了timeout表示打电话超时。需要打给别人时
使用makeAudioCall创建,接听电话用takeAudioCall来创建sipAudioCall
【2】SipAudioCall中有一个嵌套的class:SipAudioCall.Listener(此类主要用于监听SIP CALL,when[呼叫电话 or 接听电话])

Java Code复制内容到剪贴板
  1. SipAudioCall.Listener listener = new SipAudioCall.Listener() {
  2. @Override
  3. public void onCallEstablished(SipAudioCall call) { //呼叫建立
  4. call.startAudio(); //启动音频
  5. call.setSpeakerMode(true); //调整为可讲话模式
  6. call.toggleMute(); //触发无声
  7. updateStatus(call);
  8. }
  9. };
  10. SipAudioCall call = manager.makeAudioCall(me.getUriString(), sipAddress, listener, 30);

(以上例子为makeAudioCall)
【3】我们看看makeAudioCall()方法(makeAudioCall requires 2 sipProfile):

Java Code复制内容到剪贴板
  1. SipAudioCall call =new SipAudioCall(mContext, localProfile);
  2. call.setListener(listener);  //这两句很简单就是创建一个local的sipAudioCall
  3. SipSession s = createSipSession(localProfile, null); -->mSipService.createSession(localProfile, null);//  sipService来创建session,并保存在SipSessionGroupExt中
  4. call.makeCall(peerProfile,s,null); //这句就是呼叫,最后追溯到实际是SipSession.makecall

总结:在发起通话中
首先是创建SipAudioCall.listener,以便监听对话建立和对话结束,然后做相应的操作
然后是SipManager.makeAudioCall(localAdd,llistener,XXXX),在makeAudioCall方法中
A、创建一个sipAudioCall(localProfile)
B、创建SipSession以建立起会话
C、SipSession.makeCall(peerProfile,XXXX); //SipSession呼叫远程profile
【4】关于接电话道理都差不多,takeAudioCall
通过之前设置的callingID来查找mSipService.getPendingSession(callId);来获得SipSession。并创建SipAudioCall,然后attachCall就算接受电话了

三、总结
1、VOIP服务位于android.net.sip包中,关键类为SipManager。需要用到的permission列表,其中后面三个为使用WIFI获取IP地址所需要用到的权限:

XML/HTML Code复制内容到剪贴板
  1. <uses-permission android:name="android.permission.INTERNET"></uses-permission>
  2. <uses-permission android:name="android.permission.USE_SIP"/>
  3. <uses-feature android:name = "android.hardware.sip.voip" android:required = "true"/>
  4. <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
  5. <uses-permission android:name="android.permission.WAKE_LOCK" />
  6. <uses-permission android:name="android.permission.RECORD_AUDIO" />

2、类之间的关系

G、A~F步骤都是在做准备工作,大致的步骤如下:new SIPService-->new
SIPManager-->设定用户信息-->new SIPURI-->new SIPProfile-->new
PendingIntent-->set sipProfile callingID-->(if
profile.getAutoRegistation)open toReceiveCalls-->register SipService
现在是call
someone~呼叫的工作是SipAudioCall类来完成(可用sipManager.makeAudioCall或takeAudioCall来
实例化,SipAudioCall.startAudio时需要 RECORD_AUDIO, ACCESS_WIFI_STATE, and
WAKE_LOCK permissions,setSpeakerMode() 时需要MODIFY_AUDIO_SETTINGS
permission)
【1】当需要呼叫时,使用sipManager.makeAudioCall(String localProfileURI, String
peerProfileURI, SipAudioCall.listener,int
timeout);来创建一个SipAudioCall,其中timeout以seconds为单位,过了timeout表示打电话超时。需要打给别人时
使用makeAudioCall创建,接听电话用takeAudioCall来创建sipAudioCall
【2】SipAudioCall中有一个嵌套的class:SipAudioCall.Listener(此类主要用于监听SIP CALL,when[呼叫电话 or 接听电话])

Java Code复制内容到剪贴板
  1. SipAudioCall.Listener listener = new SipAudioCall.Listener() {
  2. @Override
  3. public void onCallEstablished(SipAudioCall call) { //呼叫建立
  4. call.startAudio(); //启动音频
  5. call.setSpeakerMode(true); //调整为可讲话模式
  6. call.toggleMute(); //触发无声
  7. updateStatus(call);
  8. }
  9. };
  10. SipAudioCall call = manager.makeAudioCall(me.getUriString(), sipAddress, listener, 30);

(以上例子为makeAudioCall)
【3】我们看看makeAudioCall()方法(makeAudioCall requires 2 sipProfile):

Java Code复制内容到剪贴板
  1. SipAudioCall call =new SipAudioCall(mContext, localProfile);
  2. call.setListener(listener);  //这两句很简单就是创建一个local的sipAudioCall
  3. SipSession s = createSipSession(localProfile, null); -->mSipService.createSession(localProfile, null);//  sipService来创建session,并保存在SipSessionGroupExt中
  4. call.makeCall(peerProfile,s,null); //这句就是呼叫,最后追溯到实际是SipSession.makecall

总结:在发起通话中
首先是创建SipAudioCall.listener,以便监听对话建立和对话结束,然后做相应的操作
然后是SipManager.makeAudioCall(localAdd,llistener,XXXX),在makeAudioCall方法中
A、创建一个sipAudioCall(localProfile)
B、创建SipSession以建立起会话
C、SipSession.makeCall(peerProfile,XXXX); //SipSession呼叫远程profile
【4】关于接电话道理都差不多,takeAudioCall
通过之前设置的callingID来查找mSipService.getPendingSession(callId);来获得SipSession。并创建SipAudioCall,然后attachCall就算接受电话了

三、总结
1、VOIP服务位于android.net.sip包中,关键类为SipManager。需要用到的permission列表,其中后面三个为使用WIFI获取IP地址所需要用到的权限:

XML/HTML Code复制内容到剪贴板
  1. <uses-permission android:name="android.permission.INTERNET"></uses-permission>
  2. <uses-permission android:name="android.permission.USE_SIP"/>
  3. <uses-feature android:name = "android.hardware.sip.voip" android:required = "true"/>
  4. <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
  5. <uses-permission android:name="android.permission.WAKE_LOCK" />
  6. <uses-permission android:name="android.permission.RECORD_AUDIO" />

2、类之间的关系

Android Sip学习(三)Android Voip实现

时间:2013-08-08 17:03:03  来源:束洋洋博客  作者:束洋洋

Android Sip学习(准备知识)SIP 协议完整的呼叫流程

Android Sip学习(一)Android 2.3 APIs SIP-based VoIP

Android Sip学习(二)Android VoIP系统实现原理

Android Sip学习(三)Android Voip实现

Android Sip学习(四)Android自带SipDemo详解

回顾下:

一、基本概念

1、VOIP基于SIP协议,SDK2.3包含一个SIP协议栈和框架API

2、VOIP位于android.net.sip包中,最重要的为SipManager类,可开发基于SIP的VOIP应用。使用时要包含android.permission.INTERNET和android.permission.USE_SIP权限

3、如果在market中显示仅支持VOIP API型号的手机的话,发布时需要在androidManifest.xml中加入<uses_feature android:name = "android.software.sip" android:required = "true">和<uses_feature android:name = "android.software.sip.voip">

4、要支持SIP API

(1)仅Android2.3或更高版本平台支持

(2)不是所有设备都提供SIP支持,确保你的APP只安装在支持SIP的装置上

5、根据GOOGLE官方DEMO项目来扩展的概念

二、类及方法描述

1、一个基本的VOIP项目至少需要三个类SIPSettings(对SIP的基本设置身份验证)、WalkieTalkieActivity(登 录到SIP设备供应商,注册device去处理来电,拨打电话,在通话过程中用户界面管理)、IncomingCallReceiver(监听传入的 SIP电话,然后传递这些SIP电话给WalkieTalkieActivity控制)

2、

 WalkieTalkieActivity

A、SipManager.newInstance()-->此方法中首先判断context是否支持SIP API,若支持则new SipManager。SipManager构造函数中,实例化了一个ISIPService(运用的公式:

Java Code复制内容到剪贴板
  1. IBinder b =ServiceManager.getService(Context.SIP_SERVICE);  //获取系统相应的服务
  2. ISipService service = ISipService.Stub.asInterface(bIBinder);)

上面这两句代码其实是使用了AIDL,就以SipService为例,步骤如下
Service端
1、编写aidl文件:ISipService.aidl,并定义使用的接口(就等同于interface一样)
2、使用makefile生成与之同名的JAVA文件,SipService.java,此类继承extends ISipService.Stub并实现接口定义的方法或者在SipService extends Service,并代码中加入
ISipService.stub sipImpl = new ISipService.stub(){
//实现其接口方法,在SipService.java中是实现了一个名为start()的方法,里面有句是
ServiceManager.addService("sip",newSipService(context));表示SipService已经交给
ServiceManager统一管理了
}

Client端
一(以SIPService为例)
1、而在需要用到SipService时,也就是我们构造SipManager的时候,就通过ServiceManager.getService(Context.SIP_SERVICE)获得SIP的服务(类型为IBinder)
2、并调用
ISipService.Stub.asInterface(IBinder);去获取一个SipService实例(前提是该Service一定是通过
ServiceManager.addService的方式添加进去管理的,这样才能找到此Service)

二(以普通Activity为例)
1、利用Intent intent = new
Intent(Activity.this,SipService.class);-->bindService(intent,
serviceConnection,
Context.BIND_AUTO_CREATE);来绑定SERVICE,在serviceConnection的
onServiceConnected方法中,使用IService.stub.asIntentface(IBinder);来获取实例

B、SipManager创建好后,先从SharedPreference中获取username,domain及pwd,如果第一次进来没有设置这些的
话则需要先创建账户,这里用EditTextPreference来保存用户信息,好处是当填写信息并返回后,EditTextPreference会自
动将值放入SharedPreference中。我们假设username="woody";domain="192.168.12.30";
pwd="910913"

C、这时,我们的SipManager以及用户信息已经设定好了,接下来使用了这句SipProfile.Builder builder = new
SipProfile.Builder(username, domain);我们去看看SipProfile.Builder中做了些什么:

Java Code复制内容到剪贴板
  1. SipURI mUri =mAddressFactory.createSipURI(username,serverDomain);
  2. SipProfile mProfile.mDomain=serverDomain; //设置domain
  3. (在mAddressFactory.createSipURl方法中,我选取了一些核心代码)
  4. StringBuffer uriString=new StringBuffer("sip:");
  5. uriString.append(user);
  6. uriString.append("@");
  7. //if host is an IPv6 string we should enclose it in sq brackets
  8. if(host.indexOf(':') !=host.lastIndexOf(':')&&host.trim().charAt(0) !='[')
  9. host='['+host+']';
  10. uriString.append(host);
  11. StringMsgParser smp=new StringMsgParser();
  12. SipUrl sipUri=smp.parseSIPUrl(uriString.toString());
  13. return sipUri;

从以上代码可以看出其实就是在Format SipURL罢了,里面多加了个if
host为IPV6的判断(IPv4为为32位,十进制;IPv6为128位,16进制)。urlString最后
为"sip:woody@192.168.12.30",smp.parseSIPUrl()方法中,有关于是如何parse的就不做阐述了,总之最后返
回了一个SipUri

D、接下来就是SipProfile sipProfile = SipProfile.Builder.build(); //返回一个SipProfile object
在SipProfile.Builder.build()中,设置了sipProfile的pwd值,删除了之前SipUrl对象里的
password(mUri.setUserPassword(null);)、将sipProfile的address属性设置为
AddressImpl类型的对象值、调用AddressFactory.createURI返回一个SipUri,并
sipProfile.mProxyAddress=sipUri.getHost();

E、创建PendingIntent对象:(Intent与PendingIntent区别在于Intent是及时启动,而PendingIntent是不立刻反应,在特定的情况或通知下才启动,适用于AlertClock等)

Java Code复制内容到剪贴板
  1. Intent i = new Intent();
  2. i.setAction("android.SipDemo.INCOMING_CALL");
  3. PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, Intent.FILL_IN_DATA);

F、
SipManager.open(sipProfile,PendingIntent,null);
//(实际是SIPService在做操作)设置localSIPProfile的callingID-->建立SIP连接(算是注册至SIP
Server)-->打开receiveCall
其中建立SIP连接,最后能追溯到是在SipSessionGroup.java的reset()方法中通过是注册服务器实现的,如下图

注册服务器的步骤为:
(1)设置服务器的属性,例如服务器的地址(IP_ADDRESS_PROP)、栈名(javax.sip.STACK_NAME)、发出去的路径
(localProfile中的javax.sip.OUTBOUND_PROXY)、线程池的大小
(gov.nist.javax.sip.THREAD_POOL_SIZE)等,并且将这些属性加载到服务器中.
(2)通过SipFactory的静态方法取得一个实例,然后通过SipFactory实例sipfactory
(3)创建一个SipStack实例sipstack(这一步获得IP_ADDRESS_PROP,String address = Properties.getProperty("javax.sip.IP_ADDRESS");)
(4)用sipstack创建一个SipProvider实例sipProvider
(5)注册SipListener

G、A~F步骤都是在做准备工作,大致的步骤如下:new SIPService-->new
SIPManager-->设定用户信息-->new SIPURI-->new SIPProfile-->new
PendingIntent-->set sipProfile callingID-->(if
profile.getAutoRegistation)open toReceiveCalls-->register SipService
现在是call
someone~呼叫的工作是SipAudioCall类来完成(可用sipManager.makeAudioCall或takeAudioCall来
实例化,SipAudioCall.startAudio时需要 RECORD_AUDIO, ACCESS_WIFI_STATE, and
WAKE_LOCK permissions,setSpeakerMode() 时需要MODIFY_AUDIO_SETTINGS
permission)
【1】当需要呼叫时,使用sipManager.makeAudioCall(String localProfileURI, String
peerProfileURI, SipAudioCall.listener,int
timeout);来创建一个SipAudioCall,其中timeout以seconds为单位,过了timeout表示打电话超时。需要打给别人时
使用makeAudioCall创建,接听电话用takeAudioCall来创建sipAudioCall
【2】SipAudioCall中有一个嵌套的class:SipAudioCall.Listener(此类主要用于监听SIP CALL,when[呼叫电话 or 接听电话])

Java Code复制内容到剪贴板
  1. SipAudioCall.Listener listener = new SipAudioCall.Listener() {
  2. @Override
  3. public void onCallEstablished(SipAudioCall call) { //呼叫建立
  4. call.startAudio(); //启动音频
  5. call.setSpeakerMode(true); //调整为可讲话模式
  6. call.toggleMute(); //触发无声
  7. updateStatus(call);
  8. }
  9. };
  10. SipAudioCall call = manager.makeAudioCall(me.getUriString(), sipAddress, listener, 30);

(以上例子为makeAudioCall)
【3】我们看看makeAudioCall()方法(makeAudioCall requires 2 sipProfile):

Java Code复制内容到剪贴板
  1. SipAudioCall call =new SipAudioCall(mContext, localProfile);
  2. call.setListener(listener);  //这两句很简单就是创建一个local的sipAudioCall
  3. SipSession s = createSipSession(localProfile, null); -->mSipService.createSession(localProfile, null);//  sipService来创建session,并保存在SipSessionGroupExt中
  4. call.makeCall(peerProfile,s,null); //这句就是呼叫,最后追溯到实际是SipSession.makecall

总结:在发起通话中
首先是创建SipAudioCall.listener,以便监听对话建立和对话结束,然后做相应的操作
然后是SipManager.makeAudioCall(localAdd,llistener,XXXX),在makeAudioCall方法中
A、创建一个sipAudioCall(localProfile)
B、创建SipSession以建立起会话
C、SipSession.makeCall(peerProfile,XXXX); //SipSession呼叫远程profile
【4】关于接电话道理都差不多,takeAudioCall
通过之前设置的callingID来查找mSipService.getPendingSession(callId);来获得SipSession。并创建SipAudioCall,然后attachCall就算接受电话了

三、总结
1、VOIP服务位于android.net.sip包中,关键类为SipManager。需要用到的permission列表,其中后面三个为使用WIFI获取IP地址所需要用到的权限:

XML/HTML Code复制内容到剪贴板
  1. <uses-permission android:name="android.permission.INTERNET"></uses-permission>
  2. <uses-permission android:name="android.permission.USE_SIP"/>
  3. <uses-feature android:name = "android.hardware.sip.voip" android:required = "true"/>
  4. <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
  5. <uses-permission android:name="android.permission.WAKE_LOCK" />
  6. <uses-permission android:name="android.permission.RECORD_AUDIO" />

2、类之间的关系

Android Sip学习(三)Android Voip实现的更多相关文章

  1. Android JNI学习(三)——Java与Native相互调用

    本系列文章如下: Android JNI(一)——NDK与JNI基础 Android JNI学习(二)——实战JNI之“hello world” Android JNI学习(三)——Java与Nati ...

  2. Android动画学习笔记-Android Animation

    Android动画学习笔记-Android Animation   3.0以前,android支持两种动画模式,tween animation,frame animation,在android3.0中 ...

  3. Android FrameWork 学习之Android 系统源码调试

    这是很久以前访问掘金的时候 无意间看到的一个关于Android的文章,作者更细心,分阶段的将学习步骤记录在自己博客中,我觉得很有用,想作为分享同时也是留下自己知识的一些欠缺收藏起来,今后做项目的时候会 ...

  4. Android UI学习 - ListView (android.R.layout.simple_list_item_1是个什么东西)

    Android UI学习 - ListView -- :: 标签:Android UI 移动开发 ListView ListActivity 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始 ...

  5. Android基础学习:Android环境搭建

    在3年前,自学过Android的一些基础知识,但是那个时候Linux等其他的知识结构比较薄弱,理解得不是很深刻,后来因项目变动的原因,没有再搞Android相关的东西了.时过境迁,还是因为项目变动,重 ...

  6. Android NDK 学习之Android.mk

    Android.mk file syntax specification Introduction: This document describes the syntax of Android.mk  ...

  7. 吴裕雄--天生自然Android开发学习:android 背景相关与系统架构分析

    1.Android背景与当前的状况 Android系统是由Andy Rubin创建的,后来被Google收购了:最早的版本是:Android 1.1版本 而现在最新的版本是今年5.28,Google ...

  8. Android JNI 学习(三):JNI 数据类型和数据结构

    本文我们来讨论一下JNI如何将Java类型映射到本机C类型. 一.基本数据类型 如下图表整理了Java基本类型和native对应的关系: Java类型 Native类型 描述 boolean jboo ...

  9. Android动画学习(一)——Android动画系统框架简介

    2015-11-09补充:Drawable Animation极有可能是Frame Animation 这几天在找工作,面试的时候被问到了Android动画,之前完全没接触过这部分,直接给懵了,当然其 ...

随机推荐

  1. java--偏向锁

    Java偏向锁(Biased Locking)是Java 6引入的一项多线程优化.它通过消除资源无竞争情况下的同步原语,进一步提高了程序的运行性能. 轻量级锁也是一种多线程优化,它与偏向锁的区别在于, ...

  2. HDU 3916 Sequence Decomposition 【贪心】

    这道题目的题意就是使用题目中所给的Gate 函数,模拟出输入的结果 当然我们分析的时候可以倒着来,就是拿输入去减 每次Gate 函数都会有一个有效范围 这道题目求的就是,找出一种模拟方法,使得最小的有 ...

  3. Week3(9月23日):例子更Powerful更完整了,哇咔咔

    Part I:提问  =========================== 1.控制器中动作方法的返回类型有哪些? 2.如果控制器代码如下,请问浏览器中如何输入什么路由访问? public clas ...

  4. Use Node.js DDP Client on Arduino Yun to Access Meteor Server

    Use Node.js DDP Client on Arduino Yun to Access Meteor Server 概述 在Arduino Yun上安装 Node.js, 并測试与 Meteo ...

  5. 走进C++程序世界-----函数相关(全局变量)

    全局变量 在函数外面定义的变量的作用域为全局,在程序的任何函数中都可用.与全局变量同名的局部变量不会修改全局变量的值,但会隐藏它.如果函数中有一个与全局变量同 名的局部变量时,则在函数中使用该名称时, ...

  6. 用shell脚本爬取网页信息

    有个小需求,就是爬取一个小网站一些网页里的某些信息,url是带序号的类似的,不需要写真正的spider,网页内容也是差不多的 需要取出网页中<h1></h1>中间的字符串,而且 ...

  7. iOS开发中关于本地数据中SQLite数据库常用的SQL语句

    创建表 CREATE TABLE IF NOT EXISTS "student" ("number" INTEGER PRIMARY KEY AUTOINCRE ...

  8. BZOJ 1717: [Usaco2006 Dec]Milk Patterns 产奶的模式( 二分答案 + 后缀数组 )

    二分答案m, 后缀数组求出height数组后分组来判断. ------------------------------------------------------------ #include&l ...

  9. 利用ant的javac任务来编译程序使用ant的java任务来运行程序

    <?xml version="1.0" encoding="UTF-8"?> <project name="javaTest&quo ...

  10. Python之路day4

    坚持就是胜利.今天零下14度,从教室出来的路上真的很冷很冷,希望这个冬天自己不会白过,春暖花开的时候一定要给世界一个更好的自己. 原本以为day3的作业自己做得挺好的,没想到只得了B+.必须要加油了, ...