Para implementar una aplicación de interacción por voz (VIA), completa estos pasos:
- Crea un esqueleto de VIA.
- (Opcional) Implementa un flujo de configuración o acceso.
- (Opcional) Implementa una pantalla de configuración.
- Declara los permisos necesarios en el archivo de manifiesto.
- Implementa una IU de placa de voz.
- Implementa el reconocimiento de voz (debe incluir la implementación de la API de RecognitionService).
- Implementa la expresión (opcionalmente, puedes implementar la API de TextToSpeech).
- Implementa la entrega de comandos. Consulta este contenido en Cómo entregar comandos.
En las siguientes secciones, se describe cómo completar cada paso mencionado anteriormente.
Crea un esqueleto de VIA
Manifiestos
Una app se detecta como una con interacción por voz cuando se incluye lo siguiente en el manifiesto:
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myvoicecontrol"> ... <application ... > <service android:name=".MyInteractionService" android:label="@string/app_name" android:permission="android.permission.BIND_VOICE_INTERACTION" android:process=":interactor"> <meta-data android:name="android.voice_interaction" android:resource="@xml/interaction_service" /> <intent-filter> <action android:name= "android.service.voice.VoiceInteractionService" /> </intent-filter> </service> </application> </manifest>
En este ejemplo:
- Las VIAs deben exponer un servicio que extienda
VoiceInteractionService, con un filtro de intents para la acciónVoiceInteractionService.SERVICE_INTERFACE ("android.service.voice.VoiceInteractionService"). - Este servicio debe tener el permiso de firma del sistema
BIND_VOICE_INTERACTION. - Este servicio debe incluir un archivo de metadatos
android.voice_interactionpara contener lo siguiente:res/xml/interaction_service.xml
<voice-interaction-service xmlns:android="http://schemas.android.com/apk/res/android" android:sessionService= "com.example.MyInteractionSessionService" android:recognitionService= "com.example.MyRecognitionService" android:settingsActivity= "com.example.MySettingsActivity" android:supportsAssist="true" android:supportsLaunchVoiceAssistFromKeyguard="true" android:supportsLocalInteraction="true" />
Para obtener detalles sobre cada campo, consulta R.styleable#VoiceInteractionService.
Dado que todas las VIAs también son servicios de reconocimiento de voz, también debes incluir lo siguiente en tu manifiesto:
AndroidManifest.xml
<manifest ...> <uses-permission android:name="android.permission.RECORD_AUDIO"/> <application ...> ... <service android:name=".RecognitionService" ...> <intent-filter> <action android:name="android.speech.RecognitionService" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> <meta-data android:name="android.speech" android:resource="@xml/recognition_service" /> </service> </application> </manifest>
Los servicios de reconocimiento de voz también requieren la siguiente pieza de metadatos:
res/xml/recognition_service.xml
<recognition-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:settingsActivity="com.example.MyRecognizerSettingsActivity" />VoiceInteractionService, VoiceInteractionSessionService y VoiceInteractionSession
En el siguiente diagrama, se muestra el ciclo de vida de cada una de estas entidades:

Figura 1: Lifecycles
Como se indicó anteriormente, VoiceInteractionService es el punto de entrada a una VIA. Las principales responsabilidades de este servicio son las siguientes:
- Inicializar cualquier proceso que deba mantenerse en ejecución mientras esta VIA esté activa (por ejemplo, la detección de palabras clave)
- Informar las acciones de voz admitidas (consulta Tocar para leer del Asistente)
- Iniciar sesiones de interacción por voz desde la pantalla de bloqueo (keyguard)
En su forma más sencilla, una implementación de VoiceInteractionService se vería de la siguiente manera:
public class MyVoiceInteractionService extends VoiceInteractionService { private static final List<String> SUPPORTED_VOICE_ACTIONS = Arrays.asList( CarVoiceInteractionSession.VOICE_ACTION_READ_NOTIFICATION, CarVoiceInteractionSession.VOICE_ACTION_REPLY_NOTIFICATION, CarVoiceInteractionSession.VOICE_ACTION_HANDLE_EXCEPTION ); @Override public void onReady() { super.onReady(); // TODO: Setup hotword detector } @NonNull @Override public Set<String> onGetSupportedVoiceActions( @NonNull Set<String> voiceActions) { Set<String> result = new HashSet<>(voiceActions); result.retainAll(SUPPORTED_VOICE_ACTIONS); return result; } ... }
Se requiere la implementación de VoiceInteractionService#onGetSupportedVoiceActions() para controlar la función Tocar para leer del Asistente.
El sistema usa un VoiceInteractionSessionService para crear un VoiceInteractionSession y para interactuar con él. Solo tiene una responsabilidad: iniciar sesiones nuevas cuando se solicitan.
public class MyVoiceInteractionSessionService extends VoiceInteractionSessionService { @Override public VoiceInteractionSession onNewSession(Bundle args) { return new MyVoiceInteractionSession(this); } }
Por último, en VoiceInteractionSession se realizaría la mayor parte del trabajo. Se puede volver a usar una sola instancia de sesión para completar varias interacciones del usuario. En AAOS, existe un objeto auxiliar CarVoiceInteractionSession que ayuda a implementar algunas de las funcionalidades únicas de Automotive.
public class MyVoiceInteractionSession extends CarVoiceInteractionSession { public InteractionSession(Context context) { super(context); } @Override protected void onShow(String action, Bundle args, int showFlags) { closeSystemDialogs(); // TODO: Unhide UI and update UI state // TODO: Start processing audio input } ... }
VoiceInteractionSession tiene un gran conjunto de métodos de devolución de llamada que se
explican en las siguientes secciones. Consulta la documentación de VoiceInteractionSession para obtener una lista completa.
Implementa un flujo de configuración o acceso
La configuración y el acceso pueden ocurrir en los siguientes casos:
- Durante la integración del dispositivo (Asistente de configuración)
- Durante el intercambio de servicios de interacción por voz (Configuración)
- En el primer inicio cuando se selecciona la app
Para obtener detalles sobre la experiencia del usuario recomendada y la guía visual, consulta Asistentes precargados: Guía de UX.
Configuración durante el intercambio de servicios de voz
Siempre es posible que el usuario seleccione una VIA que no se haya configurado correctamente. Esto puede ocurrir por alguno de los siguientes motivos:
- El usuario omitió por completo el Asistente de configuración o el paso de configuración de la interacción por voz.
- El usuario seleccionó una VIA diferente de la que se configuró durante la integración del dispositivo.
En cualquier caso, un VoiceInteractionService tiene varias formas de alentar al usuario a completar la configuración:
- Recordatorio de notificación
- Respuesta de voz automática cuando el usuario intenta usarla
Nota: No se recomienda presentar un flujo de configuración de VIA sin una solicitud explícita del usuario. Esto significa que las VIAs deben evitar mostrar contenido automáticamente en la HU durante el arranque del dispositivo o como resultado de un cambio o desbloqueo del usuario.
Recordatorio de notificación
Un recordatorio de notificación es una forma no intrusiva de indicar la necesidad de configuración y de proporcionar a los usuarios una indicación visual para navegar al flujo de configuración del asistente.

Figura 2: Recordatorio de notificación
Así es como funcionaría este flujo:

Figura 3: Flujo de recordatorio de notificación
Respuesta de voz
Este es el flujo más simple para implementar, iniciar una expresión en una devolución de llamada VoiceInteractionSession#onShow(), explicarle al usuario lo que se debe hacer y, luego, preguntarle (si se permite la configuración dado el estado de restricción de UX) si desea iniciar el flujo de configuración. Si la configuración no es posible en ese momento, también explica esta situación.
Configuración en el primer uso
Siempre es posible que el usuario active una VIA que no se haya configurado correctamente. En esos casos:
- Informa verbalmente al usuario sobre esta situación (por ejemplo, "Para que funcione correctamente, debes completar algunos pasos…").
- Si el motor de restricciones de UX lo permite (consulta UX_RESTRICTIONS_NO_SETUP), pregúntale al usuario si desea iniciar el proceso de configuración y, luego, abre la pantalla de configuración de la VIA.
- De lo contrario (por ejemplo, si el usuario está conduciendo), deja una notificación para que el usuario haga clic en la opción cuando sea seguro hacerlo.
Crea pantallas de configuración de interacción por voz
Las pantallas de configuración y acceso deben desarrollarse como actividades normales. Consulta los lineamientos visuales y de UX para el desarrollo de la IU en Asistentes precargados: Guía de UX.
Lineamientos generales:
- Las VIAs deben permitir que los usuarios interrumpan y reanuden la configuración en cualquier momento.
- No se debe permitir la configuración si la restricción
UX_RESTRICTIONS_NO_SETUPestá vigente. Para obtener más información, consulta Lineamientos sobre la distracción del conductor. - Las pantallas de configuración deben coincidir con el sistema de diseño de cada vehículo. El diseño general de la pantalla, los íconos, los colores y otros aspectos deben ser coherentes con el resto de la IU. Consulta Personalización para obtener más información.
Implementa una pantalla de configuración

Figura 4: Integración de la configuración
Las pantallas de configuración son actividades normales de Android. Si se implementan, su punto de entrada
debe declararse en el res/xml/interaction_service.xml como parte de los manifiestos de VIA (consulta
Manifiestos).
La sección Configuración es un buen lugar para continuar con la configuración y el acceso (si el usuario no lo completó) o para ofrecer una opción de salir o cambiar de usuario si es necesario. Al igual que las pantallas de configuración descritas anteriormente, estas pantallas deben hacer lo siguiente:
- Proporcionar la opción de salir a la pantalla anterior en la pila de pantallas (por ejemplo, a la configuración del vehículo)
- No se permite mientras se conduce. Para obtener más información, consulta Lineamientos sobre la distracción del conductor.
- Coincidir con cada sistema de diseño del vehículo (para obtener más información, consulta Personalización)
Declara los permisos necesarios en el archivo de manifiesto
Los permisos que requiere una VIA se pueden dividir en tres categorías:
- Permisos de firma del sistema : Son permisos que solo se otorgan a los APKs preinstalados y firmados por el sistema. Los usuarios no pueden otorgar estos permisos, solo los OEMs pueden otorgarlos cuando compilan sus imágenes del sistema. Para obtener más información sobre cómo obtener permisos de firma, consulta Cómo otorgar permisos con privilegios del sistema.
- Permisos peligrosos : Son permisos que un usuario debe otorgar con el diálogo PermissionsController. Los OEMs pueden otorgar previamente algunos de estos permisos al VoiceInteractionService predeterminado. Sin embargo, dado que este valor predeterminado puede cambiar de un dispositivo a otro, las apps deben poder solicitar estos permisos cuando sea necesario.
- Otros permisos : Son todos los demás permisos que no requieren la intervención del usuario. El sistema otorga estos permisos automáticamente.
Teniendo en cuenta lo anterior, la siguiente sección se centra solo en solicitar permisos peligrosos. Los permisos solo se deben solicitar mientras el usuario se encuentre en las pantallas de acceso o configuración.
Si la app no tiene los permisos necesarios para funcionar, el flujo recomendado es usar una expresión de voz para explicar la situación al usuario y una notificación para proporcionar una indicación visual que el usuario pueda usar para volver a las pantallas de configuración de la VIA. Para obtener más información, consulta 1. Recordatorio de notificación.
Solicita permisos como parte de la pantalla de configuración
Los permisos peligrosos se solicitan con el método ActivityCompat#requestPermission() normal (o equivalente). Para obtener detalles sobre cómo solicitar permisos, consulta
Cómo solicitar permisos de la app.

Figura 5: Solicita permisos
Permiso de objeto de escucha de notificaciones
Para implementar el flujo de TTR, las VIAs deben designarse como un objeto de escucha de notificaciones. Esto no es un permiso en sí, sino una configuración que permite que el sistema envíe notificaciones a los objetos de escucha registrados. Para saber si se le otorgó acceso a la VIA a esta información, las apps pueden hacer lo siguiente:
- (Opcional) Verifica si hay objetos de escucha de notificaciones con anticipación usando
CarAssistUtils#assistantIsNotificationListener(). Esto se podría hacer, por ejemplo, durante el flujo de configuración. - (Obligatorio) Reacciona al control de
CarVoiceInteractionSession#onShow()con la acciónVOICE_ACTION_HANDLE_EXCEPTIONy la excepciónEXCEPTION_NOTIFICATION_LISTENER_PERMISSIONS_MISSING.
Si este acceso no se otorga previamente, la VIA debe dirigir al usuario a la sección Acceso a las notificaciones de la configuración del vehículo, usando una combinación de expresiones y notificaciones. Se puede usar el siguiente código para abrir la sección adecuada de la app de configuración:
private void requestNotificationListenerAccess() {
Intent intent = new Intent(Settings
.ACTION_NOTIFICATION_LISTENER_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
startActivity(intent);
}Implementa una IU de placa de voz
Cuando un VoiceInteractionSession recibe una devolución de llamada onShow(), puede presentar una IU de placa de voz. Para obtener lineamientos visuales y de UX sobre la implementación de la placa de voz,consulta
Asistentes precargados: Guía de UX.

Figura 6: Cómo mostrar la placa de voz
Existen dos opciones para implementar esta IU:
- Anular
VoiceInteractionSession#onCreateContentView() - Iniciar una actividad con
VoiceInteractionSession#startAssistantActivity()
Usa onCreateContentView()
Esta es la forma predeterminada de presentar una placa de voz. La clase base VoiceInteractionSession crea una ventana y administra su ciclo de vida mientras una sesión de voz esté activa. Las apps deben anular VoiceInteractionSession#onCreateContentView() y mostrar una vista adjunta a esa ventana en cuanto se cree la sesión. Esta vista debe ser invisible inicialmente. Cuando se inicia una interacción por voz, esta vista debe hacerse visible en VoiceInteractionSession#onShow() y, luego, volver a ser invisible en VoiceInteractionSession#onHide().
public class MyVoiceInteractionSession extends CarVoiceInteractionSession { private View mVoicePlate; … @Override public View onCreateContentView() { mVoicePlate = inflater.inflate(R.layout.voice_plate, null); … } @Override protected void onShow(String action, Bundle args, int showFlags) { // TODO: Update UI state to "listening" mVoicePlate.setVisibility(View.VISIBLE); } @Override public void onHide() { mVoicePlate.setVisibility(View.GONE); } … }
Cuando usas este método, es posible que desees ajustar VoiceInteractionSession#onComputeInsets()
para tener en cuenta las regiones ocultas de tu IU.
Usa startAssistantActivity()
En este caso, VoiceInteractionSession delega el control de la IU de la placa de voz a una actividad normal. Cuando se usa esta opción, una VoiceInteractionSession
implementación debe inhabilitar la creación de su ventana de contenido predeterminada (consulta Cómo usar onCreateContentView()) en la devolución de llamada onPrepareShow(). En VoiceInteractionSession#onShow(), la sesión iniciará la actividad de la placa de voz con VoiceInteractionSession#startAssistantActivity(). Este método inicia la IU con la configuración de ventana y los indicadores de actividad adecuados.
public class MyVoiceInteractionSession extends CarVoiceInteractionSession { … @Override public void onPrepareShow(Bundle args, int showFlags) { super.onPrepareShow(args, showFlags); setUiEnabled(false); } @Override protected void onShow(String action, Bundle args, int showFlags) { closeSystemDialogs(); Intent intent = new Intent(getContext(), VoicePlateActivity.class); intent.putExtra(VoicePlateActivity.EXTRA_ACTION, action); intent.putExtra(VoicePlateActivity.EXTRA_ARGS, args); startAssistantActivity(intent); } … }
Para mantener una comunicación entre esta actividad y la VoiceInteractionSession, es posible que se requiera un conjunto de intents internos o vinculación de servicios. Por ejemplo, cuando se invoca VoiceInteractionSession#onHide(), la sesión debe poder pasar esta solicitud a la actividad.
Importante: En Automotive, solo se pueden mostrar mientras se conduce las actividades con anotaciones especiales o las actividades que aparecen en la "lista de entidades permitidas" de UXR. Esto también se aplica a las actividades que se inician con VoiceInteractionSession#startAssistantActivity(). Recuerda
anotar tu actividad con <meta-data
android:name="distractionOptimized" android:value="true"/> o incluir esta
actividad en la clave systemActivityWhitelist del archivo /packages/services/Car/service/res/values/config.xml. Para obtener más información, consulta Lineamientos
sobre la distracción del conductor.
Implementa el reconocimiento de voz
En esta sección, aprenderás a implementar el reconocimiento de voz a través de la detección y el reconocimiento de palabras clave. Una palabra clave es una palabra de activación que se usa para iniciar una nueva consulta o acción por voz. Por ejemplo, "OK Google" o "Hey Google".
Detección de palabras clave de DSP
Android proporciona acceso a un detector de palabras clave siempre activo en el nivel de DSP a través de
AlwaysOnHotwordDetector.
forma de implementar la detección de palabras clave con CPU baja. El uso de esta funcionalidad se divide en dos partes:
- Creación de instancias de un
AlwaysOnHotwordDetector - Inscripción de un modelo de sonido de detección de palabras clave
La implementación de VoiceInteractionService puede crear un detector de palabras clave con
VoiceInteractionService#createAlwaysOnHotwordDetector(),
pasando una frase clave y una configuración regional que deseen usar para la detección. Como resultado, la
app recibe una onAvailabilityChanged()
devolución de llamada con uno de los siguientes valores posibles:
STATE_HARDWARE_UNAVAILABLE: La capacidad de DSP no está disponible en el dispositivo. En este caso, se usa la detección de palabras clave de software.STATE_HARDWARE_UNSUPPORTED: La compatibilidad con DSP no está disponible en general, pero DSP no admite la combinación de frase clave y configuración regional determinada. La app puede optar por usar la detección de palabras clave de software.STATE_HARDWARE_ENROLLED: La detección de palabras clave está lista y se puede iniciar llamando al métodostartRecognition().STATE_HARDWARE_UNENROLLED: No hay un modelo de sonido disponible para la frase clave solicitada, pero es posible la inscripción.
La inscripción de modelos de sonido de detección de palabras clave se puede realizar con IVoiceInteractionManagerService#updateKeyphraseSoundModel().
Se pueden registrar varios modelos en el sistema en un momento determinado, pero solo uno
modelo está asociado con un AlwaysOnHotwordDetector.
Es posible que la detección de palabras clave de DSP no esté disponible en todos los dispositivos. Los desarrolladores de VIA
deben verificar las capacidades de hardware con el método getDspModuleProperties(). Para obtener un código de muestra que muestre
cómo inscribir modelos de sonido, consulta VoiceEnrollment/src/com/android/test/voiceenrollment/EnrollmentUtil.java.
Consulta Captura simultánea en relación con
el reconocimiento simultáneo de palabras clave.
Detección de palabras clave de software
Como se indicó anteriormente, es posible que la detección de palabras clave de DSP no esté disponible en todos los dispositivos (por ejemplo, el emulador de Android no proporciona emulación de DSP). En este caso, el reconocimiento de voz de software es la única alternativa. Para evitar interferir con otras apps que podrían necesitar acceso al micrófono, las VIAs deben acceder a la entrada de audio con lo siguiente:
- La captura de audio debe usar MediaRecorder.AudioSource.HOTWORD.
- Mantener el permiso
android.Manifest.permission.CAPTURE_AUDIO_HOTWORD
Ambas constantes son @hide y solo están disponibles para las apps agrupadas.
Administra la entrada de audio y el reconocimiento de voz
La entrada de audio se implementaría con la clase MediaRecorder.
Para obtener más información sobre cómo usar esta API, consulta la Descripción general
de MediaRecorder. También se espera que los servicios de interacción por voz sean RecognitionService
implementaciones de clase. Cualquier app del sistema que requiera reconocimiento de voz usa para acceder a esta capacidad. Para realizar el reconocimiento de voz y tener acceso al micrófono, las VIAs
deben tener android.permission.RECORD_AUDIO.
También se espera que las apps que acceden a una implementación de RecognitionService
tengan este permiso.
Antes de Android 10, el acceso al micrófono se otorgaba a una sola app a la vez (con la excepción de la detección de palabras clave, consulta más arriba). A partir de Android 10, se puede compartir el acceso al micrófono. Para obtener más información, consulta Cómo compartir entradas de audio.
Accede a la salida de audio
Cuando la VIA esté lista para proporcionar respuestas verbales, es importante seguir este siguiente conjunto de lineamientos:
- Cuando solicites el foco de audio o administres la salida de audio, la app
debe usar
AudioAttributes#USAGE_ASSISTANTyAudioAttributes#CONTENT_TYPE_SPEECHcomo atributos de audio. - Durante el reconocimiento de voz, se debe solicitar el foco de audio con
AudioManage#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE. Ten en cuenta que es posible que algunas apps de contenido multimedia no reaccionen correctamente a los comandos de contenido multimedia (consulta Cómo entregar comandos de contenido multimedia) mientras se quita su foco de audio.