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 등록 이벤트의 리스너인 인터페이스입니다. |
manifest 생성
SIP API를 사용하는 애플리케이션을 개발하고 있다면 이 기능이 Android 2.3(API 레벨 9) 이상 버전의 플랫폼에서만 지원된다는 점에 유의하세요. 또한 Android 2.3(API 레벨 9) 이상을 실행하는 기기 중에서도 일부 기기만 SIP를 지원합니다.
SIP를 사용하려면 애플리케이션의 manifest에 다음 권한을 추가하세요.
android.permission.USE_SIP
android.permission.INTERNET
SIP를 지원할 수 있는 기기에만 애플리케이션이 설치될 수 있도록 하려면 애플리케이션의 manifest에 다음을 추가하세요.
<uses-sdk android:minSdkVersion="9" />
이는 애플리케이션에 Android 2.3 이상이 필요함을 나타냅니다. 자세한 내용은 API 레벨 및 <uses-sdk>
요소에 관한 문서를 참조하세요.
SIP를 지원하지 않는 기기에서(예를 들어 Google Play에서) 애플리케이션이 필터링되는 방식을 제어하려면 애플리케이션의 manifest에 다음을 추가하세요.
<uses-feature android:name="android.hardware.sip.voip" />
이는 애플리케이션이 SIP API를 사용함을 명시한 것입니다. 선언에는 애플리케이션이 SIP를 지원하지 않는 기기에서 필터링되어야 하는지 여부를 나타내는 android:required
속성을 포함해야 합니다.
또한 구현에 따라 다른 <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" /> ... <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) }
자바
public SipManager sipManager = null; ... if (sipManager == null) { sipManager = SipManager.newInstance(this); }
SIP 서버에 등록
일반적인 Android SIP 애플리케이션에는 각기 SIP 계정을 보유하는 사용자가 한 명 이상 포함됩니다. 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()
자바
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)
자바
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.") } })
자바
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) } }
자바
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. } }
자바
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 )
자바
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() } } } }
자바
/** * 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에서 발췌한 이 코드는 SipProfile
객체가 작업 문자열 android.SipDemo.INCOMING_CALL
을 기반으로 대기 중인 인텐트를 통해 어떻게 생성되는지 보여줍니다. 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)
자바
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
)가 실행됩니다. 인텐트 필터를 애플리케이션의 manifest 파일에서 지정하거나 다음 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) ... } ... }
자바
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 스튜디오를 사용한다면 Event Log 콘솔(View > Tool Windows > Event Log)을 열어 애플리케이션 로그 출력을 확인할 수 있습니다.
- 다음과 같이 애플리케이션이 실행될 때 Logcat을 자동으로 시작하도록 구성되어 있는지 확인합니다.
- Run > Edit Configurations를 선택합니다.
- Run/Debug Configurations 창에서 Miscellaneous 탭을 선택합니다.
- Logcat에서 Show logcat automatically, OK를 차례로 선택합니다.