Android 中的SIP協議

概述:

Android提供了支持SIP(SessionInitiation Protocol)協議的API. 這讓我們可以向APP中添加基於SIP的網絡電話功能. Android包括了完整的SIP協議棧並集成了呼叫管理服務, 它可以讓APP經過簡單的設置就可以支持呼入和呼出, 而不用必須管理會話, 傳輸級別, 通信或者音頻記錄或者直接播放. 這裏有兩種APP可能使用到的SIP API: Video conferencing和Instant messaging.

要求和限制:

這是開發SIP APP的要求:

1.      必須有個移動設備並且配備Android2.3及更高版本.

2.      SIP通過一個無線數據連接來運行,所以設備必須有一個數據連接(通過移動數據服務或者WiFi). 這意味着我們不能用AVD來測試--只能在物理設備上測試.

3.      每個APP連接會話的參與者必須擁有一個SIP賬號. 有很多不同的SIP提供者提供SIP賬號.

SIPAPI類和接口:

下面是Android SIP API中類和接口的概述:

類/接口

描述

SipAudioCall

通過SIP處理一個網絡音頻呼叫.

SipAudioCall.Listener

SIP呼叫相關的事件監聽器, 比如什麼時候收到呼叫(on ringing)或者發出呼叫(on calling).

SipErrorCode

定義SIP操作期間返回的錯誤碼.

SipManager

提供SIP任務的API, 比如啓動SIP連接, 提供相關SIP服務的訪問.

SipProfile

定義一個SIP配置文件, 包括SIP賬戶, 域和服務器信息.

SipProfile.Builder

創建SIP配置文件的幫助類.

SipSession

代表一個SIP會話. 該會話與一個SIP dialog相關聯或者不與dialog關聯的獨立的事務.

SipSession.Listener

SIP會話相關的監聽器, 比如當一個會話將要註冊的時候(on registering)或者將要呼出的時候(on calling).

SipSession.State

定義SIP會話狀態, 比如”註冊”, “呼叫”, “呼入”.

SipRegistrationListener

一個接口, 是SIP註冊事件的監聽器.

創建Manifest:

如果我們要開發一個使用SIP API的APP, 需要記得該功能只有在Android2.3及以上版本才能支持. 同樣, 在運行Android2.3及以上版本的設備中, 並非所有設備都提供對SIP的支持. 想要使用SIP, 要增加下列的權限到APP的manifest中:

l  android.permission.USE_SIP

l  android.permission.INTERNET

要確保APP可以只被安裝在支持SIP的設備上, 需要增加如下權限:

l  <uses-sdk android:minSdkVersion=”9” />. 這表示我們的APP需要Android2.3及更高的版本.

要控制APP如何過濾不支持SIP的設備(比如在Google Play上), 增加下列權限到manifest中:

l  <uses-feature android:name=”android.hardware.sip.voip” />. 這聲明瞭APP使用SIP API. 聲明應該包含一個android:required屬性, 表示我們是否希望APP被從不支持SIP的設備中過濾.

如果APP被設計來接收呼叫, 我們必須在manifest中定義一個接收器(BroadcastReceiver的子類):

l  <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, 我們的APP必須創建一個SipManager對象. SipManager可以實現這些功能:

l  初始化SIP會話.

l  初始化並接收會話.

l  使用SIPprovider註冊和註銷.

l  驗證會話連接.

我們可以這樣初始化一個SipManager:

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

註冊一個SIP服務器:

一個典型的Android SIP APP涉及到一個或者多個用戶, 他們中的每一個都有一個賬戶. 在一個Android SIP APP中, 每個SIP賬戶通過SipProfile對象來表示.

一個SipProfile定義了了一個SIP配置文件, 包括一個SIP賬戶, 一個域和服務器信息.跟SIP賬戶關聯的配置文件叫做本地配置文件. 遠端的設備的配置文件叫做peer配置文件. When your SIP application logs into the SIP server with the localSipProfile, this effectively registers the device as the location to send SIPcalls to for your SIP address.

本小節展示瞭如何創建一個SipProfile, 註冊它到一個SIP server, 並跟蹤註冊事件. 我們可以這樣創建一個SipProfile對象:

public SipProfile mSipProfile = null;
...

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

下面的代碼爲呼叫和/或接收通用SIP呼叫打開了本地配置文件. 呼叫者可以通過mSipManager.makeAudioCall做後續調用. 這段代碼還設置了actionandroid.SipDemo.INCOMING_CALL, 它將會在設備接受一個呼叫的時候被intent filter使用:

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);

最後, 這段代碼爲SipManager設置了一個SipRegistrationListener. 它用於監聽SipProfile是否成功的註冊到了我們的SIP服務provider.

mSipManager.setRegistrationListener(mSipProfile.getUriString(), new SipRegistrationListener() {

public void onRegistering(String localProfileUri) {
    updateStatus("Registeringwith SIP Server...");
}

public void onRegistrationDone(String localProfileUri, long expiryTime) {
    updateStatus("Ready");
}
   
public void onRegistrationFailed(String localProfileUri, int errorCode,
    String errorMessage) {
    updateStatus("Registrationfailed.  Please check settings.");
}

當我們的APP用完了Profile的時候, 它應該被關閉, 並釋放相關對象和從服務器註銷設備:

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

啓動一個音頻呼叫:

想要啓動一個音頻呼叫, 我們必須已經定製這些內容:

l  一個SipProfile負責啓動呼叫(localprofile), 和一個可用的SIP地址來接收呼叫(peer profile).

l  一個SipManager對象.

要啓動一個音頻呼叫, 我們應該設置一個SipAudioCall.Listener. 大部分客戶端的交互發生在listener中. 下面的代碼段裏, 我們將看到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需要下面的參數:

l  一個本地SIP配置文件(caller).

l  一個peer SIP配置文件(userbeing called).

l  一個SipAudioCall.Listener來監聽從SipAudioCall來的呼叫事件. 可以爲空, 但是就像上面講的, 監聽器是一旦呼叫建立了就可以設置了.

l  超時的單位是秒

栗子:

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

接收呼叫:

想要接收呼叫, 一個SIP APP必須包含一個BroadcastReceiver的子類, 它要有能力響應呼入的intent. 所以必須做下面這幾件事:

l  在AndroidManifest.xml中, 聲明一個<receiver>.在SipDemo中, 它長這樣: <receiver android:name=".IncomingCallReceiver"android:label="Call Receiver"/>.

l  實現receiver, 它是BroadcastReceiver的子類. 在SipDemo中, 這個類是IncomingCallReceiver.

l  用一個pendingintent初始化本地配置文件(SipProfile).

l  設置一個intentfilter來過濾代表呼入的action. 在SipDemo中, 這個action是android.SipDemo.INCOMING_CALL.

實現BroadcastReceiver的子類:

想要接收呼入, SIP APP必須繼承BroadcastReceiver的子類. 當有呼叫接入的時候Android系統處理SIP呼入並廣播一個呼入intent. 下面是SipDemo中實現BroadcastReceiver的代碼:

/*** 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 filter來接收呼入:

當SIP服務器收到了一個新的呼入, 它將發出一個帶有action string的intent. 在SipDemo中, 這個action是android.SipDemo.INCOMING_CALL.

下面來自SipDemo的代碼片段展示了SipProfile對象如何通過一個基於action爲android.SipDemo.INCOMING_CALL的pending intent創建的. 當SipProfile收到一個呼入的時候PendingIntent對象將會執行一個廣播:

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 filter截獲, 它將發給receiver(IncomingCallReceiver). 我們可以在manifest中指定一個intentfilter, 或者在APP的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 APP:

要測試SIP APP, 需要這些:

l  一個Android2.3或以上的移動設備. SIP通過無線網運行. 我們必須在真機上測試, 虛擬設備無效.

l  一個SIP賬戶. 有很多不同的SIP提供者可以提供SIP賬戶.

l  如果我們要發起一個呼叫, 那麼對方必須也是一個可用的SIP賬戶.

要測試一個SIP APP需要這樣幹:

1.      設備要連接無線網.

2.      設置移動設備爲測試模式, 就像在Developingon a Device中描述的那樣.

3.      在移動設備上運行APP.

4.      如果使用Android Studio, 我們可以通過Event log console查看APP的log輸出.

5.      啓動APP並查看日誌…..囧

 

參考: https://developer.android.com/guide/topics/connectivity/sip.html

 

發佈了81 篇原創文章 · 獲贊 4 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章