eSIM 및 SIM 카드 감지
카드 감지
SIM 카드 및 eSIM이 있는 Android 기기는 전화 통신에서 다음 ID를 사용합니다. API에는 [`TelephonyManager`](/reference/android/telephony/TelephonyManager) 및 [`SubscriptionManager`](/reference/android/telephony/SubscriptionManager): * 구독 ID: 모바일 구독을 위한 고유 ID입니다. * 논리 슬롯 인덱스 또는 ID: 논리 SIM 슬롯을 참조하는 고유 인덱스. 논리 슬롯 ID는 0에서 시작하고 모든 활성 슬롯을 지원합니다. 예를 들어 듀얼 SIM 기기는 일반적으로 슬롯 0과 슬롯 1이 있습니다. 기기에 물리적 슬롯이 여러 개 있지만 하나의 활성 슬롯을 지원하는 경우 논리 슬롯 ID 0만 있을 것입니다. * 물리적 슬롯 인덱스 또는 ID: 실물 SIM 슬롯을 나타내는 고유 인덱스. 물리적 슬롯 ID는 0에서 시작하고 실제 슬롯 ID의 수에 따라 올라갑니다. 슬롯이 있을 수 있습니다 이는 기기의 논리 슬롯의 수와는 다릅니다. 이는 기기가 사용할 수 있는 활성 슬롯의 수에 해당합니다. 있습니다. 듀얼 SIM과 단일 SIM 간에 전환하는 기기를 예로 들 수 있습니다. 모드는 항상 두 개의 물리적 슬롯을 가질 수 있지만, 단일 SIM 모드에서는 하나의 논리 슬롯만 있어야 합니다 * 카드 ID: UiccCard를 식별하는 데 사용되는 고유 ID. ![논리 슬롯이 2개이고 물리적 슬롯이 3개인 경우 ID가 사용되는 방식의 다이어그램](/images/guide/topics/connectivity/tel-ids.png) 위 다이어그램에서: * 기기에는 두 개의 논리 슬롯이 있습니다. * 물리적 슬롯 0에는 활성 프로필이 있는 물리적 UICC 카드가 있습니다. * 물리적 슬롯 2에는 활성 프로필이 있는 eUICC가 있습니다. * 물리적 슬롯 1은 현재 사용되지 않습니다. ![논리 슬롯 3개와 물리적 슬롯 2개가 있는 사례에서 ID가 사용되는 방식의 다이어그램](/images/guide/topics/connectivity/tel-ids-2.png) 위 다이어그램에서: * 기기에는 3개의 논리 슬롯이 있습니다. * 물리적 슬롯 0에는 활성 프로필이 있는 물리적 UICC 카드가 있습니다. * 물리적 슬롯 1에는 다운로드된 프로필 두 개가 있는 eUICC가 있습니다. 두 프로필 모두 MEP (다중 지원 프로필)를 사용하는 활성 상태입니다.
세션 시작 프로토콜 개요
Android는 세션 시작 프로토콜(SIP)을 지원하는 API를 제공합니다. 이를 통해 SIP 기반 인터넷 전화 통신 기능을 애플리케이션에 추가할 수 있습니다. Android에는 전체 SIP 프로토콜 스택 및 통합 통화 관리 기능이 포함되어 있습니다. 애플리케이션이 쉽게 발신 및 수신 음성 통화를 설정하고 세션, 전송 수준 통신 또는 오디오를 관리할 필요 없이 직접 녹화 또는 재생할 수 없습니다.
SIP API를 사용할 수 있는 애플리케이션 유형의 예는 다음과 같습니다.
- 화상 회의
- 채팅
요구사항 및 제한사항
SIP 애플리케이션 개발을 위한 요구사항은 다음과 같습니다.
- Android 2.3 이상을 실행하는 휴대기기가 있어야 합니다.
- SIP는 무선 데이터 연결을 통해 실행되므로 기기에 데이터가 있어야 합니다. (모바일 데이터 서비스 또는 Wi-Fi 사용) 즉, AVD에서는 테스트할 수 없습니다. 실제 기기에서만 테스트할 수 있습니다. 자세한 내용은 SIP 애플리케이션 테스트
- 애플리케이션 통신 세션의 각 참가자는 SIP 계정. SIP 계정을 제공하는 여러 다양한 SIP 제공업체가 있습니다.
참고: android.net.sip
라이브러리는 동영상을 지원하지 않습니다.
있습니다. 다음과 같은 SIP 스택을 사용하여 VOIP 통화를 구현하려는 경우
android.net.sip
님, 다양한 최신 오픈소스 중 하나를 살펴보세요
VOIP 통화 구현의 기반으로 사용할 수 있습니다. 이와 달리
kubectl run 명령어를 사용하여
ConnectionService
드림
이러한 호출을 기기의 다이얼러에 긴밀하게 통합하는 API
있습니다.
SIP API 클래스 및 인터페이스
다음은 클래스와 한 개의 인터페이스에 대한 요약입니다.
Android SIP에 포함된 (SipRegistrationListener
)
API:
클래스/인터페이스 | 설명 |
---|---|
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를 지원할 수 있는 경우 다음을 애플리케이션의 매니페스트:
<uses-sdk android:minSdkVersion="9" />
이는 애플리케이션에 Android 2.3 이상이 필요함을 나타냅니다. 대상
자세한 내용은
API 수준
및
<ph type="x-smartling-placeholder"><uses-sdk>
</ph>
요소가 포함됩니다.
지원하지 않는 기기에서 애플리케이션이 필터링되는 방식을 제어하기 위해 SIP (예: Google Play)는 애플리케이션의 매니페스트:
<uses-feature android:name="android.software.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" /> ... <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.software.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 애플리케이션이 SIP 서버에 로그인할 때
로컬 SipProfile
를 전달하면
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.Listener
SipAudioCall
의 이벤트null
, 그러나 위와 같이 리스너는 호출이 완료되면 있습니다. - 시간 제한 값(초)
예를 들면 다음과 같습니다.
Kotlin
val call: SipAudioCall? = sipManager?.makeAudioCall( sipProfile?.uriString, sipAddress, listener, 30 )
자바
call = sipManager.makeAudioCall(sipProfile.getUriString(), sipAddress, listener, 30);
전화 받기
전화를 받으려면 SIP 애플리케이션에 인텐트에 응답할 수 있는 BroadcastReceiver
의 서브클래스가 포함되어야 합니다.
수신 전화가 있음을 나타냅니다. 따라서 다음을 실행해야 합니다.
애플리케이션:
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 통화를 처리하고
애플리케이션이 정의한 대로' 인텐트를 호출합니다.
있습니다. 서브클래스화된
BroadcastReceiver
는 다음과 같습니다.
SipDemo 샘플의 코드를 살펴보세요.
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
입니다. 이
SipProfile
가 전화를 받으면 PendingIntent
객체는 브로드캐스트를 실행합니다.
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
) 인텐트를 지정할 수 있습니다.
애플리케이션의 매니페스트 파일에서 필터를 설정하거나 SipDemo에서와 같이 코드에서 직접 설정합니다.
샘플 애플리케이션의 onCreate()
메서드
애플리케이션의 Activity
:
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을 자동으로 실행하도록 구성되어 있는지 확인합니다.
<ph type="x-smartling-placeholder">
- </ph>
- Run > Edit Configurations를 선택합니다.
- Run/Debug Configurations 창에서 Miscellaneous 탭을 선택합니다.
- Logcat에서 Show logcat 자동으로를 선택합니다. 확인을 선택합니다.