2013年9月8日星期日

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.   
  13. SipUrl sipUri=smp.parseSIPUrl(uriString.toString());   
  14. 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(this0, i, Intent.FILL_IN_DATA);  
F、
SipManager.open(sipProfile,PendingIntent,null); //(实际是SIPService在做操作)设置localSIPProfile的callingID-->建立SIP连接(算是注册至SIP Server)-->打开receiveCall
其中建立SIP连接,最后能追溯到是在SipSessionGroup.java的reset()方法中通过是注册服务器实现的,如下图
QQ截图20130808164903.jpg
注册服务器的步骤为:
(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.   
  11. };  
  12. 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、类之间的关系
图片1.jpg
图片2.jpg

另外在这里给大家分享一个查看源码好用的工具SourceInsight3.5序列号+汉化【点我下载

windows下CSipSimple编译【带最新源码】

windows下CSipSimple编译【带最新源码】


发布时间:2013-07-25 16:40:05  来源:束洋洋博客  作者:束洋洋
下载后得到这样的文件:QQ截图20130725163926.jpg
解压目录后得到CSipSimple源码文件图.jpg
eclipse指定目录到这个文件夹,导入记得用这个格式:1111.jpg而非导入Android项目,记得导入的时候要两个一起导入。
导入以后,基本上就可以了,有什么问题,请Clean或者Fix Project。如果还报错,那么请右键项目CSipSimple,选择Android Tools-Add Support Libary……自动下载缺少的安卓包。
2222.jpg

好了之后,大家可以导出APK,安装到模拟设备上看看,给大家演示下
333333.jpg
444444.jpg
有什么问题欢迎在下面留言。
 
 

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



发布时间:2013-08-14 12:35:38  来源:束洋洋博客  作者:束洋洋
前几篇从Android的Voip官网支持功能到Android Voip系统实现原理到VOIP的实现,相信大家已经对Voip有了大概的了解了吧?什么,还不了解,没关系,本节给大家看下Android的SDK里自带的SipDemo,这就算实践了吧,接下来会慢慢学习怎么自己做基于SIP协议栈的软电话,希望可以做到像CsipSimple那样。
Android Sip学习(准备知识)SIP 协议完整的呼叫流程
Android Sip学习(一)Android 2.3 APIs SIP-based VoIP
Android Sip学习(二)Android VoIP系统实现原理
Android Sip学习(三)Android Voip实现
Android Sip学习(四)Android自带SipDemo详解
会话发起协议
Android提供了一个支持会话发起协议(SIP)的API,这可以让你添加基于SIP的网络电话功能到你的应用程序。Android包括一个完整的SIP协议栈和集成的呼叫管理服务,让应用轻松无需管理会话和传输层的沟通就可设置传出和传入的语音通话,或直接音频记录或播放。
以下类型的应用程序可能使用SIP API:
视频会议。
即时消息。

条件和限制
以下是开发一个SIP应用程序的条件:
你必须有一个运行Android2.3或者更高版本的移动设备。
SIP是通过无线数据连接来运行的,所以你的设备必须有一个数据连接(通过移动数据服务或者Wi-Fi)。这意味着你不能在模拟器(AVD)上进行测试,只能在一个物理设备上测试。每一个参与者在应用程序的通信会话过程中必须有一个SIP账户。有很多不同的SIP服务提供商提供SIP账户。
SIP API类和接口
以下是Android SIP API中包含的一些类和一个接口(SipRegistrationListener)的概述:
类/接口描述
 SipAudioCall 通过SIP处理网络音频电话
 SipAudioCall.Listener 关于SIP电话的事件监 听器,比如接受到一个电话(on ringing)或者呼出一个电话(on calling)的时候
 SipErrorCode 定义在SIP活动中返回的错误代码
 SipManager 为SIP任务提供APIs,比如初始化一个SIP连接。提供相关SIP服务的访问。
 SipProfile 定义了SIP的相关属性,包含SIP账户、域名和服务器信息
 SipProfile.Builder 创建SipProfile的帮助类
 SipSession 代表一个SIP会话,跟SIP对话框或者一个没有对话框的独立事务相关联
 SipSession.Listener 关于SIP会话的事件监 听器,比如注册一个会话(on registering)或者呼出一个电话(on calling)的时候
 SipSession.State 定义SIP会话的声明,比如“注册”、“呼出电话”、“打入电话”
 SipRegistrationListener 一个关于SIP注册事件监 听器的接口

 创建Manifest文件
如果你开发一个用到SIP API的应用程序,记住它需要Android2.3(API9)或者更高版本的平台的支持。所以在你的设备上要运行Android2.3(API9)或者更高的版本,并不是所有的设备都提供SIP的支持。
为了使用SIP,需要添加以下权限到你的manifest文件:
android.permission.USE_SIP
android.permission.INTERNET

为了确保你的应用程序能够安装到支持SIP的设备上,你需要添加以下内容到你应用程序的manifest文件里:
<uses-sdk android:minSdkVersion="9" />.这个设置表明你的应用程序需要Android2.3或者更高版本的平台。详情请参考API Levels和<uses-sdk>元素相关的文档。
为了控制你的应用程序被那些不支持SIP的设备过滤掉(比如:在Google Play),你需要添加以下内容到你应用程序的manifest文件里:
<uses-feature android:name="android.hardware.sip.voip" />. 这个设置声明了你的应用程序用到了SIP API。这个声明还应该包含一个android:required 属性来表明你是否想让你的应用程序被那些不提供SIP支持的设备过滤掉。其他<uses-feature>声明你也可能需要,具体取决于你的实现,详情请参考<uses- feature> 元素相关的文档。
如果你的应用程序设计用来接受呼叫,那么你还必须在应用程序的manifest文件里定义一个接收器(BroadcastReceiver 的子类):
<receiver android:name=".IncomingCallReceiver" android:label="Call Receiver"/>
以下是从SipDemo项目manifest文件中摘录的内容:
XML/HTML Code复制内容到剪贴板
  1. <?xml version="1.0" encoding="utf-8"?>  
  2.   
  3. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  4.           package="com.example.android.sip">  
  5. ……省略此部分
  6.   
  7.         <receiver android:name=".IncomingCallReceiver" android:label="Call Receiver"/>  
  8.     </application>  
  9.     <uses-sdk android:minSdkVersion="9" />  
  10.     <uses-permission android:name="android.permission.USE_SIP" />  
  11.     <uses-permission android:name="android.permission.INTERNET" />  
  12.     <uses-permission android:name="android.permission.VIBRATE" />  
  13.     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />  
  14.     <uses-permission android:name="android.permission.WAKE_LOCK" />  
  15.     <uses-permission android:name="android.permission.RECORD_AUDIO" />  
  16.   
  17.     <uses-feature android:name="android.hardware.sip.voip" android:required="true" />  
  18.     <uses-feature android:name="android.hardware.wifi" android:required="true" />  
  19.     <uses-feature android:name="android.hardware.microphone" android:required="true" />  
  20. </manifest>  

创建一个SipManager对象
要想使用SIP API,你的应用程序需要创建一个SipManager对象,这个SipManager对象在你的应用程序里负责以下内容:
发起SIP会话
发起和接受呼叫
在SIP provider里进行注册和注销
验证会话的连通性

你可以像下面一样实例化一个新的SipManager对象:
Java Code复制内容到剪贴板
  1. public SipManager mSipManager = null;  
  2. ...  
  3. if(mSipManager == null) {  
  4.     mSipManager = SipManager.newInstance(this);  
  5. }  

在SIP服务器上进行注册
一个典型的Android SIP应用中包含一个或多个用户,他们中的每个人都有一个SIP账户。在Android SIP应用中,每一个SIP账户代表一个SipProfile对象。
一个SipProfile对象定义了一个SIP的概要文件,包括SIP账户、域名和服务器信息。跟正在这个设备上运行应用的SIP账户相关联的概要文件被称之为本地配置文件。与会话相连接的概要文件被称之为对应配置文件。当你的SIP应用通过本地SipProfile登录到SIP服务器的时候,这就有效的注册当前设备为基站来发送SIP呼叫到你想呼叫的SIP地址。
本节展示了如何创建一个SipProfile,以及如何把刚创建的SipProfile注册到SIP服务器上,并且跟踪注册事件。 你可以像以下一样创建一个SipProfile对象:
Java Code复制内容到剪贴板
  1. public SipProfile mSipProfile = null;  
  2. ...  
  3.   
  4. SipProfile.Builder builder = new SipProfile.Builder(username, domain);  
  5. builder.setPassword(password);  
  6. mSipProfile = builder.build();  

接下来的代码摘录本地配置文件,用于呼出电话和/或接收通用的SIP电话。呼叫器可以通过mSipManager.makeAudioCall来呼出后续电话。这段摘录同样设置了一个android.SipDemo.INCOMING_CALL行动,这个行动会被一个intent过滤器来使用,当前设备接收到一个呼叫。以下是注册步骤:
Java Code复制内容到剪贴板
  1. Intent intent = new Intent();  
  2.  intent.setAction("android.SipDemo.INCOMING_CALL");  
  3.  PendingIntent pendingIntent = PendingIntent.getBroadcast(this0, intent, Intent.FILL_IN_DATA);  
  4.  mSipManager.open(mSipProfile, pendingIntent, null);  

最后这段代码在SipManager上设置了一个SipRegistrationListener 监 听器,这个监 听器会跟踪SipProfile是否成功的注册到你的SIP服务提供者。
Java Code复制内容到剪贴板
  1. mSipManager.setRegistrationListener(mSipProfile.getUriString(), new SipRegistrationListener() {  
  2.   
  3. public void onRegistering(String localProfileUri) {  
  4.     updateStatus("Registering with SIP Server...");  
  5. }  
  6.   
  7. public void onRegistrationDone(String localProfileUri, long expiryTime) {  
  8.     updateStatus("Ready");  
  9. }  
  10.   
  11. public void onRegistrationFailed(String localProfileUri, int errorCode,  
  12.     String errorMessage) {  
  13.     updateStatus("Registration failed.  Please check settings.");  
  14. }  

当你的应用程序使用完一个profile的时候,你应该关闭它来释放相关联的对象到内存中以及从服务器上注销当前设备。例如:
Java Code复制内容到剪贴板
  1. public void closeLocalProfile() {  
  2.     if (mSipManager == null) {  
  3.       return;  
  4.     }  
  5.     try {  
  6.        if (mSipProfile != null) {  
  7.           mSipManager.close(mSipProfile.getUriString());  
  8.        }  
  9.      } catch (Exception ee) {  
  10.        Log.d("WalkieTalkieActivity/onDestroy""Failed to close local profile.", ee);  
  11.      }  
  12. }  

拨打一个语音电话
要想拨打一个语音电话,你需要准备如下条件:
一个发起呼叫电话的SipProfile对象(本地配置文件)和一个用来接收呼叫的有效的SIP地址(对应配置文件);
一个SipManager对象。

要想拨打一个语音电话,你应该建立一个SipAudioCall.Listener监 听器。大部分客户与SIP堆栈的交互都是通过监 听器来发生的。在这一小段你将会看到SipAudioCall.Listener监 听器是如何在呼叫制定之后建立事务的:
Java Code复制内容到剪贴板
  1. SipAudioCall.Listener listener = new SipAudioCall.Listener() {  
  2.   
  3.    @Override  
  4.    public void onCallEstablished(SipAudioCall call) {  
  5.       call.startAudio();  
  6.       call.setSpeakerMode(true);  
  7.       call.toggleMute();  
  8.          ...  
  9.    }  
  10.   
  11.    @Override  
  12.    public void onCallEnded(SipAudioCall call) {  
  13.       // Do something.  
  14.    }  
  15. };  

一旦你创建了这个SipAudioCall.Listener监 听器,你就可以拨打电话了,SipManager对象里的makeAudioCall方法接受以下参数:
一个本地SIP配置文件(呼叫方)
一个相对应的SIP配置文件(被呼叫方)
一个用来监听从SipAudioCall发出的呼叫事件的SipAudioCall.Listener,这个参数可以为null,但是如上所说,一旦呼叫电话制定,这个监 听器将被用来创建事务
(超时的值,以秒为单位)

例如:
Java Code复制内容到剪贴板
  1. call = mSipManager.makeAudioCall(mSipProfile.getUriString(), sipAddress, listener, 30);  

接收呼叫
为了接收呼叫,SIP应用程序必须包含一个BroadcastReceiver的子类,这个子类得有能力响应一个表明有来电的intent。因此你需要在你的应用程序里做如下事情:
在AndroidManifest.xml文件中声明一个<receiver>元素。在SipDemo项目中,<receiver>元素是这样的<receiver android:name=".IncomingCallReceiver" android:label="Call Receiver"/>
实现BroadcastReceiver的子类,在SipDemo中,这个子类是IncomingCallReceiver
通过挂起一个intent来初始化本地配置文件(SipProfile),当有人呼叫你的时候,这个挂起的intent会调用你的接收器。
创建一个intent过滤器,这个过滤器通过标志着来电的行动来进行过滤。在SipDemo中,这个action是android.SipDemo.INCOMING_CALL

实现BroadcastReceiver的子类
为了接收呼叫,你的SIP应用必须实现BroadcastReceiver的子类。当Android系统接收到一个呼叫的时候,他会处理这个SIP呼叫,然后广播一个来电intent(这个intent由系统来定义),以下是SipDemo中实现BroadcastReceiver子类的代码。(如果想查看完整的例子,你可以下载官网的SDK,里面自带例子从android-10就开始有这个功能,下面是我的电脑截图)
QQ截图20130814145342.jpg
Java Code复制内容到剪贴板
  1. /*** Listens for incoming SIP calls, intercepts and hands them off to WalkieTalkieActivity. 
  2.  */  
  3. public class IncomingCallReceiver extends BroadcastReceiver {  
  4.     /** 
  5.      * Processes the incoming call, answers it, and hands it over to the 
  6.      * WalkieTalkieActivity. 
  7.      * @param context The context under which the receiver is running. 
  8.      * @param intent The intent being received. 
  9.      */   
  10.     @Override  
  11.     public void onReceive(Context context, Intent intent) {  
  12.         SipAudioCall incomingCall = null;  
  13.         try {  
  14.             SipAudioCall.Listener listener = new SipAudioCall.Listener() {  
  15.                 @Override  
  16.                 public void onRinging(SipAudioCall call, SipProfile caller) {  
  17.                     try {  
  18.                         call.answerCall(30);  
  19.                     } catch (Exception e) {  
  20.                         e.printStackTrace();  
  21.                     }  
  22.                 }  
  23.             };  
  24.             WalkieTalkieActivity wtActivity = (WalkieTalkieActivity) context;  
  25.             incomingCall = wtActivity.mSipManager.takeAudioCall(intent, listener);  
  26.             incomingCall.answerCall(30);  
  27.             incomingCall.startAudio();  
  28.             incomingCall.setSpeakerMode(true);  
  29.             if(incomingCall.isMuted()) {  
  30.                 incomingCall.toggleMute();  
  31.             }  
  32.             wtActivity.call = incomingCall;  
  33.             wtActivity.updateStatus(incomingCall);  
  34.         } catch (Exception e) {  
  35.             if (incomingCall != null) {  
  36.                 incomingCall.close();  
  37.             }  
  38.         }  
  39.     }  
  40. }  

创建一个用来接收呼叫的intent过滤器
当SIP服务接收到一个新的呼叫的时候,他会发送一个intent,这个intent会附带一个由应用程序提供的action。在SipDemo项目中,这个action是android.SipDemo.INCOMING_CALL。
以下从SipDemo中摘录的代码展示了如何通过挂起一个基于android.SipDemo.INCOMING_CALL action的intent来创建SipProfile对象的。PendingIntent对象将执行一个广播当SipProfile接收到一个呼叫的时候:
Java Code复制内容到剪贴板
  1. public SipManager mSipManager = null;  
  2. public SipProfile mSipProfile = null;  
  3. ...  
  4.   
  5. Intent intent = new Intent();   
  6. intent.setAction("android.SipDemo.INCOMING_CALL");   
  7. PendingIntent pendingIntent = PendingIntent.getBroadcast(this0, intent, Intent.FILL_IN_DATA);   
  8. mSipManager.open(mSipProfile, pendingIntent, null);  

上面被执行的广播如果被intent过滤器拦截的话,这个intent过滤器将会启动声明过的Receiver(IncomingCallReceiver)。你可以在你的应用程序里的manifest文件中指定一个intent过滤器,或者通过代码来指定一个intent过滤器,就像SipDemo项目中Activity中的onCreate()方法一样:
Java Code复制内容到剪贴板
  1. public class WalkieTalkieActivity extends Activity implements View.OnTouchListener {  
  2. ...  
  3.     public IncomingCallReceiver callReceiver;  
  4.     ...  
  5.   
  6.     @Override  
  7.     public void onCreate(Bundle savedInstanceState) {  
  8.   
  9.        IntentFilter filter = new IntentFilter();  
  10.        filter.addAction("android.SipDemo.INCOMING_CALL");  
  11.        callReceiver = new IncomingCallReceiver();  
  12.        this.registerReceiver(callReceiver, filter);  
  13.        ...  
  14.     }  
  15.     ...  
  16. }  

测试SIP应用程序
要测试SIP应用程序的话,你需要以下条件:
一个运行Android2.3或者更高版本的移动设备。SIP通过无线来运行,所以你必须在一个真正的设备上测试,在AVD上是测试是行不通的
一个SIP账户,有很多不同的提供SIP账户的SIP服务提供商。
如果你要打电话,这个电话必须是有效的SIP账户。
测试一个SIP应用程序的步骤:
让你的设备连接到无线(设置>无线&网络>Wi-Fi>Wi-Fi设置)
设置你的移动设备进行测试,就像在Developing on a Device里描述的一样
在你的移动设备上运行程序,就像在Developing on a Device里描述的一样
如果你正在使用Eclipse,你可以在Eclipse中查看应用程序的日志输出(Window > Show View > Other > Android > LogCat)。