Android には、セッション開始プロトコル(SIP)をサポートする API が用意されています。これにより、アプリケーションに SIP ベースのインターネット電話機能を追加できます。Android には完全な SIP プロトコル スタックと統合通話管理サービスが含まれているため、セッション、トランスポート レベルの通信、録音、再生を直接管理することなく、アプリケーションで音声通話の発信と着信を簡単に設定できます。
SIP API を使用するアプリケーションの種類の例を次に示します。
- ビデオ会議
- インスタント メッセージ
要件と制限事項
SIP アプリケーションを開発するための要件は次のとおりです。
- Android 2.3 以降を搭載しているモバイル デバイスが必要です。
- SIP はワイヤレス データ接続で動作するため、デバイスにデータ接続(モバイルデータ サービスまたは Wi-Fi)が必要です。つまり、AVD ではテストできず、物理デバイスでのみテストできます。詳細については、SIP アプリケーションのテストをご覧ください。
- アプリケーションの通信セッションの各参加者には SIP アカウントが必要です。さまざまな SIP プロバイダが SIP アカウントを提供しています。
SIP API のクラスとインターフェース
Android SIP API に含まれるクラスとインターフェース(SipRegistrationListener
)の概要は次のとおりです。
クラス / インターフェース | 説明 |
---|---|
SipAudioCall |
SIP を介したインターネット音声通話を処理します。 |
SipAudioCall.Listener |
通話の受信時(着信中)や通話の送信時(発信中)など、SIP 通話に関連するイベントのリスナー。 |
SipErrorCode |
SIP アクション中に返されるエラーコードを定義します。 |
SipManager |
SIP 接続の開始など、SIP タスク用の API を提供し、関連する SIP サービスへのアクセスを提供します。 |
SipProfile |
SIP アカウント、ドメイン、サーバー情報を含む SIP プロファイルを定義します。 |
SipProfile.Builder |
SipProfile を作成するためのヘルパークラス。 |
SipSession |
SIP ダイアログに関連付けられている、またはダイアログ内にないスタンドアロン トランザクションに関連付けられている SIP セッションを表します。 |
SipSession.Listener |
セッションの登録時(登録中)や通話の送信時(発信中)など、SIP セッションに関連するイベントのリスナー。 |
SipSession.State |
「登録」、「発信」、「着信」など、SIP セッションの状態を定義します。 |
SipRegistrationListener |
SIP 登録イベントのリスナーであるインターフェース。 |
マニフェストの作成
SIP API を使用するアプリケーションを開発する場合、この機能は Android 2.3(API レベル 9)以降のプラットフォームでのみサポートされることに注意が必要です。また、Android 2.3(API レベル 9)以降を搭載しているデバイスでは、すべてのデバイスが SIP をサポートするわけではありません。
SIP を使用するには、アプリケーションのマニフェストに次の権限を追加します。
android.permission.USE_SIP
android.permission.INTERNET
SIP をサポートできるデバイスにのみアプリケーションをインストールできるようにするには、アプリケーションのマニフェストに次の行を追加します。
<uses-sdk android:minSdkVersion="9" />
これは、アプリケーションに Android 2.3 以降が必要であることを示しています。詳細については、API レベルと、<uses-sdk>
要素のドキュメントをご覧ください。
SIP をサポートしていないデバイスからアプリケーションを(たとえば Google Play で)除外する方法を制御するには、アプリケーションのマニフェストに次の行を追加します。
<uses-feature android:name="android.hardware.sip.voip" />
これは、アプリケーションが SIP API を使用していることを示しています。宣言には、SIP サポートを提供しないデバイスからアプリケーションを除外するかどうかを示す android:required
属性を含める必要があります。実装によっては、他の <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
オブジェクトを作成する必要があります。SipManager
は、アプリケーションで次の処理を行います。
- SIP セッションの開始。
- 通話の開始と受信。
- SIP プロバイダへの登録と登録解除。
- セッション接続の確認。
新しい SipManager
を次のようにインスタンス化します。
Kotlin
val sipManager: SipManager? by lazy(LazyThreadSafetyMode.NONE) { SipManager.newInstance(this) }
Java
public SipManager sipManager = null; ... if (sipManager == null) { sipManager = SipManager.newInstance(this); }
SIP サーバーへの登録
一般的な Android SIP アプリケーションには、SIP アカウントを持つユーザーが 1 人以上関係します。Android SIP アプリケーションでは、各 SIP アカウントは SipProfile
オブジェクトによって表されます。
SipProfile
は、SIP アカウント、ドメイン、サーバーの情報を含む SIP プロファイルを定義します。アプリケーションを実行しているデバイスの SIP アカウントに関連付けられたプロファイルを、ローカル プロファイルといいます。セッションが接続されているプロファイルを、ピア プロファイルといいます。SIP アプリケーションがローカル SipProfile
で SIP サーバーにログインすると、SIP アドレスの SIP 通話の送信先としてデバイスが効率的に登録されます。
このセクションでは、SipProfile
を作成して SIP サーバーに登録し、登録イベントをトラッキングする方法について説明します。
SipProfile
オブジェクトは、次のように作成します。
Kotlin
private var sipProfile: SipProfile? = null ... val builder = SipProfile.Builder(username, domain) .setPassword(password) sipProfile = builder.build()
Java
public SipProfile sipProfile = null; ... SipProfile.Builder builder = new SipProfile.Builder(username, domain); builder.setPassword(password); sipProfile = builder.build();
次のコードの抜粋は、一般的な SIP 通話の発信や受信を行うためのローカル プロファイルを開きます。発信側は、mSipManager.makeAudioCall
を介して後続の発信を行えます。この抜粋では、アクション android.SipDemo.INCOMING_CALL
も設定します。これは、デバイスが通話を受信したときにインテント フィルタによって使用されます(通話を受信するためのインテント フィルタの設定をご覧ください)。これは登録手順です。
Kotlin
val intent = Intent("android.SipDemo.INCOMING_CALL") val pendingIntent: PendingIntent = PendingIntent.getBroadcast(this, 0, intent, Intent.FILL_IN_DATA) sipManager?.open(sipProfile, pendingIntent, null)
Java
Intent intent = new Intent(); intent.setAction("android.SipDemo.INCOMING_CALL"); PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, Intent.FILL_IN_DATA); sipManager.open(sipProfile, pendingIntent, null);
最後に、このコードは SipManager
に SipRegistrationListener
を設定します。これにより、SipProfile
が SIP サービス プロバイダに正常に登録されたかどうかがトラッキングされます。
Kotlin
sipManager?.setRegistrationListener(sipProfile?.uriString, object : SipRegistrationListener { override fun onRegistering(localProfileUri: String) { updateStatus("Registering with SIP Server...") } override fun onRegistrationDone(localProfileUri: String, expiryTime: Long) { updateStatus("Ready") } override fun onRegistrationFailed( localProfileUri: String, errorCode: Int, errorMessage: String ) { updateStatus("Registration failed. Please check settings.") } })
Java
sipManager.setRegistrationListener(sipProfile.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."); } }
プロファイルを使用してアプリケーションを終了すると、プロファイルが閉じられて、関連付けられたオブジェクトがメモリに解放され、デバイスがサーバーから登録解除されます。次に例を示します。
Kotlin
fun closeLocalProfile() { try { sipManager?.close(sipProfile?.uriString) } catch (ee: Exception) { Log.d("WalkieTalkieActivity/onDestroy", "Failed to close local profile.", ee) } }
Java
public void closeLocalProfile() { if (sipManager == null) { return; } try { if (sipProfile != null) { sipManager.close(sipProfile.getUriString()); } } catch (Exception ee) { Log.d("WalkieTalkieActivity/onDestroy", "Failed to close local profile.", ee); } }
音声通話を発信する
音声通話を発信するには、次のものが必要です。
- 通話を発信する
SipProfile
(ローカル プロファイル)と、通話を受信するための有効な SIP アドレス(ピア プロファイル)。 SipManager
オブジェクト。
音声通話を発信するには、SipAudioCall.Listener
を設定する必要があります。クライアントと SIP スタックのインタラクションの多くは、リスナーを介して行われます。このスニペットでは、通話が確立した後に SipAudioCall.Listener
がどのように設定を行うかを示しています。
Kotlin
var listener: SipAudioCall.Listener = object : SipAudioCall.Listener() { override fun onCallEstablished(call: SipAudioCall) { call.apply { startAudio() setSpeakerMode(true) toggleMute() } } override fun onCallEnded(call: SipAudioCall) { // Do something. } }
Java
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
でもかまいませんが、上記のように、通話が確立されるとリスナーを使用して設定が行われます。- タイムアウト値(秒)。
次に例を示します。
Kotlin
val call: SipAudioCall? = sipManager?.makeAudioCall( sipProfile?.uriString, sipAddress, listener, 30 )
Java
call = sipManager.makeAudioCall(sipProfile.getUriString(), sipAddress, listener, 30);
通話を受信する
通話を受信するには、着信があることを示すインテントに応答する機能を持つ BroadcastReceiver
のサブクラスが SIP アプリケーションに含まれている必要があります。したがって、アプリケーションで次のようにする必要があります。
AndroidManifest.xml
で<receiver>
を宣言する。SipDemo では、<receiver android:name=".IncomingCallReceiver" android:label="Call Receiver" />
です。- 受信側(
BroadcastReceiver
のサブクラス)を実装する。SipDemo では、IncomingCallReceiver
です。 - ローカル プロファイルが呼び出されたときに受信側を起動するペンディング インテントで、ローカル プロファイル(
SipProfile
)を初期化する。 - 着信を表すアクションでフィルタするインテント フィルタを設定する。SipDemo では、このアクションは
android.SipDemo.INCOMING_CALL
です。
BroadcastReceiver のサブクラス化
通話を受信するには、SIP アプリケーションで BroadcastReceiver
をサブクラス化する必要があります。Android システムは、着信 SIP 通話を処理し、通話を受信すると「着信」インテントをブロードキャストします(アプリケーションで定義されているとおり)。SipDemo サンプル のサブクラス化された
BroadcastReceiver
コードを次に示します。
Kotlin
/** * Listens for incoming SIP calls, intercepts and hands them off to WalkieTalkieActivity. */ class IncomingCallReceiver : 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 fun onReceive(context: Context, intent: Intent) { val wtActivity = context as WalkieTalkieActivity var incomingCall: SipAudioCall? = null try { incomingCall = wtActivity.sipManager?.takeAudioCall(intent, listener) incomingCall?.apply { answerCall(30) startAudio() setSpeakerMode(true) if (isMuted) { toggleMute() } wtActivity.call = this wtActivity.updateStatus(this) } } catch (e: Exception) { incomingCall?.close() } } private val listener = object : SipAudioCall.Listener() { override fun onRinging(call: SipAudioCall, caller: SipProfile) { try { call.answerCall(30) } catch (e: Exception) { e.printStackTrace() } } } }
Java
/** * 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.sipManager.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(); } } } }
通話を受信するためのインテント フィルタの設定
SIP サービスは、新しい通話を受信すると、アプリケーションが提供するアクション文字列を使用してインテントを送信します。SipDemo では、このアクション文字列は android.SipDemo.INCOMING_CALL
です。
このコードは SipDemo から抜粋したもので、アクション文字列 android.SipDemo.INCOMING_CALL
に基づきペンディング インテントで SipProfile
オブジェクトがどのように作成されるかを示しています。PendingIntent
オブジェクトは、SipProfile
が通話を受信したときにブロードキャストを行います。
Kotlin
val sipManager: SipManager? by lazy(LazyThreadSafetyMode.NONE) { SipManager.newInstance(this) } var sipProfile: SipProfile? = null ... val intent = Intent("android.SipDemo.INCOMING_CALL") val pendingIntent: PendingIntent = PendingIntent.getBroadcast(this, 0, intent, Intent.FILL_IN_DATA) sipManager?.open (sipProfile, pendingIntent, null)
Java
public SipManager sipManager = null; public SipProfile sipProfile = null; ... Intent intent = new Intent(); intent.setAction("android.SipDemo.INCOMING_CALL"); PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, Intent.FILL_IN_DATA); sipManager.open(sipProfile, pendingIntent, null);
ブロードキャストはインテント フィルタによってインターセプトされ、受信側(IncomingCallReceiver
)が起動されます。インテント フィルタは、アプリケーションのマニフェスト ファイルで、または SipDemo サンプル アプリケーションの Activity
の onCreate()
メソッドのようなコードで指定できます。
Kotlin
class WalkieTalkieActivity : Activity(), View.OnTouchListener { ... lateinit var callReceiver: IncomingCallReceiver ... override fun onCreate(savedInstanceState: Bundle) { val filter = IntentFilter().apply { addAction("android.SipDemo.INCOMING_CALL") } callReceiver = IncomingCallReceiver() this.registerReceiver(callReceiver, filter) ... } ... }
Java
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 アプリケーションをテストするには、次のものが必要です。
- Android 2.3 以降を搭載しているモバイル デバイス。SIP はワイヤレスで動作するため、実際のデバイスでテストする必要があります。AVD でのテストは機能しません。
- SIP アカウント。さまざまな SIP プロバイダが SIP アカウントを提供しています。
- 通話を発信する場合は、有効な SIP アカウントにも発信する必要があります。
SIP アプリケーションをテストする手順は次のとおりです。
- デバイスでワイヤレス接続します([設定] > [無線とネットワーク] > [Wi-Fi] > [Wi-Fi 設定])。
- デバイスでの開発の説明に沿って、モバイル デバイスをテスト用に設定します。
- デバイスでの開発の説明に従って、モバイル デバイスでアプリケーションを実行します。
- Android Studio を使用している場合は、Event Log コンソールを開きます([View] > [Tool Windows] > [Event Log])。
- 実行時に Logcat が自動的に起動するようにアプリケーションが設定されていることを確認します。
- [Run] > [Edit Configurations] の順に選択します。
- [Run/Debug Configurations] ウィンドウで [Miscellaneous] タブを選択します。
- [Logcat] で [Show logcat automatically] を選択し、[OK] を選択します。