Criar um aplicativo de telefone padrão

Um aplicativo de telefone padrão permite que o framework do Android Telecom informe o aplicativo sobre o estado da chamada usando o gerenciador de funções e o serviço de chamada para criar um substituto para o app de telefone padrão em um dispositivo Android e implementar a API InCallService. Sua implementação precisa atender aos seguintes requisitos:

Ele não pode ter recursos de chamada e precisa consistir apenas na interface do usuário para chamadas. Ele precisa processar todas as ligações que a estrutura de telecomunicações conhece e não fazer suposições sobre a natureza delas. Por exemplo, ele não pode presumir que as chamadas sejam de telefonia com base em chip, nem implementar restrições de chamada baseadas em qualquer ConnectionService, como aplicação de restrições de telefonia para videochamadas.

Um app de chamadas permite que os usuários recebam ou façam chamadas de áudio ou videochamadas no dispositivo. Os apps de chamadas usam a própria interface do usuário para as chamadas em vez de usar a interface padrão do app Telefone, conforme mostrado na captura de tela a seguir.

Exemplo de um app de chamadas
Exemplo de um app de chamadas usando a própria interface do usuário

O framework do Android inclui o pacote android.telecom, que contém classes que ajudam a criar um app de chamadas de acordo com o framework de telecomunicações. A criação do seu app de acordo com o framework de telecomunicações oferece os seguintes benefícios:

  • Interoperação correta do app com o subsistema de telecomunicações nativo no dispositivo.
  • Interoperação correta do app com outros apps de chamadas que também aderem ao framework.
  • O framework ajuda o app a gerenciar o roteamento de áudio e vídeo.
  • O framework ajuda o app a determinar se as chamadas têm foco.

Declarações e permissões do manifesto

No manifesto do app, declare que ele usa o MANAGE_OWN_CALLS permissão, conforme mostrado no exemplo a seguir:

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

Para ver mais informações sobre como declarar permissões do app, consulte Permissões.

Você precisa declarar um serviço que especifica a classe que implementa a classe ConnectionService no seu app. O subsistema de telecomunicações requer que o serviço declare a permissão BIND_TELECOM_CONNECTION_SERVICE para ser capaz de se vincular a ele. O exemplo a seguir mostra como declarar o serviço no manifesto do aplicativo:

<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>

Para ver mais informações sobre a declaração de componentes de app, incluindo serviços, consulte Componentes do app.

Implementar o serviço de conexão

Seu app de chamadas precisa fornecer uma implementação da classe ConnectionService à qual o subsistema de telecomunicações pode se vincular. A implementação ConnectionService precisa modificar os seguintes métodos:

onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)

Para realizar uma nova chamada, o subsistema de telecomunicações chama esse método em resposta à chamada de placeCall(Uri, Bundle) pelo app. Seu app retorna uma nova instância da implementação da classe Connection (para ver mais informações, consulte Implementar a conexão) para representar a nova chamada realizada. Você pode personalizar ainda mais a conexão de saída realizando as seguintes ações:

onCreateOutgoingConnectionFailed(PhoneAccountHandle, ConnectionRequest)

O subsistema de telecomunicações chama esse método quando seu app chama o placeCall(Uri, Bundle), e a chamada não pode ser realizada. Em resposta a essa situação, seu app precisa informar ao usuário (por exemplo, usando uma caixa de alerta ou um aviso) que não foi possível realizar a chamada. Talvez seu app não consiga fazer uma chamada se houver uma chamada de emergência em andamento ou se houver outra chamada em andamento em outro app que não puder ser colocada em espera antes da sua chamada poder ser realizada.

onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)

O subsistema de telecomunicações chama esse método quando seu app chama o método addNewIncomingCall(PhoneAccountHandle, Bundle) para informar ao sistema sobre uma nova chamada recebida no seu app. Seu app retorna uma nova instância da implementação Connection (para ver mais informações, consulte Implementar a conexão) para representar a nova chamada recebida. Você pode personalizar ainda mais a conexão de entrada realizando as seguintes ações:

onCreateIncomingConnectionFailed(PhoneAccountHandle, ConnectionRequest)

O subsistema de telecomunicações chama esse método quando seu app chama o método addNewIncomingCall(PhoneAccountHandle, Bundle) para informar ao sistema de telecomunicações sobre uma nova chamada recebida, mas a chamada recebida não é permitida. Para ver mais informações, consulte Restrições de chamadas. Seu app precisa rejeitar silenciosamente a chamada recebida e, opcionalmente, postar uma notificação para informar ao usuário sobre a chamada perdida.

Implementar a conexão

Seu app precisa criar uma subclasse de Connection para representar as chamadas no seu app. É necessário modificar os seguintes métodos na implementação:

onShowIncomingCallUi()

O subsistema de telecomunicações chama esse método quando uma chamada recebida é adicionada e seu app precisa mostrar a IU relacionada.

onCallAudioStateChanged(CallAudioState)

O subsistema de telecomunicações chama esse método para informar ao app que a rota ou modo de áudio atual mudou. Ele é chamado em resposta à mudança do modo áudio no seu app com o método setAudioRoute(int). Esse método também poderá ser chamado se o sistema mudar a rota de áudio, por exemplo, quando um fone de ouvido Bluetooth for desconectado.

onHold()

O subsistema de telecomunicações chama esse método para colocar uma chamada em espera. Em resposta a essa solicitação, seu app precisa reter a chamada e invocar o método setOnHold() para informar ao sistema que a chamada está sendo retida. O subsistema de telecomunicações pode chamar esse método quando um serviço em chamada, como o Android Auto, que está mostrando a chamada quer redirecionar uma solicitação de usuário para colocar a chamada em espera. O subsistema de telecomunicações também chamará esse método se o usuário fizer uma chamada em outro app. Para mais informações sobre os serviços de chamada recebida, consulte InCallService.

onUnhold()

O subsistema de telecomunicações chama esse método para retomar uma chamada que foi colocada em espera. Depois que seu app retomar a chamada, ele precisará invocar o método setActive() para informar ao sistema que a chamada não está mais em espera. O subsistema de telecomunicações pode chamar esse método quando um serviço de chamada que está mostrando a chamada, como o Android Auto, quer redirecionar uma solicitação para retomar a chamada. Para ver mais informações sobre os serviços de chamada, consulte InCallService.

onAnswer()

O subsistema de telecomunicações chama esse método para informar ao seu app que uma chamada recebida precisa ser atendida. Assim que seu app atender à chamada, ele precisará invocar o método setActive() para informar ao sistema que a chamada foi atendida. O subsistema de telecomunicações pode chamar esse método quando seu app adiciona uma nova chamada recebida e já há uma chamada em andamento em outro app que não pode ser colocada em espera. O subsistema de telecomunicações exibe a IU de chamada recebida em nome do seu app nessas situações. O framework fornece um método sobrecarregado que permite especificar o estado do vídeo em que a chamada será atendida. Para ver mais informações, consulte onAnswer(int).

onReject()

O subsistema de telecomunicações chama esse método para rejeitar uma chamada recebida. Depois que seu app rejeitar a chamada, ele precisará chamar setDisconnected(DisconnectCause) e especificar REJECTED como o parâmetro. Seu app precisa chamar o método destroy() para informar ao sistema que ele processou a chamada. O subsistema de telecomunicações chama esse método quando o usuário rejeita uma chamada recebida no seu app.

onDisconnect()

O subsistema de telecomunicações chama esse método para desconectar uma chamada. Quando a chamada for encerrada, seu app precisará chamar o método setDisconnected(DisconnectCause) e especificar LOCAL como o parâmetro para indicar que uma solicitação do usuário fez com que a chamada fosse desconectada. Seu app precisa chamar o método destroy() para informar ao subsistema de telecomunicações que o app processou a chamada. O sistema poderá chamar esse método quando o usuário desconectar uma chamada por meio de outro serviço, como o Android Auto. O sistema também chama esse método quando sua chamada precisa ser desconectada para permitir que outra seja feita, por exemplo, se o usuário quiser fazer uma chamada de emergência. Para ver mais informações sobre os serviços de chamada, consulte InCallService.

Lidar com situações comuns de chamada

O uso da API ConnectionService no seu fluxo de chamadas envolve a interação com as outras classes no pacote android.telecom. As seções a seguir descrevem situações comuns de chamada e como seu app precisa usar as APIs para lidar com elas.

Atender as chamadas recebidas

O fluxo para lidar com as chamadas recebidas muda de acordo com a presença de chamadas em outros apps . A razão para a diferença nos fluxos é que o framework de telecomunicações precisa estabelecer algumas restrições quando há chamadas ativas em outros apps para garantir um ambiente estável para todos os apps de chamada no dispositivo. Para ver mais informações, consulte Restrições de chamada.

Nenhuma chamada ativa em outros apps

Para atender chamadas recebidas quando não houver chamadas ativas em outros apps, siga estas etapas:

  1. Seu app recebe uma nova chamada usando os mecanismos habituais.
  2. Use o método addNewIncomingCall(PhoneAccountHandle, Bundle) para informar ao subsistema de telecomunicações sobre a nova chamada recebida.
  3. O subsistema de telecomunicações será vinculado à implementação de ConnectionService no app e solicitará uma nova instância da classe Connection que representa a nova chamada recebida usando o método onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest).
  4. O subsistema de telecomunicações informa ao seu app que ele precisa mostrar a interface do usuário de chamada recebida usando o método onShowIncomingCallUi().
  5. Seu app mostra a IU de entrada usando uma notificação com uma intent de tela cheia associada. Para mais informações, consulte onShowIncomingCallUi().
  6. Chame o método setActive() se o usuário aceitar a chamada recebida. Se o usuário rejeitar a chamada, chame setDisconnected(DisconnectCause) especificando REJECTED como o parâmetro seguido por uma chamada para o método destroy().

Chamadas ativas em outros apps que não podem ser colocadas em espera

Para atender a chamadas recebidas quando houver chamadas ativas em outros apps que não possam ser colocadas em espera, siga estas etapas:

  1. Seu app recebe uma nova chamada usando os mecanismos habituais.
  2. Use o método addNewIncomingCall(PhoneAccountHandle, Bundle) para informar ao subsistema de telecomunicações sobre a nova chamada recebida.
  3. O subsistema de telecomunicações será vinculado à implementação de ConnectionService do seu app e solicitará uma nova instância do objeto Connection que representa a nova chamada recebida usando o método onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest).
  4. O subsistema de telecomunicações exibe a IU de chamada recebida para sua chamada.
  5. Se o usuário aceitar a chamada, o subsistema de telecomunicações chamará o método onAnswer(). Você precisa chamar o método setActive() para indicar ao subsistema de telecomunicações que a chamada está conectada.
  6. Se o usuário rejeitar a chamada, o subsistema de telecomunicações chamará o método onReject(). Você precisa chamar o método setDisconnected(DisconnectCause) especificando REJECTED como o parâmetro seguido por uma chamada para o método destroy().

Realizar chamadas

O fluxo para realizar uma chamada envolve lidar com a impossibilidade de fazer a chamada devido a restrições impostas pelo framework de telecomunicações. Para mais informações, consulte Restrições de chamada.

Para realizar uma chamada, siga estas etapas:

  1. O usuário inicia uma chamada realizada dentro do seu app.
  2. Use o método placeCall(Uri, Bundle) para informar ao subsistema de telecomunicações sobre a nova chamada realizada. Faça as seguintes considerações para os parâmetros do método:
    • O parâmetro Uri representa o endereço de destino da chamada. Para números de telefone normais, use o esquema de URI tel:.
    • O parâmetro Bundle permite que você forneça informações sobre seu app de chamadas adicionando o objeto PhoneAccountHandle do seu app ao EXTRA_PHONE_ACCOUNT_HANDLE extra. Seu app precisa fornecer o objeto PhoneAccountHandle a todas as chamadas realizadas.
    • O Bundle também permite que você especifique se a chamada realizada inclui vídeo, especificando o valor STATE_BIDIRECTIONAL no EXTRA_START_CALL_WITH_VIDEO_STATE extra. Considere que, por padrão, o subsistema de telecomunicações direciona as videochamadas para o viva-voz.
  3. O subsistema de telecomunicações é vinculado à implementação de ConnectionService do seu app.
  4. Se o app não conseguir realizar uma chamada, o subsistema de telecomunicações chamará o método onCreateOutgoingConnectionFailed(PhoneAccountHandle, ConnectionRequest) para informar ao app que a chamada não pode ser feita naquele momento. Seu app precisa informar ao usuário que a chamada não pode ser feita.
  5. Se o app conseguir realizar a chamada, o subsistema de telecomunicações chamará o método onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest). Seu app precisa retornar uma instância da classe Connection para representar a nova chamada realizada. Para ver mais informações sobre as propriedades que precisam ser definidas na conexão, consulte Implementar o serviço de conexão.
  6. Quando a chamada realizada for conectada, chame o método setActive() para informar ao subsistema de telecomunicações que a chamada está ativa.

Finalizar uma chamada

Para finalizar uma chamada, siga estas etapas:

  1. Chame setDisconnected(DisconnectCause) enviando LOCAL como o parâmetro se o usuário tiver finalizado a chamada ou envie REMOTE como o parâmetro se a outra parte tiver finalizado a chamada.
  2. Chame o método destroy().

Restrições de chamada

Para garantir uma experiência de chamada consistente e simples para os usuários, o framework de telecomunicações impõe algumas restrições para o gerenciamento de chamadas no dispositivo. Por exemplo, considere que o usuário instalou dois apps de chamada que implementam a API ConnectionService autogerenciada, o FooTalk e o BarTalk. Nesse caso, as seguintes restrições se aplicam:

  • Em dispositivos no nível da API 27 ou anterior, somente um app pode manter uma chamada em andamento a qualquer momento. Essa restrição significa que, enquanto um usuário tem uma chamada em andamento usando o app FooTalk, o app BarTalk não pode iniciar ou receber uma nova chamada.

    Em dispositivos nível da API 28 ou mais recente, se tanto o FooTalk quanto o BarTalk declararem as permissões CAPABILITY_SUPPORT_HOLD e CAPABILITY_HOLD, o usuário poderá manter mais de uma chamada em andamento alternando entre os apps para iniciar ou atender outra chamada.

  • Se o usuário estiver em chamadas gerenciadas normais, por exemplo, usando o app Telefone integrado, ele não poderá estar em chamadas originadas de apps de chamada. Isso significa que, se o usuário estiver em uma chamada normal usando a operadora de celular, ele não poderá estar em uma chamada do FooTalk ou BarTalk simultaneamente.

  • O subsistema de telecomunicações desconectará as chamadas do seu app se o usuário fizer uma chamada de emergência.

  • Seu app não pode receber ou fazer chamadas enquanto o usuário estiver em uma chamada de emergência.

  • Se houver uma chamada em andamento no outro app de chamadas quando seu app receber uma chamada, atender a chamada recebida finalizará todas as chamadas em andamento no outro app. Seu app não pode exibir a interface do usuário de chamada recebida. O framework de telecomunicações exibe a interface do usuário da chamada recebida e informa que atender à nova chamada finalizará as chamadas em andamento. Isso significa que se o usuário estiver em uma chamada do FooTalk e o app BarTalk receber uma chamada, o framework de telecomunicações informará que ele tem uma nova chamada recebida do BarTalk e que atender à chamada do BarTalk finalizará a chamada do FooTalk.

Torne-se o aplicativo de telefone padrão

O aplicativo de discador/telefone padrão é aquele que fornece a interface do usuário em chamada enquanto o dispositivo está em uma chamada. Ele também oferece ao usuário um meio para iniciar ligações e ver um histórico delas no dispositivo. Um dispositivo vem com um app de discador/telefone padrão fornecido pelo sistema. O usuário pode escolher um único aplicativo para assumir essa função no aplicativo do sistema. Um app que quer cumprir esse papel usa a RoleManager para solicitar o preenchimento do papel RoleManager.ROLE_DIALER.

O aplicativo de telefone padrão fornece uma interface do usuário enquanto o dispositivo está em uma chamada e o dispositivo é não está no modo carro (ou seja, UiModeManager#getCurrentModeType() não está Configuration.UI_MODE_TYPE_CAR).

Para preencher o papel RoleManager.ROLE_DIALER, um app precisa atender a uma de requisitos:

  • Ele precisa processar a intent Intent#ACTION_DIAL. Isso significa que o app precisa oferecer uma interface de teclado para o usuário iniciar chamadas.
  • Ele precisa implementar totalmente a API InCallService e fornecer uma chamada recebida e uma interface de usuário de chamada em andamento.

Observação: se o app que preencher a RoleManager.ROLE_DIALER retornar uma null InCallService durante a vinculação, o framework de telecomunicações será automaticamente a voltar a usar o aplicativo discador pré-carregado no dispositivo. O sistema exibirá uma notificação para o usuário informar que a chamada continuou usando o aplicativo discador pré-carregado. Seu app nunca deve retornar uma vinculação null. isso significa que ela não cumpre requisitos de RoleManager.ROLE_DIALER.

Observação: caso seu app preencha RoleManager.ROLE_DIALER e faça mudanças em o que faz com que ele não atenda mais aos requisitos desse papel, RoleManager vai remover automaticamente o app da função e fechar. seu app. Por exemplo, se você usar PackageManager.setComponentEnabledSetting(ComponentName, int, int) para desativar programaticamente o InCallService declarado no manifesto, o app não atendem mais aos requisitos esperados RoleManager.ROLE_DIALER.

O discador pré-carregado SEMPRE será usado quando o usuário fizer uma chamada de emergência, mesmo que seu app preenche o papel de RoleManager.ROLE_DIALER. Para garantir a melhor forma ao fazer uma chamada de emergência, o discador padrão deve SEMPRE usar TelecomManager.placeCall(Uri, Bundle) para fazer chamadas (incluindo chamadas de emergência). Isso garante que a plataforma possa verificar se a solicitação veio de o discador padrão. Se um app de discador não pré-carregado usar Intent#ACTION_CALL para colocar uma chamada de emergência, ela será levada para o app discador pré-carregado usando Intent#ACTION_DIAL para confirmação. essa experiência do usuário não é ideal.

Confira abaixo um exemplo de registro de manifesto para um InCallService. Os metadados TelecomManager#METADATA_IN_CALL_SERVICE_UI indica que este determinado A implementação de InCallService pretende substituir a interface integrada de chamada. O TelecomManager#METADATA_IN_CALL_SERVICE_RINGING dos metadados indica que esse O dispositivo InCallService vai tocar o toque das chamadas recebidas. Consulte abaixo para mais informações sobre como mostrar a chamada recebida interface do usuário e como tocar o toque no app.

 <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>

Observação: você NÃO deve marcar seu InCallService com o atributo android:exported="false" isso pode resultar em falha na vinculação à sua implementação durante as chamadas.

Além de implementar a API InCallService, também é preciso declarar uma atividade no seu manifesto, que processa a intent Intent#ACTION_DIAL. O exemplo abaixo ilustra como isso é feito:

 <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>

Quando um usuário instalar seu aplicativo e executá-lo pela primeira vez, você deve usar o RoleManager para perguntar ao usuário se ele quer que o app ser o novo app de telefone padrão.

O código abaixo mostra como seu app pode solicitar para se tornar o app padrão de telefone/discador:

 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
         }
     }
 }

Acesso ao InCallService para dispositivos wearable

    Caso seu app seja um app complementar de terceiros e queira acessar APIs InCallService, o que app poderia fazer são:

    1. Declarar a permissão MANAGE_ONGOING_CALLS no manifesto
    2. Associar a um dispositivo wearable físico pelo API CompanionDeviceManager como um app complementar. Consulte: https://developer.android.com/guide/topics/connectivity/companion-device-pairing
    3. Implementar este InCallService com a permissão BIND_INCALL_SERVICE

Mostrando a notificação de chamada recebida

Quando seu app recebe uma nova chamada via InCallService#onCallAdded(Call), ela é responsável por exibir uma interface para a chamada recebida. Ele deve fazer isso usando NotificationManager APIs para postar uma nova notificação de chamada recebida.

Onde o app declara os metadados TelecomManager#METADATA_IN_CALL_SERVICE_RINGING, ele é responsável por reproduzir o toque para chamadas recebidas. Seu app deve criar um NotificationChannel, que especifica o toque desejado. Exemplo:

 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);

Quando o app recebe uma nova chamada, ele cria um Notification para o chamada recebida e a associa ao seu canal de notificação de chamadas recebidas. É possível especificar PendingIntent na notificação que abre a tela cheia interface de chamadas recebidas. O framework do gerenciador de notificações exibirá sua notificação como uma notificação de alerta se o usuário estiver usando o telefone ativamente. Quando o usuário não estiver usando o telefone, a interface de chamadas recebidas em tela cheia será usada. Exemplo:

 // 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());
```