통화 앱 빌드

통화 앱을 통해 사용자는 기기에서 음성 통화 또는 영상 통화를 받거나 걸 수 있습니다. 다음 스크린샷과 같이 통화 앱은 통화 시 기본 전화 앱 인터페이스를 사용하는 대신 자체 사용자 인터페이스를 사용합니다.

통화 앱의 예
자체 사용자 인터페이스를 사용하는 통화 앱의 예

Android 프레임워크에는 android.telecom 패키지가 포함되어 있으며 이 패키지에는 텔레콤 프레임워크에 따라 통화 앱을 빌드하는 데 도움이 되는 클래스가 포함되어 있습니다. 텔레콤 프레임워크에 따라 앱을 빌드하면 다음과 같은 이점이 있습니다.

  • 앱이 기기의 네이티브 텔레콤 하위 시스템과 올바르게 상호 운용됩니다.
  • 앱이 프레임워크를 준수하는 다른 통화 앱과도 올바르게 상호 운용됩니다.
  • 프레임워크를 통해 앱이 음성 및 영상 라우팅을 관리할 수 있습니다.
  • 프레임워크를 통해 앱이 통화에 포커스가 있는지 여부를 확인할 수 있습니다.

매니페스트 선언 및 권한

앱 매니페스트에서 다음 예와 같이 앱이 MANAGE_OWN_CALLS 권한을 사용한다고 선언합니다.

<manifest … >
    <uses-permission android:name="android.permission.MANAGE_OWN_CALLS"/>
</manifest>

앱 권한 선언에 관한 자세한 내용은 권한을 참조하세요.

앱에서 ConnectionService 클래스를 구현하는 클래스를 지정하는 서비스를 선언해야 합니다. 텔레콤 하위 시스템에서는 서비스가 시스템에 결합할 수 있도록 BIND_TELECOM_CONNECTION_SERVICE 권한을 선언해야 합니다. 다음 예는 앱 매니페스트에서 서비스를 선언하는 방법을 보여줍니다.

<service android:name="com.example.MyConnectionService"
    android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
    <intent-filter>
        <action android:name="android.telecom.ConnectionService" />
    </intent-filter>
</service>

서비스를 비롯한 앱 구성요소 선언에 관한 자세한 내용은 앱 구성요소를 참조하세요.

연결 서비스 구현

통화 앱은 텔레콤 하위 시스템이 결합할 수 있는 ConnectionService 클래스의 구현을 제공해야 합니다. ConnectionService 구현은 다음 메서드를 재정의해야 합니다.

onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)

텔레콤 하위 시스템은 새 발신 전화를 걸기 위해 placeCall(Uri, Bundle)을 호출하는 앱에 응답하여 이 메서드를 호출합니다. 앱은 새 발신 전화를 나타내는 Connection 클래스 구현의 새 인스턴스를 반환합니다. 자세한 내용은 연결 구현을 참조하세요. 다음 작업을 실행하여 발신 연결을 더욱 상세하게 맞춤설정할 수 있습니다.

onCreateOutgoingConnectionFailed(PhoneAccountHandle, ConnectionRequest)

앱에서 placeCall(Uri, Bundle) 메서드를 호출했는데 발신 전화를 걸 수 없을 때 텔레콤 하위 시스템은 이 메서드를 호출합니다. 이 상황에 대응하여 앱은 발신 전화를 걸 수 없다는 것을 사용자에게 알려야 합니다(예: 알림 상자 또는 토스트 메시지 사용). 진행 중인 긴급 전화가 있거나 다른 앱에서 먼저 진행 중인 통화를 보류할 수 없다면 앱에서 전화를 걸지 못할 수 있습니다.

onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)

앱에서 addNewIncomingCall(PhoneAccountHandle, Bundle) 메서드를 호출하여 앱의 새로운 수신 전화를 시스템에 알릴 때 텔레콤 하위 시스템은 이 메서드를 호출합니다. 앱은 새 수신 전화를 나타내는 Connection 구현의 새 인스턴스를 반환합니다. 자세한 내용은 연결 구현을 참조하세요. 다음 작업을 실행하여 수신 연결을 더욱 상세하게 맞춤설정할 수 있습니다.

onCreateIncomingConnectionFailed(PhoneAccountHandle, ConnectionRequest)

앱에서 addNewIncomingCall(PhoneAccountHandle, Bundle) 메서드를 호출하여 새로운 수신 전화를 텔레콤에 알렸으나 수신 전화가 허용되지 않을 때 텔레콤 하위 시스템이 이 메서드를 호출합니다. 자세한 내용은 통화 제약을 참조하세요. 앱에서 자동으로 수신 전화를 거부하고 부재중 전화를 사용자에게 알리는 알림을 게시할 수도 있습니다.

연결 구현

앱에서 Connection의 서브클래스를 생성하여 앱의 통화를 나타내야 합니다. 구현 시 다음 메서드를 재정의해야 합니다.

onShowIncomingCallUi()

새 수신 전화가 추가되어 앱에 수신 전화 UI를 표시해야 할 때 텔레콤 하위 시스템은 이 메서드를 호출합니다.

onCallAudioStateChanged(CallAudioState)

텔레콤 하위 시스템은 이 메서드를 호출하여 현재 오디오 경로 또는 모드가 변경되었다는 것을 앱에 알립니다. 앱에서 setAudioRoute(int) 메서드를 사용하여 오디오 모드를 변경하는 것에 대응하여 이 메서드가 호출됩니다. 또한 시스템이 오디오 경로를 변경하면(예: 블루투스 헤드셋 연결이 끊겼을 때) 이 메서드가 호출될 수도 있습니다.

onHold()

텔레콤 하위 시스템은 통화를 대기 상태로 전환하려고 할 때 이 메서드를 호출합니다. 이 요청에 대응하여 앱은 통화를 대기 상태로 전환한 후 setOnHold() 메서드를 호출하여 통화가 대기 중이라는 것을 시스템에 알려야 합니다. 통화를 표시하는 Android Auto와 같은 통화 중 서비스에서 통화를 대기 상태로 전환하려는 사용자 요청을 전달하려고 할 때 텔레콤 하위 시스템은 이 메서드를 호출할 수 있습니다. 또한 사용자가 다른 앱에서 통화를 활성화할 때에도 텔레콤 하위 시스템은 이 메서드를 호출합니다. 통화 중 서비스에 관한 자세한 내용은 InCallService를 참조하세요.

onUnhold()

텔레콤 하위 시스템은 대기 상태의 통화를 재개하려고 할 때 이 메서드를 호출합니다. 앱에서 통화를 재개하면 setActive() 메서드를 호출하여 통화가 더 이상 대기 상태가 아니라는 것을 시스템에 알려야 합니다. 통화를 표시하는 Android Auto와 같은 통화 중 서비스에서 통화 재개 요청을 전달하려고 할 때 텔레콤 하위 시스템은 이 메서드를 호출할 수 있습니다. 통화 중 서비스에 관한 자세한 내용은 InCallService를 참조하세요.

onAnswer()

텔레콤 하위 시스템은 이 메서드를 호출하여 수신 전화에 응답해야 한다는 것을 앱에 알립니다. 앱에서 통화에 응답하면 setActive() 메서드를 호출하여 통화에 응답했다는 것을 시스템에 알려야 합니다. 앱에서 새 수신 전화를 추가했는데 다른 앱에서 이미 진행 중인 통화를 보류할 수 없을 때 텔레콤 하위 시스템은 이 메서드를 호출할 수 있습니다. 이럴 때 텔레콤 하위 시스템은 앱을 대신하여 수신 전화 UI를 표시합니다. 프레임워크는 전화에 응답할 영상 상태를 지정할 수 있도록 지원하는 오버로드된 메서드를 제공합니다. 자세한 내용은 onAnswer(int)를 참고하세요.

onReject()

텔레콤 하위 시스템은 수신 전화를 거부하려고 할 때 이 메서드를 호출합니다. 앱에서 통화를 거부하면 setDisconnected(DisconnectCause)를 호출하고 REJECTED를 매개변수로 지정해야 합니다. 그런 다음 앱에서 destroy() 메서드를 호출하여 통화를 처리했다는 것을 시스템에 알려야 합니다. 사용자가 앱에서 수신 전화를 거부했을 때 텔레콤 하위 시스템은 이 메서드를 호출합니다.

onDisconnect()

텔레콤 하위 시스템은 통화 연결을 끊으려고 할 때 이 메서드를 호출합니다. 통화가 종료되면 앱에서 setDisconnected(DisconnectCause) 메서드를 호출하고 LOCAL를 매개변수로 지정하여 사용자 요청으로 인해 통화 연결이 끊어졌음을 나타내야 합니다. 그런 다음 앱에서 destroy() 메서드를 호출하여 통화를 처리했다는 것을 텔레콤 하위 시스템에 알려야 합니다. 사용자가 Android Auto와 같은 다른 통화 중 서비스를 통해 통화 연결을 끊었을 때 시스템이 이 메서드를 호출할 수 있습니다. 또한 사용자가 긴급 전화를 걸려고 하는 상황과 같이 새 전화를 걸기 위해 기존 통화 연결을 끊어야 하는 때에도 시스템은 이 메서드를 호출합니다. 통화 중 서비스에 관한 자세한 내용은 InCallService를 참조하세요.

일반적인 통화 처리 시나리오

호출 흐름의 ConnectionService API를 사용하면 android.telecom 패키지의 다른 클래스와 상호작용하게 됩니다. 다음 섹션에서는 일반적인 통화 시나리오 및 앱에서 API를 사용하여 통화를 처리하는 방법을 설명합니다.

수신 전화 받기

수신 전화를 처리하는 흐름은 다른 앱에 통화가 있는지 여부에 따라 변경됩니다. 흐름의 차이가 생기는 이유는 기기의 모든 통화 앱에 안정적인 환경을 보장하기 위해 다른 앱에 진행 중인 통화가 있을 때 텔레콤 프레임워크가 일부 제약을 설정해야 하기 때문입니다. 자세한 내용은 통화 제약을 참조하세요.

다른 앱에 진행 중인 통화가 없을 때

다른 앱에 진행 중인 통화가 없을 때 수신 전화를 받으려면 다음 단계를 따르세요.

  1. 앱에서 일반적인 메커니즘을 사용하여 새로운 수신 전화를 받습니다.
  2. addNewIncomingCall(PhoneAccountHandle, Bundle) 메서드를 사용하여 새 수신 전화에 관해 텔레콤 하위 시스템에 알립니다.
  3. 텔레콤 하위 시스템은 앱의 ConnectionService 구현에 결합하고 onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest) 메서드를 사용하여 새 수신 전화를 나타내는 Connection 클래스의 새 인스턴스를 요청합니다.
  4. 텔레콤 하위 시스템은 onShowIncomingCallUi() 메서드를 사용하여 수신 전화 사용자 인터페이스를 표시해야 한다는 것을 앱에 알립니다.
  5. 앱은 관련 전체 화면 인텐트가 있는 알림을 사용하여 수신 전화 UI를 표시합니다. 자세한 내용은 onShowIncomingCallUi()를 참조하세요.
  6. 사용자가 수신 전화를 수락하면 setActive() 메서드를 호출하거나 사용자가 수신 전화를 거부하면 REJECTED를 매개변수로 지정하여 setDisconnected(DisconnectCause)를 호출한 후 destroy() 메서드를 호출합니다.

다른 앱에서 진행 중인 통화를 보류할 수 없을 때

다른 앱에서 진행 중인 통화를 보류할 수 없을 때 수신 전화를 받으려면 다음 단계를 따르세요.

  1. 앱에서 일반적인 메커니즘을 사용하여 새로운 수신 전화를 받습니다.
  2. addNewIncomingCall(PhoneAccountHandle, Bundle) 메서드를 사용하여 새 수신 전화에 관해 텔레콤 하위 시스템에 알립니다.
  3. 텔레콤 하위 시스템은 앱의 ConnectionService 구현에 결합하고 onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest) 메서드를 사용하여 새 수신 전화를 나타내는 Connection 객체의 새 인스턴스를 요청합니다.
  4. 텔레콤 하위 시스템은 수신 전화를 위해 수신 전화 UI를 표시합니다.
  5. 사용자가 통화를 수락하면 텔레콤 하위 시스템은 onAnswer() 메서드를 호출합니다. setActive() 메서드를 호출하여 이제 통화가 연결되었음을 텔레콤 하위 시스템에 알려야 합니다.
  6. 사용자가 통화를 거부하면 텔레콤 하위 시스템은 onReject() 메서드를 호출합니다. REJECTED를 매개변수로 지정하여 setDisconnected(DisconnectCause) 메서드를 호출한 후 destroy() 메서드를 호출해야 합니다.

발신 전화 걸기

발신 전화를 걸기 위한 흐름에는 텔레콤 프레임워크에 의해 부과된 제약으로 인해 전화를 걸지 못할 가능성을 처리하는 것이 포함됩니다. 자세한 내용은 통화 제약을 참조하세요.

발신 전화를 걸려면 다음 단계를 따르세요.

  1. 사용자가 앱 내에서 발신 전화를 시작합니다.
  2. placeCall(Uri, Bundle) 메서드를 사용하여 새 발신 전화에 관해 텔레콤 하위 시스템에 알립니다. 메서드 매개변수와 관련하여 다음을 고려합니다.
    • Uri 매개변수는 전화를 걸고 있는 대상 주소를 나타냅니다. 일반 전화번호에는 tel: URI 스키마를 사용합니다.
    • Bundle 매개변수를 사용하면 EXTRA_PHONE_ACCOUNT_HANDLE 엑스트라에 앱의 PhoneAccountHandle 객체를 추가하여 통화 앱에 관한 정보를 제공할 수 있습니다. 앱은 모든 발신 전화에 PhoneAccountHandle 객체를 제공해야 합니다.
    • 또한 Bundle 매개변수를 사용하면 EXTRA_START_CALL_WITH_VIDEO_STATE 엑스트라에 STATE_BIDIRECTIONAL 값을 지정하여 발신 전화에 영상을 포함할지 여부도 지정할 수 있습니다. 기본적으로 텔레콤 하위 시스템은 영상 통화를 스피커폰으로 라우팅합니다.
  3. 텔레콤 하위 시스템은 앱의 ConnectionService 구현에 결합합니다.
  4. 앱에서 발신 전화를 걸 수 없다면 텔레콤 하위 시스템은 onCreateOutgoingConnectionFailed(PhoneAccountHandle, ConnectionRequest) 메서드를 호출하여 현재는 전화를 걸 수 없다는 것을 앱에 알립니다. 앱은 전화를 걸 수 없다는 것을 사용자에게 알려야 합니다.
  5. 앱에서 발신 전화를 걸 수 있다면 텔레콤 하위 시스템은 onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest) 메서드를 호출합니다. 앱은 새 발신 전화를 나타내는 Connection 클래스의 인스턴스를 반환해야 합니다. 연결 시 설정해야 하는 속성에 관한 자세한 내용은 연결 서비스 구현을 참조하세요.
  6. 발신 전화가 연결되면 setActive() 메서드를 호출하여 통화가 활성화되었다는 것을 텔레콤 하위 시스템에 알립니다.

통화 종료

통화를 종료하려면 다음 단계를 따르세요.

  1. 사용자가 통화를 종료하면 LOCAL을 매개변수로 전달하여 setDisconnected(DisconnectCause)를 호출하고 상대방이 통화를 종료하면 REMOTE를 매개변수로 전달합니다.
  2. destroy() 메서드를 호출합니다.

통화 제약

사용자에게 일관되고 간편한 통화 환경을 보장하기 위해 텔레콤 프레임워크는 기기에서 통화를 관리하기 위한 몇 가지 제약을 적용합니다. 예를 들어 사용자가 자체 관리 ConnectionService API를 구현하는 두 가지 통화 앱인 FooTalk 및 BarTalk을 설치했다고 생각해 보세요. 이 경우 다음과 같은 제약이 적용됩니다.

  • API 레벨 27 이하에서 실행되는 기기에서는 한번에 하나의 앱만 진행 중인 통화를 유지할 수 있습니다. 이 제약은 사용자가 FooTalk 앱을 사용하여 통화를 진행하는 동안 BarTalk 앱은 새로운 통화를 시작하거나 전화를 받을 수 없다는 것을 의미합니다.

    API 레벨 28 이상에서 실행되는 기기에서는 FooTalk 및 BarTalk 앱 모두에서 CAPABILITY_SUPPORT_HOLDCAPABILITY_HOLD 권한을 선언하면 사용자가 앱 간을 전환하며 또 다른 통화를 시작하거나 전화를 받아 진행 중인 통화를 둘 이상 유지할 수 있습니다.

  • 사용자가 일반 관리 통화(예: 기본 제공 전화 또는 다이얼러 앱 사용)를 사용 중이라면 통화 앱에서 시작된 통화를 사용할 수 없습니다. 즉, 사용자가 이동통신사를 통해 일반 통화를 하는 중에 FooTalk 또는 BarTalk 통화를 동시에 할 수 없습니다.

  • 사용자가 긴급 전화를 걸면 텔레콤 하위 시스템은 앱의 통화 연결을 끊습니다.

  • 사용자가 긴급 전화를 하는 동안에는 앱에서 전화를 받거나 걸 수 없습니다.

  • 앱에 수신 전화가 걸려올 때 다른 통화 앱에서 진행 중인 통화가 있는데 수신 전화를 받으면 다른 앱에서 진행 중인 통화는 종료됩니다. 앱에 일반적인 수신 전화 사용자 인터페이스가 표시되어서는 안 됩니다. 텔레콤 프레임워크는 수신 전화 사용자 인터페이스를 표시하여 새로운 전화를 받으면 진행 중인 통화가 종료됨을 사용자에게 알립니다. 즉, 사용자가 FooTalk 통화 중인데 BarTalk 앱에 수신 전화가 걸려올 경우 텔레콤 프레임워크는 사용자에게 새로운 BarTalk 수신 전화가 왔으며 이를 받으면 기존 FooTalk 통화가 종료된다고 알려줍니다.