Compila tu jerarquía de contenido

Android Auto y el SO Android Automotive (AAOS) llaman al servicio de navegador multimedia de tu app para descubrir qué contenido está disponible. Para ello, debes implementar estos dos métodos en tu servicio de navegador multimedia.

Implementa onGetRoot

El método onGetRoot de tu servicio muestra información sobre el nodo raíz de tu jerarquía de contenido. Android Auto y AAOS usan este nodo raíz para solicitar el resto del contenido mediante el onLoadChildren método. Este fragmento de código muestra una implementación del método onGetRoot:

Kotlin

override fun onGetRoot(
    clientPackageName: String,
    clientUid: Int,
    rootHints: Bundle?
): BrowserRoot? =
    // Verify that the specified package is allowed to access your
    // content. You'll need to write your own logic to do this.
    if (!isValid(clientPackageName, clientUid)) {
        // If the request comes from an untrusted package, return null.
        // No further calls will be made to other media browsing methods.

        null
    } else MediaBrowserServiceCompat.BrowserRoot(MY_MEDIA_ROOT_ID, null)

Java

@Override
public BrowserRoot onGetRoot(String clientPackageName, int clientUid,
    Bundle rootHints) {

    // Verify that the specified package is allowed to access your
    // content. You'll need to write your own logic to do this.
    if (!isValid(clientPackageName, clientUid)) {
        // If the request comes from an untrusted package, return null.
        // No further calls will be made to other media browsing methods.

        return null;
    }

    return new MediaBrowserServiceCompat.BrowserRoot(MY_MEDIA_ROOT_ID, null);
}

Para obtener un ejemplo detallado de este método, consulta onGetRoot en la app de ejemplo Universal Android Music Player en GitHub.

Agrega validación de paquetes

Cuando se realiza una llamada al método onGetRoot de tu servicio, el paquete de llamadas pasa información de identificación a tu servicio. Tu servicio puede usar esta información para decidir si ese paquete puede acceder a tu contenido.

Por ejemplo, puedes restringir el acceso al contenido de tu app a una lista de paquetes aprobados:

  • Compara el clientPackageName con tu lista de entidades permitidas.
  • Verifica el certificado que se usó para firmar el APK del paquete.

Si no se puede verificar el paquete, muestra null para denegar el acceso a tu contenido.

Para proporcionar a las apps del sistema, como Android Auto y AAOS, acceso a tu contenido, tu servicio debe mostrar un BrowserRoot que no sea nulo cuando estas apps del sistema llamen al método onGetRoot.

La firma de la app del sistema AAOS varía según la marca y el modelo de un automóvil. Asegúrate de permitir las conexiones de todas las apps del sistema para admitir AAOS.

Este fragmento de código muestra cómo tu servicio puede validar que el paquete de llamadas es una app del sistema:

fun isKnownCaller(
    callingPackage: String,
    callingUid: Int
): Boolean {
    ...
    val isCallerKnown = when {
       // If the system is making the call, allow it.
       callingUid == Process.SYSTEM_UID -> true
       // If the app was signed by the same certificate as the platform
       // itself, also allow it.
       callerSignature == platformSignature -> true
       // ... more cases
    }
    return isCallerKnown
}

Este fragmento de código es un extracto de la PackageValidator clase en la app de ejemplo Universal Android Music Player en GitHub. Consulta esa clase para obtener un ejemplo más detallado de cómo implementar la validación de paquetes para el método onGetRoot de tu servicio.

Además de permitir las apps del sistema, debes permitir que el Asistente de Google se conecte a tu MediaBrowserService. El Asistente de Google usa diferentes nombres de paquetes para sus apps para dispositivos móviles y AAOS.

Implementa onLoadChildren

Después de recibir el objeto del nodo raíz, Android Auto y AAOS compilan un menú de nivel superior llamando a onLoadChildren en el objeto de nodo raíz para obtener sus descendientes. Las apps cliente compilan submenús llamando al mismo método con los objetos del nodo descendiente.

Cada nodo de tu jerarquía de contenido está representado por un MediaBrowserCompat.MediaItem objeto. Cada uno de estos elementos multimedia se identifica con una cadena de ID única. Las apps cliente tratan estas cadenas de ID como tokens opacos.

Cuando una app cliente quiere navegar a un submenú o reproducir un elemento multimedia, pasa el token. Tu app es responsable de asociar el token con el elemento multimedia adecuado.

Este fragmento de código muestra una implementación de onLoadChildren.

Kotlin

override fun onLoadChildren(
    parentMediaId: String,
    result: Result<List<MediaBrowserCompat.MediaItem>>
) {
    // Assume for example that the music catalog is already loaded/cached.

    val mediaItems: MutableList&lt;MediaBrowserCompat.MediaItem> = mutableListOf()

    // Check if this is the root menu:
    if (MY_MEDIA_ROOT_ID == parentMediaId) {

        // Build the MediaItem objects for the top level
        // and put them in the mediaItems list.
    } else {

        // Examine the passed parentMediaId to see which submenu we're at
        // and put the descendants of that menu in the mediaItems list.
    }
    result.sendResult(mediaItems)
}

Java

@Override
public void onLoadChildren(final String parentMediaId,
    final Result&lt;List&lt;MediaBrowserCompat.MediaItem>> result) {

    // Assume for example that the music catalog is already loaded/cached.

    List&lt;MediaBrowserCompat.MediaItem> mediaItems = new ArrayList&lt;>();

    // Check if this is the root menu:
    if (MY_MEDIA_ROOT_ID.equals(parentMediaId)) {

        // Build the MediaItem objects for the top level
        // and put them in the mediaItems list.
    } else {

        // Examine the passed parentMediaId to see which submenu we're at
        // and put the descendants of that menu in the mediaItems list.
    }
    result.sendResult(mediaItems);
}

Para ver un ejemplo de este método, consulta onLoadChildren en la app de ejemplo Universal Android Music Player en GitHub.

Estructura el menú raíz

Android Auto y el SO Android Automotive tienen restricciones específicas sobre la estructura del menú raíz. Se comunican con MediaBrowserService a través de sugerencias de raíz, que se pueden leer con el argumento Bundle que se pasa a onGetRoot(). Cuando se siguen, estas sugerencias permiten que el sistema muestre el contenido raíz como pestañas de navegación. Si no sigues estas sugerencias, es posible que el sistema quite parte del contenido raíz o que sea menos fácil de descubrir.

El contenido raíz se muestra como pestañas de navegación.

Figura 1: Contenido raíz que se muestra como pestañas de navegación

Si aplicas estas sugerencias, el sistema mostrará el contenido raíz como pestañas de navegación. Si no aplicas estas sugerencias, es posible que se quite parte del contenido raíz o que sea menos fácil de descubrir. Estas sugerencias se transmiten de la siguiente manera:

Kotlin

import androidx.media.utils.MediaConstants

override fun onGetRoot(
    clientPackageName: String,
    clientUid: Int,
    rootHints: Bundle
): BrowserRoot {

  val maximumRootChildLimit = rootHints.getInt(
      MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT,
      /* defaultValue= */ 4)
  val supportedRootChildFlags = rootHints.getInt(
      MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS,
      /* defaultValue= */ MediaItem.FLAG_BROWSABLE)

  // Rest of method...
}

Java

import androidx.media.utils.MediaConstants;

// Later, in your MediaBrowserServiceCompat.
@Override
public BrowserRoot onGetRoot(
    String clientPackageName, int clientUid, Bundle rootHints) {

    int maximumRootChildLimit = rootHints.getInt(
        MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT,
        /* defaultValue= */ 4);
    int supportedRootChildFlags = rootHints.getInt(
        MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS,
        /* defaultValue= */ MediaItem.FLAG_BROWSABLE);

    // Rest of method...
}

Puedes ramificar la lógica de la estructura de tu jerarquía de contenido en función de los valores de estas sugerencias, especialmente si tu jerarquía varía entre las integraciones de MediaBrowser de fuera de Android Auto y AAOS.

Por ejemplo, si sueles mostrar un elemento raíz reproducible, tal vez sea preferible que lo anides en un elemento raíz explorable, debido al valor de la sugerencia de marcas compatible.

Además de las sugerencias de raíz, usa estos lineamientos para renderizar pestañas de forma óptima:

  • Íconos monocromáticos (preferentemente blancos) para cada elemento de pestaña

  • Etiquetas cortas y significativas para cada elemento de pestaña (las etiquetas cortas reducen las probabilidades de que se trunquen)