會話發起協議 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 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"/>
- </application>
- <uses-sdk android:minSdkVersion="9" />
- <uses-permission android:name="android.permission.USE_SIP" />
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.VIBRATE" />
- <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
- <uses-permission android:name="android.permission.WAKE_LOCK" />
- <uses-permission android:name="android.permission.RECORD_AUDIO" />
- <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對象,這個SipManager對象在你的應用程序里負責以下內容: 發起SIP會話 發起和接受呼叫 在SIP provider里進行注冊和注銷 驗證會話的連通性
你可以像下面一樣實例化一個新的SipManager對象:
- public SipManager mSipManager = null;
- ...
- if(mSipManager == null) {
- mSipManager = SipManager.newInstance(this);
- }
在SIP服務器上進行注冊 一個典型的Android SIP應用中包含一個或多個用戶,他們中的每個人都有一個SIP賬戶。在Android SIP應用中,每一個SIP賬戶代表一個SipProfile對象。 一個SipProfile對象定義了一個SIP的概要文件,包括SIP賬戶、域名和服務器信息。跟正在這個設備上運行應用的SIP賬戶相關聯的概要文件被稱之為本地配置文件。與會話相連接的概要文件被稱之為對應配置文件。當你的SIP應用通過本地SipProfile登錄到SIP服務器的時候,這就有效的注冊當前設備為基站來發送SIP呼叫到你想呼叫的SIP地址。 本節展示了如何創建一個SipProfile,以及如何把剛創建的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);
最后這段代碼在SipManager上設置了一個SipRegistrationListener 監 聽器,這個監 聽器會跟蹤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.");
- }
當你的應用程序使用完一個profile的時候,你應該關閉它來釋放相關聯的對象到內存中以及從服務器上注銷當前設備。例如:
- 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發出的呼叫事件的SipAudioCall.Listener,這個參數可以為null,但是如上所說,一旦呼叫電話制定,這個監 聽器將被用來創建事務 (超時的值,以秒為單位)
例如:
- 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就開始有這個功能,下面是我的電腦截圖)
- /*** 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,這個intent會附帶一個由應用程序提供的action。在SipDemo項目中,這個action是android.SipDemo.INCOMING_CALL。 以下從SipDemo中摘錄的代碼展示了如何通過掛起一個基於android.SipDemo.INCOMING_CALL action的intent來創建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過濾器攔截的話,這個intent過濾器將會啟動聲明過的Receiver(IncomingCallReceiver)。你可以在你的應用程序里的manifest文件中指定一個intent過濾器,或者通過代碼來指定一個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應用程序的步驟: 讓你的設備連接到無線(設置>無線&網絡>Wi-Fi>Wi-Fi設置) 設置你的移動設備進行測試,就像在Developing on a Device里描述的一樣 在你的移動設備上運行程序,就像在Developing on a Device里描述的一樣 如果你正在使用Eclipse,你可以在Eclipse中查看應用程序的日志輸出(Window > Show View > Other > Android > LogCat)。