会话发起协议(SIP)

Android提供支持会话发起协议(SIP)的API。这让你可以把基于SIP的互联网电话功能添加到你的应用程序中。Android包含了一个完整的SIP协议栈,并且集成了呼叫管理服务,这样应用程序可以容易的建立呼入和呼出的连接,而不需要去管理会话、传输层的通信、音频记录、回拨等操作。

要求和限制

以下是开发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任务的API,如发起SIP连接,并提供对相关SIP服务的访问。
SipProfile 定义SIP配置,包括SIP账号、域名和服务器信息。
SipProfile.Builder 创建SipProfile类型对象的辅助类。
SipSession 代表一个跟SIP对话框或没有对话框的独立事务相关联的SIP会话。
SipSession.Listener 监听与SIP会话相关的事件,如注册会话(“on registering”)或呼出(“on calling”)的时候。
SipSession.State 定义SIP会话的状态,如“registering”,“outgoing call”和“in call”
SipRegistrationListener 监听SIP注册事件的接口。

创建清单

如果你正在开发一个使用SIP API的应用程序,那么一定要记住,该功能只在Android2.3(API Level 9)以后的版本上才被支持。还有,不是所有的运行Android2.3(API Level 9)以后版本的设备都提供对SIP的支持。

把下列权限添加到你的应用程序清单中,以便使用SIP

  • android.permission.USE_SIP

  • android.permission.INTERNET

  • 要确保你的应用程序只能被安装子提供SIP能力的设备上,就要在你的应用程序清单中添加<uses-sdk android:minSdkVersion=”9”>。这个声明指明你的应用程序要求Android2.3以上的版本。更多的信息请看API级别和<uses-sdk>元素相关的文档。

  • 要控制你的应用程序能够被那些不支持SIP的设备过滤掉(例如,在Google Play上),还要把下列声明添加到你的应用程序的清单中:
    <uses-feature android:name=”android.hardware.sip.voip” />。这就声明了你的应用程序所使用的SIP API。这个声明应该包含一个android:required数据,用它来指示你是否希望你的应用程序被不提供SIP支持的设备过滤掉。其他的<uses-feature>声明也可能需要,这依赖与你的实现。更多的信息请看<uses-feature>元素的文档。

  • 如果你的应用程序被设计成要接收呼叫,你还必须在应用程序的清单中定义一个接收器(BroadcastReceiver类的子类)。
    <receiver android:name=”.IncomingCallReceiver” android:label=”Call Receiver” />

以下是SipDemo示例清单中的摘要:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.android.sip">
  ...
     <receiver android:name=".IncomingCallReceiver" android:label="Call Receiver"/>
  ...
  <uses-sdk android:minSdkVersion="9" />
  <uses-permission android:name="android.permission.USE_SIP" />
  <uses-permission android:name="android.permission.INTERNET" />
  ...
  <uses-feature android:name="android.hardware.sip.voip" android:required="true" />
  <uses-feature android:name="android.hardware.wifi" android:required="true" />
  <uses-feature android:name="android.hardware.microphone" android:required="true" />
</manifest>

创建SipManager

要使用SIP API,你的应用程序就必须创建一个SipManager对象,在你的应用程序中,该对象负责以下工作:

  • 启动SIP会话;

  • 启动并接受呼叫;

  • 注册和注销SIP服务提供商;

  • 验证会话的联通性。

用下列方式实例化一个新的SipManager对象:

    public SipManager mSipManager = null;
    …
    if(mSipManager == null) {
        mSipManager = SipManager.newInstance(this);
    }

注册SIP服务

典型的Android SIP应用程序要涉及到一个或多个用户,每个用户都要有一个SIP账号。在一个Android SIP应用程序中,每个SIP账号由一个SipProfile对象来代表。

一个SipProfile对象定义了一个SIP配置,包括:一个SIP账号、域名和服务器信息。跟运行应用程序的设备上的SIP账号相关联的配置(profile)被叫做本地配置(local profile)。用于连接会话的配置被叫做对等配置(peer profile)。当你的SIP应用程序使用本地的SipProfile对象登录到SIP服务器时,这实际上是用注册设备所用的SIP地址来发送SIP呼叫。

本段向你介绍如何创建SipProfile对象,注册SIP服务器、跟中注册事件。

以下是创建SipProfile对象的代码:

public SipProfile mSipProfile = null;
...
SipProfile.Builder builder = new SipProfile.Builder(username, domain);
builder.setPassword(password);
mSipProfile = builder.build();

以下代码判断会打开本地配置用于拨打电话和(或)接收通用的SIP呼叫。主叫方通过mSipManager.makeAudioCall()方法可以进行后续的呼叫。这段代码还设置了android.SipDemo.INCOMING_CALL操作,该操作会在设备接收到一个呼叫时,由Intent过滤器来使用,以下是注册的步骤:

Intent intent = new Intent();
intent.setAction("android.SipDemo.INCOMING_CALL");
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, Intent.FILL_IN_DATA);
mSipManager.open(mSipProfile, pendingIntent, null);

最后,这段代码把SipRegistrationListener监听器设置到SipManager对象上。这样就跟踪SipProfile对象是否成功的注册到了你SIP服务提供商那儿:

mSipManager.setRegistrationListener(mSipProfile.getUriString(), new SipRegistrationListener() {
public void onRegistering(String localProfileUri) {
    updateStatus("Registering with SIP Server...");
}

public void onRegistrationDone(String localProfileUri, long expiryTime) {
    updateStatus("Ready");
}

public void onRegistrationFailed(String localProfileUri, int errorCode,
    String errorMessage) {
    updateStatus("Registration failed.  Please check settings.");
}

当你的应用程序完成对一个配置的使用时,它应该被关闭,以便释放跟这些对象关联的内存,并从服务器上注销该设备的连接,如:

public void closeLocalProfile() {
    if (mSipManager == null) {
       return;
    }

    try {
       if (mSipProfile != null) {
          mSipManager.close(mSipProfile.getUriString());
       }
     } catch (Exception ee) {
       Log.d("WalkieTalkieActivity/onDestroy", "Failed to close local profile.", ee);
     }
}

拨打音频电话

要拨打音频呼叫,就必须具备以下条件:

  • 一个用于拨号的SipProfile对象(本地配置),一个接收呼叫的有效的SIP地址(对等配置)。

  • 一个SipManager对象。

你应该建立一个SipAudioCall.Listener来进行音频呼叫。跟SIP协议栈的大多数客户交互都是通过这个监听器来发生的。在下面的代码中,你会看到在建立呼叫之后,SipAudioCall.Listener是如何工作的:

SipAudioCall.Listener listener = new SipAudioCall.Listener() {
   @Override
   public void onCallEstablished(SipAudioCall call) {
      call.startAudio();
      call.setSpeakerMode(true);
      call.toggleMute();
         ...
   }

   @Override
   public void onCallEnded(SipAudioCall call) {
      // Do something.
   }
};

一旦你建立了SipAudioCall.Listener,你就可以呼叫了。SipManager对象的makeAudioCall方法需要以下参数:

  • 一个本地的SIP配置(呼叫者);

  • 一个对等的SIP配置(被呼叫的用户);

  • 一个SipAudioCall.Listener监听器,用于监听来自SipAudioCall的呼叫事件。这个监听器可以是null,但是如上述代码所示,一旦建立了呼叫,这个监听器就被用于做一些事情。

  • 超时值,以秒为单位。

例如:

call = mSipManager.makeAudioCall(mSipProfile.getUriString(), sipAddress, listener,30);

接收呼叫

SIP应用程序必须包含一个BroadcastReceiver类的子类来接收呼叫,这个类具有响应指明呼叫源的Intent对象的能力。因此,你的应用程序要做以下事情:

  • 在AndroidManifest.xml中声明一个<receiver>元素。在SipDemo中的声明是:<receiver Android:name=".IncomingCallReceiver" android:label="Call Receiver"/>.

  • 实现该接收器,它是BroadcastReceiver的一个子类。在SipDemo中,这个类是IncomingCallReceiver。

  • 用呼叫本地配置文件时,触发你的接收器的PendingIntent对象来初始化本地的配置对象(SipProfile)。

  • 建立一个Intent过滤器来过滤代表呼入的操作。在SipDemo中,这个操作是android.SipDemo.INCOMING_CALL。

BroadcastReceiver的子类

要接收呼叫,你的SIP应用程序就必须实现一个BroadcastReceiver的子类。Android系统处理SIP的呼入,并且在收到呼入的时候广播一个“incoming call”的Intent对象(由应用程序定义的)。以下是来自SipDemo的BroadcastReceiver的子类的代码。要看完整的例子,请看SipDemo sample,它包含在SDK的示例中。

/*** Listens for incoming SIP calls, intercepts and hands them off to WalkieTalkieActivity.

 */

public class IncomingCallReceiver extends BroadcastReceiver {
    /**
     * Processes the incoming call, answers it, and hands it over to the
     * WalkieTalkieActivity.
     * @param context The context under which the receiver is running.
     * @param intent The intent being received.
     */

    @Override
    public void onReceive(Context context, Intent intent) {
        SipAudioCall incomingCall = null;
        try {
            SipAudioCall.Listener listener = new SipAudioCall.Listener() {
                @Override
                public void onRinging(SipAudioCall call, SipProfile caller) {
                    try {
                        call.answerCall(30);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            };

            WalkieTalkieActivity wtActivity = (WalkieTalkieActivity) context;
            incomingCall = wtActivity.mSipManager.takeAudioCall(intent, listener);
            incomingCall.answerCall(30);
            incomingCall.startAudio();
            incomingCall.setSpeakerMode(true);
            if(incomingCall.isMuted()) {
                incomingCall.toggleMute();
            }
            wtActivity.call = incomingCall;
            wtActivity.updateStatus(incomingCall);
        } catch (Exception e) {
            if (incomingCall != null) {
                incomingCall.close();
            }
        }
    }
}

建立接收呼叫的Intent过滤器

当SIP服务接到一个新的呼叫的时候,它会发出一个带有由应用程序提供的操作字符串的Intent对象。在SipDemo中,这个操作字符串是android.SipDemo.INCOMING_CALL。

以下来自SipDemo的代码摘要显示了如果用基于操作字符串android.SipDemo.INCOMING_CALL所获得的PendingIntent对象来创建SipProfile对象。PendingIntent对象会在SipProfile对象接收一个呼叫时执行一个广播处理:

public SipManager mSipManager = null;
public SipProfile mSipProfile = null;
...
Intent intent = new Intent();
intent.setAction("android.SipDemo.INCOMING_CALL");
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, Intent.FILL_IN_DATA);
mSipManager.open(mSipProfile, pendingIntent, null);

该广播会被Intent过滤器截获,然后触发接收器(IncomingCallReceiver)。你可以在你的应用程序清单文件中指定一个Intent过滤器,或者是像SipDemo示例那样在应用程序的Activity的onCreate()方法中用代码来做这件事:

public class WalkieTalkieActivity extends Activity implements View.OnTouchListener {
...
    public IncomingCallReceiver callReceiver;
    ...
    @Override
    public void onCreate(Bundle savedInstanceState) { 
       IntentFilter filter = new IntentFilter();
       filter.addAction("android.SipDemo.INCOMING_CALL");
       callReceiver = new IncomingCallReceiver();
       this.registerReceiver(callReceiver, filter);
       ...
    }
    ...
}

测试SIP应用程序

按照以下步骤来测试SIP应用程序

  • 一部运行Android2.3以上系统的移动设备。SIP是运行在无线之上的,因此你必须在实际的设备上测试,在AVD上不能进行测试工作。

  • 一个SIP账号。有很多不同的SIP提供商会提供SIP账号。

  • 如果你要进行呼叫,就必须有一个有效的SIP账号。

测试SIP应用程序:

  • 在你的设备上,接入无线(Setting->Wireless&networks->Wi-Fi->Wi-Fi settings)

  • 把你的移动设备设置为用于调试,设置方法在Developing on a Device中进行了介绍。

  • 在你的移动设备上运行你的应用程序,运行方法在Developing on a Device中进行了介绍。

  • 如果你使用的是Eclipse,你可以使用Eclipse中LogCat来查看应用程序的日志输出(Window->Show View->Other->Android->LogCat)。

Copyright© 2020-2022 li-xyz 冀ICP备2022001112号-1