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. QT窗口置顶/真透明/背景模糊/非矩形/跳过任务栏分页器/无边框/无焦点点击/焦点穿透

    qt 窗口置顶/真透明/背景模糊/非矩形/跳过任务栏分页器/无边框/无焦点点击/焦点穿透 窗口置顶qt 里是 setWindowFlags(Qt::WindowStaysOnTopHint)kde 里 ...

  2. php利用iframe实现无刷新文件上传功能

    上传原理很简单就是利用表单的打开方式为iframe的name名,这样就可以在当前页面的iframe打来了,实现文件上传,再利用js返回上传结果. form target .在 action 属性中规定 ...

  3. Windows Azure 网站开发Stacks支持

    编辑人员注释:本文章由 Windows Azure 网站团队的项目经理 Daria Grigoriu 和 Windows Azure 网站开发人员体验合作伙伴共同撰写. Windows Azure 网 ...

  4. 基于visual Studio2013解决C语言竞赛题之0523魔方阵

     题目

  5. Mac OSX的开机启动配置

    Login Items Mac OSX的当前用户成功登录后启动的程序,该类别的启动项配置文件存放在~/Library/Preferences/com.apple.loginitems.plist,所以 ...

  6. WCF技术剖析之二十四: ServiceDebugBehavior服务行为是如何实现异常的传播的?

    原文:WCF技术剖析之二十四: ServiceDebugBehavior服务行为是如何实现异常的传播的? 服务端只有抛出FaultException异常才能被正常地序列化成Fault消息,并实现向客户 ...

  7. c语言string.h和memory.h某些函数重复问题

    在C语言中,为了使用memset()函数,你是选择#include <string.h>还是<memory.h>?两个都可以,如何选择? <string.h>,标准 ...

  8. Tri_integral Summer Training 9 总结

    比赛链接 A B C D H I J K 多灾多难的 Summer Training 9,前一天挂了一场比赛,结果题一半不能做,于是打了一个小时就放弃了.之后的两场Summer Training 9一 ...

  9. ARM相关知识

    ARM7采用冯·诺依曼(Von-Neumann)结构,数据存储器和程序存储器重合在一起.    同时,此结构也被大多数计算机所采用. ARM7为三级流水线结构(取指,译码,执行),平均功耗为0.6mW ...

  10. Learning Lua Programming (4) Cocos2d-x中Lua编程(一)

    刚开始接触cocos2d-x 下的Lua编程,主要参看了李华明大神的博客中的介绍,http://blog.csdn.net/xiaominghimi/article/category/1155088  ...