O Android oferece uma API compatível com o Protocolo de início de sessão (SIP, na sigla em inglês). Isso permite que você adicione recursos de telefonia via Internet baseados em SIP aos seus aplicativos. O Android inclui uma pilha completa do protocolo SIP e serviços integrados de gerenciamento de chamadas que permitem que os aplicativos configurem facilmente chamadas de voz recebidas e efetuadas, sem precisar gerenciar sessões, comunicação no nível de transporte, gravação ou reprodução de áudio diretamente.
Veja alguns exemplos dos tipos de aplicativos que podem usar a API SIP:
- Videoconferência
- Mensagem instantânea
Requisitos e limitações
Veja os requisitos para desenvolver um aplicativo com SIP:
- Você precisa ter um dispositivo móvel com o Android 2.3 ou versão posterior.
- O SIP é executado em uma conexão de dados sem fio. Por isso, o dispositivo precisa ter uma conexão de dados (com um serviço móvel de dados ou Wi-Fi). Isso significa que não é possível testar no AVD. Só é possível testar em um dispositivo físico. Para ver detalhes, consulte Testar aplicativos com SIP.
- Cada participante da sessão de comunicação do aplicativo precisa ter uma conta SIP. Há muitos provedores SIP diferentes que oferecem contas SIP.
Classes e interfaces da API SIP
Veja um resumo das classes e uma interface (SipRegistrationListener
) que estão incluídas na API SIP do Android:
Classe/interface | Descrição |
---|---|
SipAudioCall |
Processa uma chamada de áudio de Internet por meio do SIP. |
SipAudioCall.Listener |
Listener para eventos relacionados a uma chamada SIP, como quando uma chamada é recebida ("tocando") ou realizada ("chamando"). |
SipErrorCode |
Define os códigos de erro retornados durante ações SIP. |
SipManager |
Oferece APIs para tarefas SIP, como iniciar conexões SIP, e fornece acesso a serviços SIP relacionados. |
SipProfile |
Define um perfil SIP, incluindo uma conta SIP e informações de domínio e servidor. |
SipProfile.Builder |
Classe auxiliar para a criação de um SipProfile. |
SipSession |
Representa uma sessão SIP associada a uma caixa de diálogo do SIP ou uma transação independente que não está em uma caixa de diálogo. |
SipSession.Listener |
Listener para eventos relacionados a uma sessão SIP, como quando uma sessão é registrada ("registrando") ou quando uma chamada é realizada ("chamando"). |
SipSession.State |
Define os estados de sessões SIP, como "registrando", "chamada realizada" e "em chamada". |
SipRegistrationListener |
Uma interface que é um listener para eventos de registro SIP. |
Criar um manifesto
Se você está desenvolvendo um aplicativo que usa a API SIP, lembre-se de que esse recurso só é compatível com o Android 2.3 (API nível 9) e versões posteriores da plataforma. Além disso, nem todos os dispositivos com o Android 2.3 (API nível 9) ou posterior oferecem compatibilidade com SIP.
Para usar o SIP, adicione as seguintes permissões ao manifesto do seu aplicativo:
android.permission.USE_SIP
android.permission.INTERNET
Para garantir que seu aplicativo só possa ser instalado em dispositivos compatíveis com o SIP, adicione o seguinte ao manifesto do aplicativo:
<uses-sdk android:minSdkVersion="9" />
Isso indica que o aplicativo requer o Android 2.3 ou posterior. Para mais informações, consulte Níveis de APIs e a documentação do elemento <uses-sdk>
.
Para controlar a forma como seu aplicativo é filtrado de dispositivos que não são compatíveis com SIP (no Google Play, por exemplo), adicione o seguinte ao manifesto do seu aplicativo:
<uses-feature android:name="android.hardware.sip.voip" />
Isso declara que seu aplicativo usa a API SIP. Essa declaração deve incluir um atributo android:required
que indica se você quer que o aplicativo seja filtrado de dispositivos que não oferecem compatibilidade com SIP.
Outras declarações <uses-feature>
também podem ser necessárias, dependendo da implementação. Para mais informações, consulte a documentação do elemento <uses-feature>
.
Se o aplicativo for projetado para receber chamadas, também será necessário definir um destinatário (subclasse BroadcastReceiver
) no manifesto do aplicativo:
<receiver android:name=".IncomingCallReceiver" android:label="Call Receiver" />
Veja trechos do manifesto 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.hardware.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>
Criar o SipManager
Para usar a API SIP, seu aplicativo precisa criar um objeto SipManager
. O SipManager
é responsável pelas seguintes ações no seu aplicativo:
- Iniciar sessões SIP.
- Iniciar e receber chamadas.
- Registrar e cancelar o registro com um provedor SIP.
- Verificar a conectividade da sessão.
Instancie um novo SipManager
da seguinte forma:
Kotlin
val sipManager: SipManager? by lazy(LazyThreadSafetyMode.NONE) { SipManager.newInstance(this) }
Java
public SipManager sipManager = null; ... if (sipManager == null) { sipManager = SipManager.newInstance(this); }
Como registrar-se com um servidor SIP
Um aplicativo comum com SIP para Android envolve um ou mais usuários, cada um com uma conta SIP. Em um aplicativo com SIP para Android, cada conta SIP é representada por um objeto SipProfile
.
Um SipProfile
define um perfil SIP, incluindo uma conta SIP e informações de domínio e servidor. O perfil associado à conta SIP no dispositivo que executa o aplicativo é denominado perfil local. O perfil ao qual a sessão é conectada é denominado perfil semelhante. Quando seu aplicativo SIP faz login no servidor SIP com o SipProfile
local, isso efetivamente registra o dispositivo como o local para onde enviar chamadas SIP para seu endereço SIP.
Esta seção mostra como criar um SipProfile
, registrá-lo com um servidor SIP e rastrear eventos de registro.
Crie um objeto SipProfile
da seguinte forma:
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();
O trecho de código a seguir abre o perfil local para realizar chamadas e/ou receber chamadas SIP genéricas. O autor da chamada pode realizar chamadas subsequentes por meio do mSipManager.makeAudioCall
. Esse trecho também define a ação android.SipDemo.INCOMING_CALL
, que será usada por um filtro de intent quando o dispositivo receber uma chamada. Consulte Configurar um filtro de intent para receber chamadas. Esta é a etapa de registro:
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);
Por fim, este código define um SipRegistrationListener
no SipManager
. Isso rastreia se o SipProfile
foi registrado no seu provedor de serviços 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 seu aplicativo terminar de usar um perfil, ele deverá fechá-lo para liberar objetos associados na memória e cancelar o registro do dispositivo no servidor. Por exemplo:
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); } }
Fazer uma chamada de áudio
Para fazer uma chamada de áudio, é preciso ter o seguinte:
- Um
SipProfile
que faça a chamada (o “perfil local”) e um endereço SIP válido para receber a chamada (o “perfil semelhante”). - Um objeto
SipManager
.
Para fazer uma chamada de áudio, configure um SipAudioCall.Listener
. Grande parte da interação do cliente com a pilha do SIP acontece por meio dos listeners. Neste snippet, é possível ver como o SipAudioCall.Listener
faz as configurações depois que a chamada é estabelecida:
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. } };
Depois de configurar o SipAudioCall.Listener
, você pode fazer a chamada. O método SipManager
makeAudioCall
utiliza os seguintes parâmetros:
- Um perfil SIP local (o autor da chamada).
- Um perfil SIP semelhante (o usuário que recebe a chamada).
- Um
SipAudioCall.Listener
para ouvir os eventos de chamada deSipAudioCall
. Esse parâmetro pode sernull
, mas, como mostrado acima, o listener é usado para definir as configurações quando a chamada é estabelecida. - O valor de tempo limite, em segundos.
Por exemplo:
Kotlin
val call: SipAudioCall? = sipManager?.makeAudioCall( sipProfile?.uriString, sipAddress, listener, 30 )
Java
call = sipManager.makeAudioCall(sipProfile.getUriString(), sipAddress, listener, 30);
Receber chamadas
Para receber chamadas, um aplicativo SIP precisa incluir uma subclasse de BroadcastReceiver
que possa responder a um intent indicando que há uma chamada recebida. Portanto, é preciso fazer o seguinte no seu aplicativo:
- Em
AndroidManifest.xml
, declare um<receiver>
. Em SipDemo, isso é<receiver android:name=".IncomingCallReceiver" android:label="Call Receiver" />
. - Implemente o destinatário, que é uma subclasse de
BroadcastReceiver
. Em SipDemo, isso éIncomingCallReceiver
. - Inicialize o perfil local (
SipProfile
) com um intent pendente que dispara o destinatário quando alguém faz uma chamada para o perfil local. - Configure um filtro de intent que filtre pela ação que representa uma chamada recebida. Em SipDemo, essa ação é
android.SipDemo.INCOMING_CALL
.
Criar uma subclasse de BroadcastReceiver
Para receber chamadas, seu aplicativo SIP precisa ter a subclasse BroadcastReceiver
. O sistema Android processa chamadas SIP recebidas e transmite um intent de "chamada recebida", conforme definido pelo aplicativo, quando recebe uma chamada. Veja o código
BroadcastReceiver
com subclasses da amostra 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(); } } } }
Como configurar um filtro de intent para receber chamadas
Quando o serviço SIP recebe uma nova chamada, ele envia um intent com a string de ação fornecida pelo aplicativo. No SipDemo, essa string de ação é android.SipDemo.INCOMING_CALL
.
Esse trecho de código do SipDemo mostra como o objeto SipProfile
é criado com um intent pendente baseado na string de ação android.SipDemo.INCOMING_CALL
. O objeto PendingIntent
realizará uma transmissão quando o SipProfile
receber uma chamada:
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);
A transmissão será interceptada pelo filtro de intent, que disparará o destinatário (IncomingCallReceiver
). Você pode especificar um filtro de intent no arquivo de manifesto do aplicativo ou fazer isso por meio do código, como no método onCreate()
da amostra de aplicativo SipDemo da Activity
do aplicativo:
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); ... } ... }
Testar aplicativos SIP
Para testar aplicativos SIP, você precisa do seguinte:
- Um dispositivo móvel com o Android 2.3 ou versões posteriores. O SIP é executado por meio de conexão sem fio. Por isso, é necessário testar em um dispositivo real. Não é possível realizar os testes no AVD.
- Uma conta SIP. Há muitos provedores SIP diferentes que oferecem contas SIP.
- Se você fizer uma chamada, ela também precisará ser para uma conta SIP válida.
Para testar um aplicativo SIP:
- No dispositivo, conecte-se à rede sem fio (Config. > Redes sem fio e outras > Wi-Fi > Configurações de Wi-Fi).
- Configure seu dispositivo móvel para testes, conforme descrito em Executar apps em um dispositivo de hardware.
- Execute seu aplicativo no dispositivo móvel, conforme descrito em Executar apps em um dispositivo de hardware.
- Se você está usando o Android Studio, é possível ver o resultado do registro do aplicativo abrindo o console Event Log (View > Tool Windows > Event Log).
- Verifique se o aplicativo está configurado para iniciar o Logcat automaticamente quando ele for executado:
- Selecione Run > Edit Configurations.
- Selecione a guia Miscellaneous na janela Run/Debug Configurations.
- Em Logcat, selecione Show logcat automatically e, em seguida, OK.