Crea un'applicazione telefonica predefinita

Un'applicazione telefonica predefinita consente al framework Android Telecom di informare l'applicazione dello stato della chiamata utilizzando il gestore ruoli e il servizio in-call per creare una sostituzione dell'app telefono predefinita su un dispositivo Android e implementare l'API InCallService. La tua implementazione deve soddisfare i seguenti requisiti:

Non deve avere funzionalità di chiamata e deve essere costituito esclusivamente dall'interfaccia utente. Deve gestire tutte le chiamate di cui il framework Telecom è a conoscenza e non deve fare ipotesi sulla natura di tali chiamate. Ad esempio, non deve presupporre che le chiamate siano chiamate di telefonia basate su SIM, né implementare limitazioni di chiamata basate su un qualsiasi ConnectionService, come l'applicazione di limitazioni di telefonia per le videochiamate.

Un'app per le chiamate consente agli utenti di ricevere o effettuare chiamate audio o videochiamate sul proprio dispositivo. Le app di chiamata utilizzano la propria interfaccia utente per le chiamate anziché l'interfaccia predefinita dell'app Telefono, come mostrato nello screenshot seguente.

Esempio di app per chiamate
Esempio di app per chiamate con la propria interfaccia utente

Il framework Android include il pacchetto android.telecom, che contiene classi che consentono di creare un'app per le chiamate in base al framework di telecomunicazioni. La creazione della tua app in base al framework per le telecomunicazioni offre i seguenti vantaggi:

  • La tua app interagisce correttamente con il sottosistema di telecomunicazioni nativo del dispositivo.
  • La tua app interagisce correttamente con altre app di chiamata che rispettano il framework.
  • Il framework aiuta l'app a gestire il routing audio e video.
  • Il framework aiuta la tua app a determinare se le chiamate sono focalizzate.

Dichiarazioni e autorizzazioni del file manifest

Nel file manifest dell'app, dichiara che la tua app utilizza l'autorizzazione MANAGE_OWN_CALLS, come mostrato nell'esempio seguente:

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

Per ulteriori informazioni sulla dichiarazione delle autorizzazioni app, consulta Autorizzazioni.

Devi dichiarare un servizio che specifica la classe che implementa la classe ConnectionService nella tua app. Il sottosistema telecom richiede che il servizio dichiari che l'autorizzazione BIND_TELECOM_CONNECTION_SERVICE può essere associata al servizio. L'esempio seguente mostra come dichiarare il servizio nel file manifest dell'app:

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

Per ulteriori informazioni sulla dichiarazione dei componenti dell'app, inclusi i servizi, consulta Componenti dell'app.

Implementare il servizio di connessione

L'app per le chiamate deve fornire un'implementazione della classe ConnectionService a cui può essere associato il sottosistema di telecomunicazioni. L'implementazione ConnectionService dovrebbe sostituire i seguenti metodi:

onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)

Il sottosistema delle telecomunicazioni chiama questo metodo in risposta alla chiamata dell'app placeCall(Uri, Bundle) per creare una nuova chiamata in uscita. La tua app restituisce una nuova istanza dell'implementazione della classe Connection (per ulteriori informazioni, consulta Implementare la connessione) per rappresentare la nuova chiamata in uscita. Puoi personalizzare ulteriormente la connessione in uscita eseguendo queste azioni:

onCreateOutgoingConnectionFailed(PhoneAccountHandle, ConnectionRequest)

Il sottosistema delle telecomunicazioni chiama questo metodo quando la tua app chiama il metodo placeCall(Uri, Bundle) e la chiamata in uscita non può essere effettuata. In risposta a questa situazione, l'app deve informare l'utente (ad esempio, utilizzando una casella di avviso o un annuncio toast) che non è stato possibile effettuare la chiamata in uscita. La tua app potrebbe non riuscire a effettuare una chiamata se è in corso una chiamata di emergenza o se è in corso una chiamata in un'altra app che non può essere messa in attesa prima di effettuare una chiamata.

onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)

Il sottosistema delle telecomunicazioni chiama questo metodo quando l'app chiama il metodo addNewIncomingCall(PhoneAccountHandle, Bundle) per informare il sistema di una nuova chiamata in arrivo nell'app. L'app restituisce una nuova istanza dell'implementazione Connection (per saperne di più, consulta Implementare la connessione) per rappresentare la nuova chiamata in arrivo. Puoi personalizzare ulteriormente la connessione in entrata eseguendo queste operazioni:

onCreateIncomingConnectionFailed(PhoneAccountHandle, ConnectionRequest)

Il sottosistema delle telecomunicazioni chiama questo metodo quando l'app chiama il metodo addNewIncomingCall(PhoneAccountHandle, Bundle) per informare Telecom di una nuova chiamata in arrivo, ma la chiamata in arrivo non è consentita. Per saperne di più, consulta i limiti di chiamata. L'app deve rifiutare in silenzio la chiamata in arrivo e, facoltativamente, pubblicare una notifica per informare l'utente della chiamata senza risposta.

Implementare la connessione

La tua app deve creare una sottoclasse Connection per rappresentare le chiamate nell'app. Dovresti eseguire l'override dei seguenti metodi nell'implementazione:

onShowIncomingCallUi()

Il sottosistema delle telecomunicazioni chiama questo metodo quando aggiungi una nuova chiamata in arrivo e la tua app dovrebbe mostrare la relativa UI per le chiamate in arrivo.

onCallAudioStateChanged(CallAudioState)

Il sottosistema delle telecomunicazioni chiama questo metodo per informare la tua app che il percorso o la modalità audio correnti sono cambiati. Questo comando viene chiamato in risposta alla modifica della modalità audio da parte dell'app usando il metodo setAudioRoute(int). Questo metodo può essere chiamato anche se il sistema cambia il percorso audio (ad esempio, quando si disconnettono le cuffie Bluetooth).

onHold()

Il sottosistema delle telecomunicazioni chiama questo metodo quando vuole mettere una chiamata in attesa. In risposta a questa richiesta, la tua app deve sospendere la chiamata e poi richiamare il metodo setOnHold() per comunicare al sistema che la chiamata è in attesa. Il sottosistema delle telecomunicazioni potrebbe chiamare questo metodo quando un servizio in chiamata, come Android Auto, che mostra che la tua chiamata vuole inoltrare una richiesta dell'utente di mettere la chiamata in attesa. Il sottosistema delle telecomunicazioni chiama questo metodo anche se l'utente effettua una chiamata attiva in un'altra app. Per ulteriori informazioni sui servizi in linea con le chiamate, vedi InCallService.

onUnhold()

Questo metodo viene richiamato dal sottosistema di telecomunicazioni quando vuole riprendere una chiamata che è stata messa in attesa. Una volta ripresa la chiamata, l'app deve richiamare il metodo setActive() per comunicare al sistema che la chiamata non è più in attesa. Il sottosistema di telecomunicazioni potrebbe chiamare questo metodo quando un servizio in-chiamata, come Android Auto, mostra che la tua chiamata vuole inoltrare una richiesta per riprendere la chiamata. Per ulteriori informazioni sui servizi in chiamata, vedi InCallService.

onAnswer()

Il sottosistema delle telecomunicazioni chiama questo metodo per comunicare all'app che a una chiamata in arrivo. Dopo che l'app ha risposto alla chiamata, deve richiamare il metodo setActive() per comunicare al sistema che la chiamata ha ricevuto una risposta. Il sottosistema di telecomunicazioni potrebbe chiamare questo metodo quando la tua app aggiunge una nuova chiamata in arrivo ed è già in corso una chiamata in corso in un'altra app che non può essere messa in attesa. Il sottosistema di telecomunicazione mostra l'interfaccia utente delle chiamate in arrivo per conto della tua app in questi casi. Il framework fornisce un metodo di sovraccarico che fornisce supporto per specificare lo stato del video in cui rispondere alla chiamata. Per scoprire di più, consulta onAnswer(int).

onReject()

Il sottosistema delle telecomunicazioni chiama questo metodo quando vuole rifiutare una chiamata in arrivo. Dopo che l'app ha rifiutato la chiamata, deve chiamare setDisconnected(DisconnectCause) e specificare REJECTED come parametro. L'app deve quindi chiamare il metodo destroy() per comunicare al sistema che ha elaborato la chiamata. Il sottosistema delle telecomunicazioni chiama questo metodo quando l'utente ha rifiutato una chiamata in arrivo dalla tua app.

onDisconnect()

Il sottosistema delle telecomunicazioni chiama questo metodo quando vuole disconnettere una chiamata. Al termine della chiamata, l'app deve chiamare il metodo setDisconnected(DisconnectCause) e specificare LOCAL come parametro per indicare che una richiesta dell'utente ha causato la disconnessione della chiamata. L'app dovrebbe quindi chiamare il metodo destroy() per informare il sottosistema di telecomunicazione che ha elaborato la chiamata. Il sistema può chiamare questo metodo quando l'utente ha disconnesso una chiamata tramite un altro servizio durante la chiamata, come Android Auto. Il sistema chiama questo metodo anche quando la chiamata deve essere disconnessa per consentire l'esecuzione di un'altra chiamata, ad esempio se l'utente vuole effettuare una chiamata di emergenza. Per ulteriori informazioni sui servizi in chiamata, vedi InCallService.

Gestire scenari di chiamata comuni

L'utilizzo dell'API ConnectionService nel flusso di chiamata comporta l'interazione con le altre classi nel pacchetto android.telecom. Le seguenti sezioni descrivono scenari di chiamata comuni e come l'app deve utilizzare le API per gestirli.

Risposta alle chiamate in arrivo

Il flusso per gestire le chiamate in arrivo cambia indipendentemente dal fatto che ci siano o meno chiamate in altre app. Il motivo della differenza nei flussi è che il framework per le telecomunicazioni deve stabilire alcuni vincoli quando ci sono chiamate attive in altre app per garantire un ambiente stabile per tutte le app per le chiamate sul dispositivo. Per ulteriori informazioni, consulta la sezione Vincoli relativi alle chiamate.

Nessuna chiamata attiva in altre app

Per rispondere alle chiamate in arrivo quando non ci sono chiamate attive in altre app, procedi nel seguente modo:

  1. L'app riceve una nuova chiamata in arrivo utilizzando i consueti meccanismi.
  2. Utilizza il metodo addNewIncomingCall(PhoneAccountHandle, Bundle) per informare il sottosistema di telecomunicazioni della nuova chiamata in arrivo.
  3. Il sottosistema delle telecomunicazioni si associa all'implementazione ConnectionService della tua app e richiede una nuova istanza della classe Connection che rappresenti la nuova chiamata in entrata utilizzando il metodo onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest).
  4. Il sottosistema di telecomunicazioni comunica alla tua app che deve mostrare l'interfaccia utente delle chiamate in entrata utilizzando il metodo onShowIncomingCallUi().
  5. La tua app mostra la UI in arrivo utilizzando una notifica con un intent a schermo intero associato. Per ulteriori informazioni, vedi onShowIncomingCallUi().
  6. Chiama il metodo setActive() se l'utente accetta la chiamata in arrivo oppure setDisconnected(DisconnectCause) specificando REJECTED come parametro seguito da una chiamata al metodo destroy() se l'utente rifiuta la chiamata in arrivo.

Chiamate attive in altre app che non possono essere messe in attesa

Per rispondere a chiamate in arrivo quando ci sono chiamate attive in altre app che non è possibile mettere in attesa, svolgi i seguenti passaggi:

  1. L'app riceve una nuova chiamata in arrivo utilizzando i consueti meccanismi.
  2. Utilizza il metodo addNewIncomingCall(PhoneAccountHandle, Bundle) per informare il sottosistema di telecomunicazioni della nuova chiamata in arrivo.
  3. Il sottosistema di telecomunicazione si associa all'implementazione ConnectionService della tua app e richiede una nuova istanza dell'oggetto Connection che rappresenti la nuova chiamata in arrivo utilizzando il metodo onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest).
  4. Il sottosistema di telecomunicazione mostra l'interfaccia utente delle chiamate in arrivo per la chiamata in arrivo.
  5. Se l'utente accetta la chiamata, il sottosistema di telecomunicazione chiama il metodo onAnswer(). Devi chiamare il metodo setActive() per indicare al sottosistema di telecomunicazione che la chiamata è ora connessa.
  6. Se l'utente rifiuta la chiamata, il sottosistema di telecomunicazione chiama il metodo onReject(). Devi chiamare il metodo setDisconnected(DisconnectCause) specificando REJECTED come parametro seguito da una chiamata al metodo destroy().

Effettua chiamate in uscita

Il flusso per effettuare una chiamata in uscita riguarda la gestione della possibilità che la chiamata non possa essere effettuata a causa dei vincoli imposti dal framework di telecomunicazioni. Per ulteriori informazioni, consulta la sezione Vincoli relativi alle chiamate.

Per effettuare una chiamata in uscita, procedi nel seguente modo:

  1. L'utente avvia una chiamata in uscita all'interno dell'app.
  2. Utilizza il metodo placeCall(Uri, Bundle) per informare il sottosistema di telecomunicazioni della nuova chiamata in uscita. Prendi le seguenti considerazioni per i parametri del metodo:
    • Il parametro Uri rappresenta l'indirizzo a cui viene effettuata la chiamata. Per i numeri di telefono normali, utilizza lo schema URI tel:.
    • Il parametro Bundle ti consente di fornire informazioni sulla tua app per le chiamate aggiungendo l'oggetto PhoneAccountHandle dell'app al EXTRA_PHONE_ACCOUNT_HANDLE extra. L'app deve fornire l'oggetto PhoneAccountHandle a ogni chiamata in uscita.
    • Il parametro Bundle ti consente anche di specificare se la chiamata in uscita include video specificando il valore STATE_BIDIRECTIONAL nell'elemento EXTRA_START_CALL_WITH_VIDEO_STATE aggiuntivo. Tieni presente che, per impostazione predefinita, il sottosistema delle telecomunicazioni indirizza le videochiamate agli altoparlanti.
  3. Il sottosistema di telecomunicazioni si associa all'implementazione ConnectionService della tua app.
  4. Se la tua app non è in grado di effettuare una chiamata in uscita, il sottosistema di telecomunicazione chiama il metodo onCreateOutgoingConnectionFailed(PhoneAccountHandle, ConnectionRequest) per informare l'app che al momento non è possibile effettuare la chiamata. L'app deve informare l'utente che non è possibile effettuare la chiamata.
  5. Se la tua app è in grado di effettuare la chiamata in uscita, il sottosistema di telecomunicazione chiama il metodo onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest). L'app dovrebbe restituire un'istanza della classe Connection per rappresentare la nuova chiamata in uscita. Per ulteriori informazioni sulle proprietà che dovresti impostare per la connessione, consulta Implementare il servizio di connessione.
  6. Una volta connessa la chiamata in uscita, chiama il metodo setActive() per comunicare al sottosistema di telecomunicazioni che la chiamata è attiva.

Terminare una chiamata

Per terminare una chiamata:

  1. Richiama setDisconnected(DisconnectCause) che invia LOCAL come parametro se l'utente ha terminato la chiamata oppure invia REMOTE come parametro se l'altra parte ha terminato la chiamata.
  2. Chiama il metodo destroy().

Vincoli di chiamata

Per garantire agli utenti un'esperienza di chiamata semplice e coerente, il framework di telecomunicazione applica alcuni vincoli per la gestione delle chiamate sul dispositivo. Ad esempio, supponiamo che l'utente abbia installato due app di chiamata che implementano l'API ConnectionService autogestita, FooTalk e BarTalk. In questo caso, si applicano i seguenti vincoli:

  • Sui dispositivi eseguiti con livello API 27 o precedente, solo un'app può mantenere una chiamata in corso in un determinato momento. Questo vincolo significa che, mentre un utente ha una chiamata in corso utilizzando l'app FooTalk, l'app BarTalk non può avviare o ricevere una nuova chiamata.

    Sui dispositivi in esecuzione al livello API 28 o superiore, se sia FooTalk che BarTalk dichiarano le autorizzazioni CAPABILITY_SUPPORT_HOLD e CAPABILITY_HOLD, l'utente può mantenere più di una chiamata in corso passando da un'app all'altra per avviare o rispondere a un'altra chiamata.

  • Se l'utente è coinvolto in chiamate gestite regolari (ad esempio, utilizzando l'app Telefono o Telefono integrata), non può partecipare alle chiamate provenienti da app per le chiamate. Ciò significa che se l'utente sta partecipando a una chiamata normale utilizzando il proprio operatore di telefonia mobile, non può partecipare contemporaneamente anche a una chiamata FooTalk o BarTalk.

  • Il sottosistema di telecomunicazioni disconnette le chiamate della tua app se l'utente compone una chiamata di emergenza.

  • L'app non può ricevere o effettuare chiamate mentre l'utente si trova in una chiamata di emergenza.

  • Se è in corso una chiamata in un'altra app per le chiamate quando la tua app riceve una chiamata in arrivo, quando rispondi alla chiamata vengono terminate tutte le chiamate in corso nell'altra app. L'app non dovrebbe mostrare la solita interfaccia utente delle chiamate in arrivo. Il framework di telecomunicazione mostra l'interfaccia utente della chiamata in entrata e informa l'utente che rispondendo alla nuova chiamata terminerà le chiamate in corso. Ciò significa che se l'utente sta partecipando a una chiamata FooTalk e l'app BarTalk riceve una chiamata in arrivo, il framework di telecomunicazioni informa l'utente che ha una nuova chiamata BarTalk in arrivo e che la risposta alla chiamata BarTalk termina la chiamata FooTalk.

Diventa l'app predefinita del telefono

L'app tastiera/telefono predefinita è quella che fornisce l'interfaccia utente durante la chiamata mentre il dispositivo è impegnato in una chiamata. Consente inoltre all'utente di effettuare chiamate e visualizzare una cronologia delle chiamate sul proprio dispositivo. Un dispositivo è associato a un'app tastiera/telefono predefinita fornita dal sistema. L'utente può scegliere una singola app per assumere questo ruolo dall'app di sistema. Un'app che vuole svolgere questo ruolo utilizza RoleManager per richiedere il ruolo RoleManager.ROLE_DIALER.

L'app Telefono predefinita fornisce un'interfaccia utente mentre il dispositivo è in una chiamata e il dispositivo non è in modalità automobile (ad esempio, UiModeManager#getCurrentModeType() non è Configuration.UI_MODE_TYPE_CAR).

Per soddisfare il ruolo RoleManager.ROLE_DIALER, un'app deve soddisfare una serie di requisiti:

  • Deve gestire l'intent Intent#ACTION_DIAL. Ciò significa che l'app deve fornire all'utente un'interfaccia utente sul tastierino per consentire le chiamate in uscita.
  • Deve implementare completamente l'API InCallService e fornire sia una UI per le chiamate in entrata sia una UI per le chiamate in corso.

Nota: se l'app che riempie RoleManager.ROLE_DIALER restituisce un null InCallService durante l'associazione, il framework Telecom ricorrerà automaticamente all'app Telefono precaricata sul dispositivo. Il sistema mostrerà una notifica all'utente per informarlo che la sua chiamata è stata continuata utilizzando l'app Telefono precaricata. La tua app non dovrebbe mai restituire un'associazione null, pertanto l'app non soddisfa i requisiti di RoleManager.ROLE_DIALER.

Nota: se la tua app compila RoleManager.ROLE_DIALER e apporta modifiche in fase di runtime che impediscono di soddisfare più i requisiti di questo ruolo, RoleManager rimuoverà automaticamente la tua app dal ruolo e chiuderà l'app. Ad esempio, se utilizzi PackageManager.setComponentEnabledSetting(ComponentName, int, int) per disabilitare in modo programmatico il valore InCallService dichiarato dall'app nel file manifest, l'app non soddisferà più i requisiti previsti per RoleManager.ROLE_DIALER.

La tastiera precaricata verrà SEMPRE usata quando l'utente effettua una chiamata di emergenza, anche se la tua app ha il ruolo RoleManager.ROLE_DIALER. Per garantire un'esperienza ottimale quando si effettua una chiamata di emergenza, la tastiera predefinita deve SEMPRE utilizzare TelecomManager.placeCall(Uri, Bundle) per effettuare chiamate (incluse le chiamate di emergenza). Ciò garantisce che la piattaforma sia in grado di verificare che la richiesta provenga dalla tastiera predefinita. Se un'app Telefono non precaricata utilizza Intent#ACTION_CALL per effettuare una chiamata di emergenza, verrà trasferita all'app Telefono precaricata utilizzando Intent#ACTION_DIAL per la conferma. Si tratta di un'esperienza utente non ottimale.

Di seguito è riportato un esempio di registrazione del file manifest per un InCallService. I metadati TelecomManager#METADATA_IN_CALL_SERVICE_UI indicano che questa particolare implementazione di InCallService intende sostituire l'interfaccia utente integrata nelle chiamate. I metadati TelecomManager#METADATA_IN_CALL_SERVICE_RINGING indicano che questo InCallService riprodurrà la suoneria per le chiamate in arrivo. Leggi di seguito per maggiori informazioni sulla visualizzazione dell'UI delle chiamate in arrivo e sulla riproduzione della suoneria nella tua 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>

Nota: NON devi contrassegnare InCallService con l'attributo android:exported="false", in quanto ciò potrebbe impedire l'associazione alla tua implementazione durante le chiamate.

Oltre a implementare l'API InCallService, devi anche dichiarare nel manifest un'attività che gestisce l'intent Intent#ACTION_DIAL. L'esempio seguente illustra come eseguire questa operazione:

 <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 un utente installa la tua applicazione e la esegue per la prima volta, devi utilizzare RoleManager per richiedere all'utente di vedere se vuole che la tua app sia la nuova app telefonica predefinita.

Il codice riportato di seguito mostra in che modo la tua app può richiedere di diventare l'app predefinita del telefono/della tastiera:

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

Accesso a InCallService per i dispositivi indossabili

    Se la tua app è un'app complementare di terze parti e vuole accedere alle API InCallService, le operazioni che puoi eseguire sono:

    1. Dichiara l'autorizzazione MANAGE_ONGOING_CALLS nel file manifest
    2. Associa un dispositivo indossabile fisico tramite l'API CompanionDeviceManager come app complementare. Consulta: https://developer.android.com/guide/topics/connectivity/companion-device-pairing
    3. Implementa questo InCallService con autorizzazione BIND_INCALL_SERVICE

Visualizzazione dell'avviso di chiamata in arrivo

Quando la tua app riceve una nuova chiamata in arrivo tramite InCallService#onCallAdded(Call), deve mostrare un'interfaccia utente per le chiamate in arrivo per la chiamata in arrivo. Deve farlo utilizzando le API NotificationManager per pubblicare una nuova notifica di chiamata in arrivo.

Quando la tua app dichiara i metadati TelecomManager#METADATA_IN_CALL_SERVICE_RINGING, deve riprodurre la suoneria per le chiamate in arrivo. L'app deve creare un elemento NotificationChannel che specifica la suoneria desiderata. Ecco alcuni esempi:

 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 l'app riceve una nuova chiamata, crea un Notification per la chiamata in arrivo e la associa al tuo canale di notifica delle chiamate in arrivo. Puoi specificare un PendingIntent nella notifica per avviare l'interfaccia utente per le chiamate in arrivo a schermo intero. Il framework del gestore delle notifiche visualizzerà la notifica come notifica in evidenza se l'utente sta utilizzando attivamente il telefono. Quando l'utente non usa lo smartphone, viene utilizzata la UI per le chiamate in arrivo a schermo intero. Ecco alcuni esempi:

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