O Android Auto e o Android Automotive OS (AAOS) chamam o serviço de navegação de mídia do seu app para descobrir qual conteúdo está disponível. Para oferecer suporte a isso, implemente esses dois métodos no serviço de navegação de mídia.
Implementar onGetRoot
O método onGetRoot
do serviço retorna informações sobre o nó raiz
da hierarquia de conteúdo. O Android Auto e o AAOS usam esse nó raiz para solicitar o restante do seu conteúdo usando o método onLoadChildren
. Este snippet de código mostra uma implementação do 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 ver um exemplo detalhado desse método, consulte onGetRoot
no app de exemplo Universal
Android Music Player no GitHub (link em inglês).
Adicionar validação de pacote
Quando uma chamada é feita para o método onGetRoot
do seu serviço, o pacote de chamada transmite informações de identificação para o serviço. Ele pode usar essas informações para decidir se o pacote pode acessar seu conteúdo.
Por exemplo, é possível restringir o acesso ao conteúdo do app a uma lista de pacotes aprovados:
- Compare o
clientPackageName
com sua lista de permissões. - Verifique o certificado usado para assinar o APK do pacote.
Se não for possível verificar o pacote, retorne null
para negar acesso ao seu conteúdo.
Para que os apps
do sistema (como o Android Auto e o AAOS) acessem o conteúdo, seu serviço precisa retornar um valor
BrowserRoot
não nulo quando esses apps chamam o método
onGetRoot
.
A assinatura do app do sistema AAOS varia de acordo com a marca e o modelo do carro. Permita conexões de todos os apps do sistema para oferecer suporte ao AAOS.
Este snippet de código mostra como seu serviço pode validar que o pacote de chamada é um app do 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
}
Esse snippet de código é um trecho da classe PackageValidator
no
app de exemplo Universal Android Music Player no GitHub (link em inglês). Confira essa classe para um exemplo mais detalhado de como implementar a validação de pacote para o método onGetRoot
do seu serviço.
Além de permitir apps do sistema, você precisa permitir que o Google Assistente se conecte ao
MediaBrowserService
. O Google Assistente usa nomes de pacote separados
para o smartphone, que inclui o Android Auto e o Android Automotive OS (AAOS).
Implementar onLoadChildren
Depois de receber o objeto do nó raiz, o Android Auto e o AAOS
criam um menu de nível superior chamando onLoadChildren
no objeto do nó raiz
para receber os descendentes dele. Os apps clientes criam submenus chamando esse mesmo método
usando objetos de nós descendentes.
Cada nó da sua hierarquia de conteúdo é representado por um
objeto MediaBrowserCompat.MediaItem
. Cada um desses itens de mídia é
identificado por uma string de ID exclusiva. Os apps clientes tratam essas strings de ID como tokens opacos.
Quando um app cliente quer navegar para um submenu ou abrir um item de mídia, ele transmite o token. Seu app é responsável por associar o token ao item de mídia apropriado.
Este snippet de código mostra uma implementação 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<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<List<MediaBrowserCompat.MediaItem>> result) {
// Assume for example that the music catalog is already loaded/cached.
List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>();
// 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 um exemplo desse método, consulte o onLoadChildren
no
app de exemplo Universal Android Music Player no GitHub (link em inglês).
Estruturar o menu raiz
O Android Auto e o Android Automotive OS têm restrições específicas sobre a
estrutura do menu raiz. Elas são comunicadas ao MediaBrowserService
por dicas raiz, que podem ser lidas pelo argumento Bundle transmitido para
onGetRoot()
. Seguindo essas dicas, o sistema pode exibir o conteúdo raiz como guias de navegação. Se você não seguir essas dicas, alguns conteúdos raiz poderão
ser descartados ou passar a ser menos detectáveis pelo sistema.
Figura 1. Conteúdo raiz mostrado como guias de navegação.
Ao aplicar essas dicas, o sistema mostra o conteúdo raiz como guias de navegação. Se você não seguir essas dicas, alguns conteúdos raiz poderão ser descartados ou passar a ser menos detectáveis. Estas dicas são transmitidas:
Limite para o número de filhos raiz: na maioria dos casos, esse número é quatro, o que significa que apenas quatro guias (ou menos) podem ser mostradas.
Flags aceitas nos filhos raiz: esse valor pode ser
MediaItem#FLAG_BROWSABLE
, o que significa que somente itens navegáveis (e não jogáveis) podem ser mostrados como guias.Limite no número de ações de navegação personalizadas: verifique quantas ações de navegação personalizadas são compatíveis.
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...
}
Você pode optar por ramificar a lógica da estrutura de sua hierarquia de conteúdo
com base nos valores dessas dicas, especialmente se a hierarquia variar entre
integrações de MediaBrowser
fora do Android Auto e do AAOS.
Por exemplo, se você normalmente mostra um item raiz reproduzível, convém aninhá-lo em um item navegável raiz devido ao valor da dica de flag aceita.
Além das dicas raiz, use estas diretrizes para renderizar guias de maneira ideal:
Ícones monocromáticos (preferencialmente brancos) para cada item da guia
Rótulos curtos e significativos para cada item da guia (rótulos curtos reduzem as chances de serem truncados)