Panoramica del protocollo di avvio delle sessioni

Rileva eSIM e schede SIM

Rilevamento delle carte

I dispositivi Android con schede SIM ed eSIM utilizzano i seguenti ID nelle API di telefonia, tra cui ["TelephonyManager"](/reference/android/telephony/TelephonyManager) e [`SubscriptionManager"](/reference/android/telephony/SubscriptionManager): * ID abbonamento: ID univoco di un abbonamento mobile. * Indice o ID slot logico: indice univoco che fa riferimento a uno slot SIM logico. Gli ID slot logici iniziano da 0 e salgono a seconda del numero di slot attivi supportati su un dispositivo. Ad esempio, un dispositivo dual SIM dispone in genere di slot 0 e slot 1. Se un dispositivo dispone di più slot fisici, ma supporta solo uno slot attivo, avrà solo l'ID slot logico 0. * Indice o ID slot fisico: indice univoco che fa riferimento a uno slot SIM fisico. Gli ID degli slot fisici iniziano da 0 e salgono a seconda del numero di slot fisici sul dispositivo. È diverso dal numero di slot logici di un dispositivo, che corrisponde al numero di slot attivi che un dispositivo è in grado di utilizzare. Ad esempio, un dispositivo che passa dalla modalità dual SIM alla modalità SIM singola e viceversa potrebbe avere sempre due slot fisici, mentre in modalità SIM singola avrà un solo slot logico. * ID carta: ID univoco utilizzato per identificare una UiccCard. ![Un diagramma di come gli ID vengono utilizzati in un caso con due slot logici e tre slot fisici](/images/guide/topics/connectivity/tel-ids.png) Nell'immagine precedente: * Il dispositivo ha due slot logici. * Nello slot fisico 0 è presente una scheda UICC fisica con un profilo attivo. * Nello slot fisico 2 è presente una eUICC con un profilo attivo. * Lo slot fisico 1 non è attualmente in uso. ![Un diagramma di come vengono utilizzati gli ID in un caso con tre slot logici e due slot fisici](/images/guide/topics/connectivity/tel-ids-2.png) Nell'immagine precedente: * Il dispositivo ha tre slot logici. * Nello slot fisico 0 è presente una scheda UICC fisica con un profilo attivo. * Nello slot fisico 1 è presente un eUICC con due profili scaricati, entrambi attivi utilizzando MEP (Multiple Enabled Profiles).

Panoramica del protocollo di avvio della sessione

Android fornisce un'API che supporta il protocollo SIP (Session Initiation Protocol). Ciò consente di aggiungere alle applicazioni funzionalità di telefonia Internet basate su SIP. Android include uno stack completo di protocolli SIP e servizi integrati di gestione delle chiamate che consentono alle applicazioni di configurare facilmente chiamate vocali in uscita e in entrata, senza dover gestire le sessioni, la comunicazione a livello di trasporto o la registrazione o la riproduzione audio diretta.

Ecco alcuni esempi dei tipi di applicazioni che potrebbero utilizzare l'API SIP:

  • Videoconferenze
  • Messaggistica immediata

Requisiti e limitazioni

Di seguito sono riportati i requisiti per lo sviluppo di un'applicazione SIP:

  • Devi disporre di un dispositivo mobile con Android 2.3 o versioni successive.
  • SIP viene eseguito tramite una connessione dati wireless, pertanto il dispositivo deve disporre di una connessione dati (con un servizio dati mobili o Wi-Fi). Ciò significa che non puoi eseguire test sulla durata di visualizzazione media, ma solo su un dispositivo fisico. Per maggiori dettagli, consulta Test delle applicazioni SIP.
  • Ogni partecipante alla sessione di comunicazione dell'applicazione deve avere un account SIP. Esistono diversi provider SIP che offrono account SIP.

Nota: la raccolta android.net.sip non supporta le videochiamate. Se vuoi implementare le chiamate VOIP utilizzando uno stack SIP come android.net.sip, dai un'occhiata a una delle numerose alternative open source moderne come base per qualsiasi implementazione di chiamate VOIP. In alternativa, puoi implementare l'API ConnectionService per fornire una stretta integrazione di queste chiamate nell'app Dialer del dispositivo.

Classi e interfacce API SIP

Ecco un riepilogo delle classi e un'interfaccia (SipRegistrationListener) incluse nell'API Android SIP:

Lezione/interfaccia Descrizione
SipAudioCall Gestisce una chiamata audio Internet tramite SIP.
SipAudioCall.Listener Listener di eventi relativi a una chiamata SIP, ad esempio quando una chiamata viene ricevuta ("suonata") o una chiamata in uscita ("in chiamata").
SipErrorCode Definisce i codici di errore restituiti durante le azioni SIP.
SipManager Fornisce le API per le attività SIP, come l'avvio di connessioni SIP, e fornisce l'accesso ai servizi SIP correlati.
SipProfile Definisce un profilo SIP, inclusi un account SIP, informazioni sul dominio e sul server.
SipProfile.Builder Classe di supporto per la creazione di un profilo SipProfile.
SipSession Rappresenta una sessione SIP associata a una finestra di dialogo SIP o a una transazione autonoma non all'interno di una finestra di dialogo.
SipSession.Listener Listener per gli eventi relativi a una sessione SIP, ad esempio quando una sessione viene registrata ("al momento della registrazione") o una chiamata è in uscita ("in chiamata").
SipSession.State Definisce gli stati della sessione SIP, ad esempio "registrazione", "chiamata in uscita" e "in chiamata".
SipRegistrationListener Un'interfaccia che funge da listener per gli eventi di registrazione SIP.

Creazione del manifest in corso...

Se stai sviluppando un'applicazione che utilizza l'API SIP, ricorda che la funzionalità è supportata solo su Android 2.3 (livello API 9) e versioni successive della piattaforma. Inoltre, tra i dispositivi con Android 2.3 (livello API 9) o versioni successive, non tutti i dispositivi supporteranno SIP.

Per utilizzare SIP, aggiungi le seguenti autorizzazioni al file manifest dell'applicazione:

  • android.permission.USE_SIP
  • android.permission.INTERNET

Per assicurarti che l'applicazione possa essere installata solo su dispositivi in grado di supportare SIP, aggiungi quanto segue al file manifest dell'applicazione:

<uses-sdk android:minSdkVersion="9" />

Questo indica che la tua applicazione richiede Android 2.3 o versioni successive. Per ulteriori informazioni, consulta la sezione Livelli API e la documentazione relativa all'elemento <uses-sdk>.

Per controllare il modo in cui la tua applicazione viene filtrata dai dispositivi che non supportano SIP (ad esempio, su Google Play), aggiungi quanto segue al file manifest dell'applicazione:

<uses-feature android:name="android.software.sip.voip" />

Questo indica che l'applicazione utilizza l'API SIP. La dichiarazione deve includere un attributo android:required che indica se vuoi che l'applicazione venga filtrata o meno dai dispositivi che non offrono supporto SIP. Potrebbero essere necessarie anche altre dichiarazioni <uses-feature>, a seconda dell'implementazione. Per ulteriori informazioni, consulta la documentazione relativa all'elemento <uses-feature>.

Se l'applicazione è progettata per ricevere chiamate, devi anche definire un ricevitore (sottoclasse BroadcastReceiver) nel manifest dell'applicazione:

<receiver android:name=".IncomingCallReceiver" android:label="Call Receiver" />

Ecco alcuni estratti del file manifest SipDemo:

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

Creazione di SipManager

Per utilizzare l'API SIP, l'applicazione deve creare un oggetto SipManager. L'SipManager si occupa di quanto segue nella tua richiesta:

  • Avvio delle sessioni SIP.
  • Avvio e ricezione di chiamate.
  • Registrazione e annullamento della registrazione presso un provider SIP.
  • Verifica della connettività della sessione in corso...

Per creare un'istanza di un nuovo SipManager:

Kotlin

val sipManager: SipManager? by lazy(LazyThreadSafetyMode.NONE) {
    SipManager.newInstance(this)
}

Java

public SipManager sipManager = null;
...
if (sipManager == null) {
    sipManager = SipManager.newInstance(this);
}

Registrazione con un server SIP in corso...

Una tipica applicazione SIP di Android coinvolge uno o più utenti, ognuno dei quali dispone di un account SIP. In un'applicazione SIP Android, ogni account SIP è rappresentato da un oggetto SipProfile.

Un SipProfile definisce un profilo SIP, che include un account SIP e informazioni su dominio e server. Il profilo associato all'account SIP sul dispositivo che esegue l'applicazione è chiamato profilo locale. Il profilo a cui è connessa la sessione è chiamato profilo peer. Quando l'applicazione SIP accede al server SIP con l'indirizzo SipProfile locale, il dispositivo viene registrato di fatto come posizione a cui inviare le chiamate SIP per l'indirizzo SIP.

Questa sezione mostra come creare un SipProfile, registrarlo con un server SIP e tenere traccia degli eventi di registrazione.

Per creare un oggetto SipProfile:

Kotlin

private var sipProfile: SipProfile? = null
...

val builder = SipProfile.Builder(username, domain)
        .setPassword(password)
sipProfile = builder.build()

Java

public SipProfile sipProfile = null;
...

SipProfile.Builder builder = new SipProfile.Builder(username, domain);
builder.setPassword(password);
sipProfile = builder.build();

Il seguente estratto di codice apre il profilo locale per l'esecuzione di chiamate e/o la ricezione di chiamate SIP generiche. Il chiamante può effettuare chiamate successive tramite mSipManager.makeAudioCall. Questo estratto imposta anche l'azione android.SipDemo.INCOMING_CALL, che verrà utilizzata da un filtro per intent quando il dispositivo riceve una chiamata (consulta la sezione Configurare un filtro per intent per ricevere chiamate). Questo è il passaggio per la registrazione:

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)

Java

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

Infine, questo codice imposta un SipRegistrationListener su SipManager. Questo ti consente di verificare se SipProfile è stato registrato correttamente con il tuo fornitore di servizi 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.")
    }
})

Java

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.");
    }
}

Quando l'applicazione ha finito di utilizzare un profilo, dovrebbe chiuderla per liberare gli oggetti associati in memoria e annullare la registrazione del dispositivo dal server. Ad esempio:

Kotlin

fun closeLocalProfile() {
    try {
        sipManager?.close(sipProfile?.uriString)
    } catch (ee: Exception) {
        Log.d("WalkieTalkieActivity/onDestroy", "Failed to close local profile.", ee)
    }
}

Java

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

Effettuare una chiamata audio

Per effettuare una chiamata audio, devi disporre di quanto segue:

  • Un SipProfile che sta effettuando la chiamata (il "profilo locale") e un indirizzo SIP valido per ricevere la chiamata (il "profilo peer").
  • Un oggetto SipManager.

Per effettuare una chiamata audio devi configurare un SipAudioCall.Listener. Gran parte dell'interazione del client con lo stack SIP avviene attraverso i listener. In questo snippet, puoi vedere come viene eseguita la configurazione di SipAudioCall.Listener dopo che la chiamata è stabilita:

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

Java

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

Dopo aver configurato SipAudioCall.Listener, puoi effettuare la chiamata. Il metodo SipManager makeAudioCall accetta i seguenti parametri:

  • Un profilo SIP locale (il chiamante).
  • Un profilo SIP peer (l'utente chiamato).
  • Un SipAudioCall.Listener per ascoltare gli eventi di chiamata di SipAudioCall. Può essere null, ma come mostrato sopra, il listener viene utilizzato per configurare gli elementi una volta che la chiamata è stata stabilita.
  • Il valore di timeout, in secondi.

Ecco alcuni esempi:

Kotlin

val call: SipAudioCall? = sipManager?.makeAudioCall(
        sipProfile?.uriString,
        sipAddress,
        listener,
        30
)

Java

call = sipManager.makeAudioCall(sipProfile.getUriString(), sipAddress, listener, 30);

Ricevere chiamate

Per ricevere chiamate, un'applicazione SIP deve includere una sottoclasse BroadcastReceiver in grado di rispondere a un intent che indica che c'è una chiamata in arrivo. Pertanto, nella tua applicazione devi eseguire le seguenti operazioni:

  • In AndroidManifest.xml, dichiara un <receiver>. In SipDemo, è <receiver android:name=".IncomingCallReceiver" android:label="Call Receiver" />.
  • Implementa il ricevitore, che è una sottoclasse di BroadcastReceiver. In SipDemo, è IncomingCallReceiver.
  • Inizializza il profilo locale (SipProfile) con un intent in attesa che attivi il tuo ricevitore quando qualcuno chiama il profilo locale.
  • Imposta un filtro per intent che filtri in base all'azione che rappresenta una chiamata in arrivo. In SipDemo, questa azione è android.SipDemo.INCOMING_CALL.

Sottoclasse di BroadcastRicevir

Per ricevere chiamate, l'applicazione SIP deve avere una sottoclasse BroadcastReceiver. Il sistema Android gestisce le chiamate SIP in arrivo e trasmette un intent di "chiamata in arrivo" (come definito dall'applicazione) quando riceve una chiamata. Ecco il codice BroadcastReceiver sottoclasse dell'esempio 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()
            }
        }
    }
}

Java

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

Configurare un filtro per intent per ricevere chiamate

Quando il servizio SIP riceve una nuova chiamata, invia un intent con la stringa di azione fornita dall'applicazione. In SipDemo, questa stringa di azione è android.SipDemo.INCOMING_CALL.

Questo estratto di codice da SipDemo mostra come l'oggetto SipProfile viene creato con un intent in sospeso basato sulla stringa di azione android.SipDemo.INCOMING_CALL. L'oggetto PendingIntent eseguirà una trasmissione quando SipProfile riceve una chiamata:

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)

Java

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

La trasmissione verrà intercettata dal filtro per intent, che a sua volta attiverà il ricevitore (IncomingCallReceiver). Puoi specificare un filtro per intent nel file manifest dell'applicazione o farlo nel codice come nel metodo onCreate() dell'applicazione di esempio SipDemo del Activity dell'applicazione:

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

Java

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

Test delle applicazioni SIP in corso...

Per testare le applicazioni SIP, devi disporre di:

  • Un dispositivo mobile con Android 2.3 o versioni successive. SIP funziona in modalità wireless, quindi devi eseguire il test su un dispositivo reale. I test sulla durata di visualizzazione media non funzioneranno.
  • Un account SIP. Esistono diversi provider SIP che offrono account SIP.
  • Se stai effettuando una chiamata, anche questa deve essere relativa a un account SIP valido.

Per testare un'applicazione SIP:

  1. Sul dispositivo, collegati alla rete wireless (Impostazioni > Wireless e reti > Wi-Fi > Impostazioni Wi-Fi).
  2. Configura il dispositivo mobile per i test, come descritto nella sezione Sviluppo su un dispositivo.
  3. Esegui l'applicazione sul tuo dispositivo mobile, come descritto nella sezione Sviluppo su un dispositivo.
  4. Se utilizzi Android Studio, puoi visualizzare l'output del log dell'applicazione aprendo la console Log eventi (Visualizza > Finestra degli strumenti > Log eventi).
  5. Assicurati che l'applicazione sia configurata per avviare automaticamente Logcat quando viene eseguito:
    1. Seleziona Esegui > Modifica configurazioni.
    2. Seleziona la scheda Varie nella finestra Configurazioni di esecuzione/debug.
    3. In Logcat, seleziona Mostra logcat automaticamente, quindi seleziona OK.