Android Auto and Android Automotive OS (AAOS) call your app's media browser service to discover which content is available. To support this, you implement these two methods in your media browser service.

## Implement onGetRoot

Your service's[`onGetRoot`](https://developer.android.com/reference/androidx/media/MediaBrowserServiceCompat#onGetRoot(java.lang.String,%20int,%20android.os.Bundle))method returns information about the root node of your content hierarchy. Android Auto and AAOS use this root node to request the rest of your content using the[`onLoadChildren`](https://developer.android.com/reference/androidx/media/MediaBrowserServiceCompat#onLoadChildren(java.lang.String,androidx.media.MediaBrowserServiceCompat.Result%3Cjava.util.List%3Candroid.support.v4.media.MediaBrowserCompat.MediaItem%3E%3E,android.os.Bundle))method. This code snippet shows an implementation of the`onGetRoot`method:  

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

For a detailed example of this method, see[`onGetRoot`](https://github.com/googlesamples/android-UniversalMusicPlayer/blob/47da058112cee0b70442bcd0370c1e46e830c66b/media/src/main/java/com/example/android/uamp/media/MusicService.kt#L189)in the Universal Android Music Player sample app on GitHub.

### Add package validation

When a call is made to your service's[`onGetRoot`](https://developer.android.com/reference/androidx/media/MediaBrowserServiceCompat#onGetRoot(java.lang.String,%20int,%20android.os.Bundle))method, the calling package passes identifying information to your service. Your service can use this information to decide if that package can access your content.

For example, you can restrict access to your app's content to a list of approved packages:

- Compare the`clientPackageName`to your allowlist.
- Verify the certificate used to sign the APK for the package.

If the package can't be verified, return`null`to deny access to your content.

To provide system apps, such as Android Auto and AAOS, with access to your content, your service must return a non-null`BrowserRoot`when these system apps call the`onGetRoot`method.

The signature of the AAOS system app varies according to the make and model of a car. Be sure to allow connections from all system apps to support AAOS.

This code snippet shows how your service can validate that the calling package is a system app:  

    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
    }

This code snippet is an excerpt from the[`PackageValidator`](https://github.com/android/uamp/blob/7080a323ae5181cc44d11b319feab81fd4e568d1/common/src/main/java/com/example/android/uamp/media/PackageValidator.kt#L50)class in the Universal Android Music Player sample app on GitHub. See that class for a more detailed example of how to implement package validation for your service's`onGetRoot`method.

In addition to allowing system apps, you must allow Google Assistant connect to your`MediaBrowserService`. Google Assistant uses[separate package names](https://developer.android.com/media/implement/assistant#signatures)for the phone, which includes Android Auto Android AAOS.
| **Caution:** Don't perform checks for user authentication or other slow processes within your service's`onGetRoot`implementation. Your service must quickly return a non-null`BrowserRoot`when`onGetRoot`is called so that the calling service doesn't timeout. Handle user authentication and other business logic for slow processes in the[`onLoadChildren`](https://developer.android.com/reference/androidx/media/MediaBrowserServiceCompat#onLoadChildren(java.lang.String,androidx.media.MediaBrowserServiceCompat.Result%3Cjava.util.List%3Candroid.support.v4.media.MediaBrowserCompat.MediaItem%3E%3E,android.os.Bundle))method instead.

## Implement onLoadChildren

After receiving your root node object, Android Auto and AAOS build a top-level menu by calling[`onLoadChildren`](https://developer.android.com/reference/androidx/media/MediaBrowserServiceCompat#onLoadChildren(java.lang.String,androidx.media.MediaBrowserServiceCompat.Result%3Cjava.util.List%3Candroid.support.v4.media.MediaBrowserCompat.MediaItem%3E%3E,android.os.Bundle))on the root node object to get its descendants. Client apps build submenus by calling this same method using descendant node objects.
| **Note:** Android Auto and AAOS don't support pagination. If you build your app with`MediaLibraryService`and`MediaLibrarySession`, don't rely on the`page`or`pageSize`parameters of the`onGetChildren`callback.

Each node in your content hierarchy is represented by a[`MediaBrowserCompat.MediaItem`](https://developer.android.com/reference/android/support/v4/media/MediaBrowserCompat.MediaItem)object. Each of these media items is identified by a unique ID string. Client apps treat these ID strings as opaque tokens.

When a client app wants to browse to a submenu, or play a media item, it passes the token. Your app is responsible for associating the token with the appropriate media item.
| **Note:** Android Auto and AAOS have strict limits on how many media items can be displayed at each level in the menu. These limits minimize distractions for drivers and help your app to operate with voice commands. To learn more, see[Browsing content details](https://developers.google.com/cars/design/automotive-os/apps/media/interaction-model/browsing)and[Browsing views](https://developers.google.com/cars/design/android-auto/apps/browsing-views).

This code snippet shows an implementation of`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);
    }

To view an example of this method, see the[`onLoadChildren`](https://github.com/googlesamples/android-UniversalMusicPlayer/blob/47da058112cee0b70442bcd0370c1e46e830c66b/media/src/main/java/com/example/android/uamp/media/MusicService.kt#L215)in the Universal Android Music Player sample app on GitHub.

## Structure the root menu

Android Auto and Android Automotive OS have specific constraints about the structure of the root menu. These are communicated to the`MediaBrowserService`through root hints, which can be read through the Bundle argument passed into`onGetRoot()`. When followed, these hints let the system display the root content as navigational tabs. If you don't follow these hints, some root content might be dropped or made less discoverable by the system.

![Root content displayed as navigational tabs](https://developer.android.com/static/images/training/cars/browse_home_tabs.png)

**Figure 1.**Root content displayed as navigational tabs.

By applying these hints, the system displays the root content as navigational tabs. By not applying these hints, some root content might be dropped or made less discoverable. These hints are transmitted:

- [Limit on the number of root children](https://developer.android.com/reference/androidx/media/utils/MediaConstants#BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT()): In most cases, expect this number to be four, meaning that only four (or fewer) tabs can be shown.

- [Supported flags on the root children](https://developer.android.com/reference/androidx/media/utils/MediaConstants#BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS()): Expect this value to be[`MediaItem#FLAG_BROWSABLE`](https://developer.android.com/reference/android/support/v4/media/MediaBrowserCompat.MediaItem#FLAG_BROWSABLE()), which means only browsable items (not playable items) can be shown as tabs.

- [Limit on the number of custom browse actions](https://developer.android.com/training/cars/media/create-media-browser/custom-browse-actions#parse-actions-limit): Check how many custom browse actions are supported.

**Caution:** Not all versions of AAOS send these root hints. In the absence of these hints, assume that AAOS requires only root browsable items, and (at most) four. Consider the default values in this code snippet.  

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

You can choose to branch the logic for the structure of your content hierarchy based on the values of these hints, particularly if your hierarchy varies among`MediaBrowser`integrations outside of Android Auto and AAOS.

For example, if you normally show a root playable item, you might want to nest it under a root browsable item instead due to the value of the supported flags hint.

Apart from root hints, use these guidelines to optimally render tabs:

- Monochrome (preferably white) icons for each tab item

- Short and meaningful labels for each tab item (short labels reduce the odds that labels might be truncated)