Создание приложения для звонков по умолчанию

Приложение "Телефон" по умолчанию позволяет фреймворку Android Telecom сообщать вашему приложению о состоянии вызова. Для этого используются менеджер ролей и сервис InCallService, который заменяет стандартное приложение "Телефон" на устройстве Android. Чтобы реализовать эту функцию, используйте InCallService API. Реализация должна соответствовать следующим требованиям:

Оно не должно иметь никаких функций вызова и должно состоять исключительно из пользовательского интерфейса для вызова. Оно должно обрабатывать все вызовы, о которых знает фреймворк Telecom, и не делать предположений о характере вызовов. Например, оно не должно предполагать, что вызовы осуществляются через SIM-карту, или применять ограничения на вызовы, основанные на одном ConnectionService, например ограничения телефонии для видеовызовов.

Приложение для звонков позволяет пользователям принимать и совершать аудио- и видеовызовы на устройстве. Приложения для звонков используют собственный пользовательский интерфейс для звонков вместо интерфейса приложения "Телефон" по умолчанию, как показано на скриншоте ниже.

Пример приложения для звонков
Пример приложения для звонков, использующего собственный пользовательский интерфейс

Фреймворк Android включает пакет android.telecom, который содержит классы, помогающие создать приложение для звонков в соответствии с фреймворком Telecom. Разработка приложения в соответствии с фреймворком 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 (подробнее о том, как реализовать подключение), который представляет новый исходящий вызов. Вы можете настроить исходящее подключение, выполнив следующие действия:

  • Приложение должно вызвать метод setConnectionProperties(int) с константой PROPERTY_SELF_MANAGED в качестве аргумента, чтобы указать, что подключение было инициировано приложением для звонков.
  • Если ваше приложение поддерживает удержание вызовов, вызовите метод setConnectionCapabilities(int) и задайте для аргумента значение битовой маски констант CAPABILITY_HOLD и CAPABILITY_SUPPORT_HOLD.
  • Чтобы задать имя звонящего, используйте метод setCallerDisplayName(String, int), передав константу PRESENTATION_ALLOWED в качестве параметра int. Это укажет, что имя звонящего должно быть показано.
  • Чтобы убедиться, что исходящий вызов имеет соответствующее состояние видео, вызовите метод setVideoState(int) объекта Connection и отправьте значение, возвращенное методом getVideoState() объекта ConnectionRequest.
onCreateOutgoingConnectionFailed(PhoneAccountHandle, ConnectionRequest)

Телекоммуникационная подсистема вызывает этот метод, когда ваше приложение вызывает метод placeCall(Uri, Bundle) и исходящий вызов не может быть выполнен. В этом случае приложение должно сообщить пользователю (например, с помощью всплывающего окна или уведомления), что исходящий вызов не может быть выполнен. Приложение не сможет совершить вызов, если в данный момент выполняется экстренный вызов или вызов в другом приложении, который нельзя поставить на удержание.

onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)

Подсистема телекоммуникаций вызывает этот метод, когда ваше приложение вызывает метод addNewIncomingCall(PhoneAccountHandle, Bundle), чтобы сообщить системе о новом входящем вызове в вашем приложении. Ваше приложение возвращает новый экземпляр вашей реализации Connection (дополнительную информацию см. в разделе Реализация подключения), представляющий новый входящий вызов. Вы можете настроить входящее подключение, выполнив следующие действия:

  • Приложение должно вызвать метод setConnectionProperties(int) с константой PROPERTY_SELF_MANAGED в качестве аргумента, чтобы указать, что подключение было инициировано приложением для звонков.
  • Если ваше приложение поддерживает удержание вызовов, вызовите метод setConnectionCapabilities(int) и задайте в качестве аргумента значение битовой маски констант CAPABILITY_HOLD и CAPABILITY_SUPPORT_HOLD.
  • Чтобы задать имя звонящего, используйте метод setCallerDisplayName(String, int), передав константу PRESENTATION_ALLOWED в качестве параметра int. Это укажет, что имя звонящего должно быть показано.
  • Чтобы указать номер телефона или адрес входящего вызова, используйте метод setAddress(Uri, int) объекта Connection.
  • Чтобы убедиться, что исходящий вызов имеет соответствующее состояние видео, вызовите метод setVideoState(int) объекта Connection и отправьте значение, возвращенное методом getVideoState() объекта ConnectionRequest.
onCreateIncomingConnectionFailed(PhoneAccountHandle, ConnectionRequest)

Телекоммуникационная подсистема вызывает этот метод, когда ваше приложение вызывает метод addNewIncomingCall(PhoneAccountHandle, Bundle), чтобы сообщить о новом входящем вызове, но он не разрешен. Подробнее об ограничениях на вызовы… Приложение должно отклонить входящий вызов без звука и при необходимости отправить уведомление о пропущенном вызове.

Как настроить связь

Чтобы представить звонки в приложении, создайте подкласс Connection. В реализации переопределите следующие методы:

onShowIncomingCallUi()

Телекоммуникационная подсистема вызывает этот метод, когда вы добавляете новый входящий вызов и ваше приложение должно показать интерфейс входящего вызова.

onCallAudioStateChanged(CallAudioState)

Телекоммуникационная подсистема вызывает этот метод, чтобы сообщить приложению, что текущий аудиомаршрут или режим изменился. Вызывается в ответ на изменение аудиорежима в приложении с помощью метода setAudioRoute(int). Этот метод также может вызываться, если система меняет маршрут аудио (например, при отключении Bluetooth-гарнитуры).

onHold()

Телекоммуникационная подсистема вызывает этот метод, когда хочет поставить вызов на удержание. В ответ на этот запрос приложение должно удержать вызов, а затем вызвать метод setOnHold(), чтобы сообщить системе, что вызов удерживается. Подсистема Telecom может вызвать этот метод, когда сервис, показывающий ваш звонок, например Android Auto, хочет передать запрос пользователя на удержание вызова. Подсистема телекоммуникаций также вызывает этот метод, если пользователь совершает звонок в другом приложении. Подробнее InCallService…

onUnhold()

Подсистема телекоммуникаций вызывает этот метод, когда хочет возобновить вызов, который был поставлен на удержание. После возобновления вызова приложение должно вызвать метод setActive(), чтобы сообщить системе, что вызов больше не находится на удержании. Телекоммуникационная подсистема может вызывать этот метод, когда сервис, показывающий ваш звонок, например Android Auto, хочет передать запрос на возобновление звонка. Подробнее о сервисах, доступных во время звонка, рассказывается в статье InCallService.

onAnswer()

Телекоммуникационная подсистема вызывает этот метод, чтобы сообщить вашему приложению, что на входящий вызов необходимо ответить. После того как приложение ответит на вызов, оно должно вызвать метод setActive(), чтобы сообщить системе, что на вызов был дан ответ. Телекоммуникационная подсистема может вызвать этот метод, когда ваше приложение добавляет новый входящий вызов, а в другом приложении уже есть текущий вызов, который нельзя поставить на удержание. В таких случаях телекоммуникационная подсистема показывает интерфейс входящего вызова от имени вашего приложения. Фреймворк предоставляет перегруженный метод, который позволяет указать состояние видео, в котором нужно ответить на вызов. Дополнительную информацию можно найти по адресу onAnswer(int).

onReject()

Телекоммуникационная подсистема вызывает этот метод, когда хочет отклонить входящий вызов. После того как приложение отклонит вызов, оно должно вызвать setDisconnected(DisconnectCause) и указать REJECTED в качестве параметра. Затем приложение должно вызвать метод destroy(), чтобы сообщить системе, что вызов обработан. Подсистема телекоммуникаций вызывает этот метод, когда пользователь отклоняет входящий вызов из вашего приложения.

onDisconnect()

Телекоммуникационная подсистема вызывает этот метод, когда хочет завершить вызов. После завершения звонка приложение должно вызвать метод setDisconnected(DisconnectCause) и указать LOCAL в качестве параметра, чтобы показать, что звонок был прерван по запросу пользователя. Затем приложение должно вызвать метод destroy(), чтобы сообщить телекоммуникационной подсистеме, что вызов обработан. Система может вызвать этот метод, когда пользователь завершает вызов через другой сервис, например Android Auto. Система также вызывает этот метод, когда ваш вызов должен быть отключен, чтобы разрешить другой вызов, например, если пользователь хочет сделать экстренный вызов. Подробнее о сервисах во время звонка: InCallService.

Как обрабатывать распространенные сценарии звонков

Чтобы использовать API ConnectionService в последовательности вызовов, необходимо взаимодействовать с другими классами в пакете android.telecom. В следующих разделах описаны распространенные сценарии вызовов и то, как ваше приложение должно использовать API для их обработки.

Как отвечать на звонки

Процесс обработки входящих вызовов зависит от того, есть ли звонки в других приложениях. Разница в процессах обусловлена тем, что фреймворк Telecom должен устанавливать определенные ограничения, когда в других приложениях есть активные вызовы. Это необходимо, чтобы обеспечить стабильную работу всех приложений для звонков на устройстве. Подробнее об ограничениях на вызовы…

Нет активных звонков в других приложениях

Чтобы отвечать на входящие вызовы, когда в других приложениях нет активных вызовов, выполните следующие действия:

  1. Приложение получает новый входящий вызов, используя обычные механизмы.
  2. Используйте метод addNewIncomingCall(PhoneAccountHandle, Bundle), чтобы сообщить телекоммуникационной подсистеме о новом входящем вызове.
  3. Телекоммуникационная подсистема связывается с реализацией ConnectionService вашего приложения и запрашивает новый экземпляр класса Connection, представляющий новый входящий вызов, с помощью метода onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest).
  4. Телекоммуникационная подсистема сообщает приложению, что оно должно показать пользовательский интерфейс входящего вызова, используя метод onShowIncomingCallUi().
  5. Приложение показывает интерфейс входящего звонка с помощью уведомления, связанного с полноэкранным интентом. Дополнительную информацию можно найти по адресу onShowIncomingCallUi().
  6. Вызовите метод setActive(), если пользователь принимает входящий вызов, или setDisconnected(DisconnectCause), указав REJECTED в качестве параметра, а затем вызовите метод destroy(), если пользователь отклоняет входящий вызов.

активные звонки в других приложениях, которые нельзя поставить на удержание;

Чтобы ответить на входящий вызов, когда в других приложениях есть активные вызовы, которые нельзя поставить на удержание, выполните следующие действия:

  1. Приложение получает новый входящий вызов, используя обычные механизмы.
  2. Используйте метод addNewIncomingCall(PhoneAccountHandle, Bundle), чтобы сообщить телекоммуникационной подсистеме о новом входящем вызове.
  3. Телекоммуникационная подсистема привязывается к реализации ConnectionService вашего приложения и запрашивает новый экземпляр объекта Connection, представляющий новый входящий вызов, с помощью метода onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest).
  4. Подсистема телекоммуникаций показывает интерфейс входящего вызова.
  5. Если пользователь принимает вызов, телекоммуникационная подсистема вызывает метод onAnswer(). Вызовите метод setActive(), чтобы сообщить телекоммуникационной подсистеме, что вызов подключен.
  6. Если пользователь отклоняет вызов, телекоммуникационная подсистема вызывает метод onReject(). Вызовите метод setDisconnected(DisconnectCause), указав в качестве параметра REJECTED, а затем вызовите метод destroy().

Совершать исходящие вызовы.

При совершении исходящего вызова необходимо учитывать, что он может быть не выполнен из-за ограничений, накладываемых фреймворком Telecom. Подробнее об ограничениях на звонки…

Чтобы сделать исходящий вызов, выполните следующие действия:

  1. Пользователь инициирует исходящий вызов в вашем приложении.
  2. Используйте метод placeCall(Uri, Bundle), чтобы сообщить телекоммуникационной подсистеме о новом исходящем вызове. При работе с параметрами метода учитывайте следующее:
    • Параметр Uri представляет адрес, по которому выполняется вызов. Для обычных номеров телефонов используйте схему URI tel:.
    • Параметр Bundle позволяет предоставить информацию о приложении для звонков, добавив объект PhoneAccountHandle приложения в дополнительный параметр EXTRA_PHONE_ACCOUNT_HANDLE. Приложение должно предоставлять объект PhoneAccountHandle для каждого исходящего вызова.
    • Параметр Bundle также позволяет указать, включает ли исходящий вызов видео, указав значение STATE_BIDIRECTIONAL в дополнительном параметре EXTRA_START_CALL_WITH_VIDEO_STATE. Учтите, что по умолчанию телекоммуникационная подсистема направляет видеовызовы на громкую связь.
  3. Телекоммуникационная подсистема привязывается к реализации ConnectionService вашего приложения.
  4. Если приложение не может выполнить исходящий вызов, телекоммуникационная подсистема вызывает метод onCreateOutgoingConnectionFailed(PhoneAccountHandle, ConnectionRequest), чтобы сообщить приложению, что вызов не может быть выполнен в данный момент. Приложение должно сообщить пользователю, что вызов не может быть выполнен.
  5. Если ваше приложение может совершить исходящий вызов, телекоммуникационная подсистема вызывает метод onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest). Приложение должно возвращать экземпляр класса Connection, представляющий новый исходящий вызов. Подробную информацию о свойствах, которые необходимо задать в подключении, можно найти в разделе Реализация сервиса подключения.
  6. Когда исходящий вызов будет подключен, вызовите метод setActive(), чтобы сообщить телекоммуникационной подсистеме, что вызов активен.

Как завершить вызов

Чтобы завершить звонок, выполните следующие действия:

  1. Вызовите setDisconnected(DisconnectCause), передав LOCAL в качестве параметра, если пользователь завершил вызов, или REMOTE, если вызов завершил другой абонент.
  2. Вызовите метод destroy().

Ограничения на вызовы

Чтобы обеспечить единообразие и простоту звонков для пользователей, фреймворк телекоммуникаций накладывает некоторые ограничения на управление вызовами на устройстве. Например, у пользователя установлены два приложения для звонков, в которых реализован API ConnectionService, – FooTalk и BarTalk. В этом случае действуют следующие ограничения:

  • На устройствах с уровнем API 27 или ниже только одно приложение может поддерживать текущий вызов в любой момент времени. Это ограничение означает, что пока пользователь совершает звонок в приложении FooTalk, приложение BarTalk не может инициировать или принять новый вызов.

    На устройствах с уровнем API 28 или выше, если в FooTalk и BarTalk объявлены разрешения CAPABILITY_SUPPORT_HOLD и CAPABILITY_HOLD, пользователь может поддерживать несколько текущих вызовов, переключаясь между приложениями, чтобы начать или принять другой вызов.

  • Если пользователь совершает обычный управляемый вызов (например, с помощью встроенного приложения "Телефон" или "Телефонный набор"), он не может совершать вызовы из приложений для звонков. Это означает, что если пользователь совершает обычный звонок через оператора мобильной связи, он не может одновременно участвовать в звонке FooTalk или BarTalk.

  • Телекоммуникационная подсистема отключает вызовы вашего приложения, если пользователь набирает номер экстренной службы.

  • Ваше приложение не может принимать или совершать исходящие вызовы, пока пользователь совершает экстренный вызов.

  • Если в другом приложении для звонков есть текущий вызов, когда ваше приложение получает входящий вызов, ответ на входящий вызов завершает все текущие вызовы в другом приложении. Ваше приложение не должно показывать обычный пользовательский интерфейс входящего вызова. Фреймворк Telecom показывает пользовательский интерфейс входящего вызова и сообщает пользователю, что ответ на новый вызов завершит текущие. Это означает, что если пользователь находится в вызове FooTalk и приложение BarTalk получает входящий вызов, телекоммуникационная среда сообщает пользователю, что у него есть новый входящий вызов BarTalk и что ответ на вызов BarTalk завершит его вызов FooTalk.

Как стать приложением "Телефон" по умолчанию

Приложение "Телефон" по умолчанию – это приложение, которое предоставляет пользовательский интерфейс во время вызова. Кроме того, оно позволяет пользователю совершать звонки и просматривать историю вызовов на устройстве. На устройстве есть стандартное приложение "Телефон". Пользователь может выбрать другое приложение, которое будет выполнять эту роль. Для этого приложение должно использовать RoleManager, чтобы запросить роль RoleManager.ROLE_DIALER.

Приложение "Телефон" по умолчанию предоставляет пользовательский интерфейс, когда устройство находится в режиме вызова, но не в режиме автомобиля (то есть UiModeManager#getCurrentModeType() не равно Configuration.UI_MODE_TYPE_CAR).

Чтобы приложение могло выполнять роль RoleManager.ROLE_DIALER, оно должно соответствовать ряду требований:

  • Оно должно обрабатывать намерение Intent#ACTION_DIAL. Это означает, что в приложении должен быть интерфейс панели набора номера, чтобы пользователь мог инициировать исходящие вызовы.
  • Оно должно полностью реализовывать InCallService API и предоставлять интерфейс как для входящих вызовов, так и для текущих вызовов.

Примечание. Если приложение, заполняющее RoleManager.ROLE_DIALER, возвращает null InCallService во время привязки, фреймворк Telecom автоматически переключается на использование приложения для набора номера, предустановленного на устройстве. Система покажет пользователю уведомление о том, что звонок был продолжен в предустановленном приложении для набора номера. Ваше приложение не должно возвращать привязку null, иначе оно не будет соответствовать требованиям RoleManager.ROLE_DIALER.

Примечание. Если ваше приложение выполняет роль RoleManager.ROLE_DIALER и вносит изменения во время выполнения, из-за которых оно перестает соответствовать требованиям этой роли, RoleManager автоматически удалит приложение из роли и закроет его. Например, если вы используете PackageManager.setComponentEnabledSetting(ComponentName, int, int), чтобы программно отключить InCallService, объявленный в манифесте приложения, оно перестанет соответствовать требованиям, предъявляемым к роли RoleManager.ROLE_DIALER.

Для экстренных вызовов ВСЕГДА будет использоваться предустановленное приложение, даже если ваше приложение выполняет роль RoleManager.ROLE_DIALER. Чтобы обеспечить оптимальную работу при совершении экстренного вызова, приложение для набора номера по умолчанию должно ВСЕГДА использовать TelecomManager.placeCall(Uri, Bundle) для совершения исходящих вызовов (включая экстренные). Это позволяет платформе убедиться, что запрос поступил от приложения для звонков по умолчанию. Если приложение для набора номера, которое не было предустановлено, использует Intent#ACTION_CALL для экстренного вызова, то для подтверждения будет вызвано предустановленное приложение с помощью Intent#ACTION_DIAL. Это не очень удобно для пользователя.

Ниже приведен пример регистрации манифеста для InCallService. Метаданные TelecomManager#METADATA_IN_CALL_SERVICE_UI указывают, что эта реализация InCallService предназначена для замены встроенного интерфейса во время звонка. Метаданные TelecomManager#METADATA_IN_CALL_SERVICE_RINGING указывают, что этот InCallService будет воспроизводить рингтон для входящих вызовов. Подробнее о том, как показывать интерфейс входящего вызова и воспроизводить рингтон в приложении…

 <service android:name="your.package.YourInCallServiceImplementation"
          android:permission="android.permission.BIND_INCALL_SERVICE"
          android:exported="true">
      <meta-data android:name="android.telecom.IN_CALL_SERVICE_UI" android:value="true" />
      <meta-data android:name="android.telecom.IN_CALL_SERVICE_RINGING"
          android:value="true" />
      <intent-filter>
          <action android:name="android.telecom.InCallService"/>
      </intent-filter>
 </service>

Примечание. Не помечайте InCallService атрибутом android:exported="false". В противном случае при вызовах может не выполняться привязка к вашей реализации.

Помимо реализации API InCallService, вам также необходимо объявить в манифесте действие, которое обрабатывает намерение Intent#ACTION_DIAL. Ниже показано, как это сделать.

 <activity android:name="your.package.YourDialerActivity"
           android:label="@string/yourDialerActivityLabel">
      <intent-filter>
           <action android:name="android.intent.action.DIAL" />
           <category android:name="android.intent.category.DEFAULT" />
      </intent-filter>
      <intent-filter>
           <action android:name="android.intent.action.DIAL" />
           <category android:name="android.intent.category.DEFAULT" />
           <data android:scheme="tel" />
      </intent-filter>
 </activity>

Когда пользователь устанавливает ваше приложение и запускает его в первый раз, вы должны использовать RoleManager, чтобы предложить пользователю сделать ваше приложение приложением "Телефон" по умолчанию.

В коде ниже показано, как приложение может запросить разрешение стать приложением для звонков по умолчанию.

 private static final int REQUEST_ID = 1;

 public void requestRole() {
     RoleManager roleManager = (RoleManager) getSystemService(ROLE_SERVICE);
     Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_DIALER);
     startActivityForResult(intent, REQUEST_ID);
 }

 public void onActivityResult(int requestCode, int resultCode, Intent data) {
     if (requestCode == REQUEST_ID) {
         if (resultCode == android.app.Activity.RESULT_OK) {
             // Your app is now the default dialer app
         } else {
             // Your app is not the default dialer app
         }
     }
 }

Доступ к InCallService для носимых устройств

    Если ваше приложение является сторонним сопутствующим приложением и хочет получить доступ к API InCallService, вы можете сделать следующее:

    1. Объявите разрешение MANAGE_ONGOING_CALLS в манифесте.
    2. Свяжите сопутствующее приложение с физическим носимым устройством с помощью API CompanionDeviceManager. Подробнее: https://developer.android.com/guide/topics/connectivity/companion-device-pairing.
    3. Реализуйте этот InCallService с разрешением BIND_INCALL_SERVICE.

Показ уведомления о входящем вызове

Когда приложение получает новый входящий вызов через InCallService#onCallAdded(Call), оно должно показать интерфейс входящего вызова. Для этого нужно использовать API NotificationManager, чтобы опубликовать новое уведомление о входящем вызове.

Если в приложении объявлены метаданные TelecomManager#METADATA_IN_CALL_SERVICE_RINGING, оно отвечает за воспроизведение рингтона при входящих вызовах. Приложение должно создать объект NotificationChannel, в котором указан нужный рингтон. Пример:

 NotificationChannel channel = new NotificationChannel(YOUR_CHANNEL_ID, "Incoming Calls",
          NotificationManager.IMPORTANCE_MAX);
 // other channel setup stuff goes here.

 // We'll use the default system ringtone for our incoming call notification channel.  You can
 // use your own audio resource here.
 Uri ringtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
 channel.setSound(ringtoneUri, new AudioAttributes.Builder()
          // Setting the AudioAttributes is important as it identifies the purpose of your
          // notification sound.
          .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
          .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
      .build());

 NotificationManager mgr = getSystemService(NotificationManager.class);
 mgr.createNotificationChannel(channel);

Когда приложение получает новый входящий вызов, оно создает для него объект Notification и связывает его с каналом уведомлений о входящих вызовах. Вы можете указать PendingIntent в уведомлении, которое будет запускать полноэкранный интерфейс входящего вызова. Если пользователь активно использует телефон, фреймворк менеджера уведомлений покажет ваше уведомление как всплывающее. Если пользователь не работает с телефоном, вместо этого используется полноэкранный интерфейс входящего вызова. Пример:

 // Create an intent which triggers your fullscreen incoming call user interface.
 Intent intent = new Intent(Intent.ACTION_MAIN, null);
 intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
 intent.setClass(context, YourIncomingCallActivity.class);
 PendingIntent pendingIntent = PendingIntent.getActivity(context, 1, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED);
 // Build the notification as an ongoing high priority item; this ensures it will show as
 // a heads up notification which slides down over top of the current content.
 final Notification.Builder builder = new Notification.Builder(context);
 builder.setOngoing(true);
 builder.setPriority(Notification.PRIORITY_HIGH);
 // Set notification content intent to take user to the fullscreen UI if user taps on the
 // notification body.
 builder.setContentIntent(pendingIntent);
 // Set full screen intent to trigger display of the fullscreen UI when the notification
 // manager deems it appropriate.
 builder.setFullScreenIntent(pendingIntent, true);
 // Setup notification content.
 builder.setSmallIcon( yourIconResourceId );
 builder.setContentTitle("Your notification title");
 builder.setContentText("Your notification content.");
 // Use builder.addAction(..) to add buttons to answer or reject the call.
 NotificationManager notificationManager = mContext.getSystemService(
     NotificationManager.class);
 notificationManager.notify(YOUR_CHANNEL_ID, YOUR_TAG, YOUR_ID, builder.build());
```