Android Auto 和 Android Automotive OS (AAOS) 會呼叫應用程式的媒體瀏覽器服務,查看有哪些內容可用。如要支援這項功能,請在媒體瀏覽器服務中實作這兩種方法。
實作 onGetRoot
服務的 onGetRoot 方法會傳回內容階層根節點的相關資訊。Android Auto 和 AAOS 會使用這個根節點,透過 onLoadChildren 方法要求其他內容。以下程式碼片段顯示 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);
}
如需此方法的詳細範例,請參閱 GitHub 上通用 Android 音樂播放器範例應用程式中的 onGetRoot。
新增套件驗證
呼叫服務的 onGetRoot 方法時,呼叫套件會將身分識別資訊傳遞至您的服務。您的服務可利用此資訊判斷該套件是否能存取您的內容。
舉例來說,您可以只讓列在核准清單中的套件存取應用程式內容:
- 將 clientPackageName與許可清單進行比較。
- 驗證用於簽署套件 APK 的憑證。
如果套件沒有通過驗證,則傳回 null,拒絕該套件存取您的內容。
為了讓系統應用程式 (例如 Android Auto 和 AAOS) 存取您的內容,當這些系統應用程式呼叫 onGetRoot 方法時,您的服務必須傳回非空值的 BrowserRoot。
AAOS 系統應用程式的簽章會因車輛廠牌和車型而異。請務必允許所有系統應用程式建立連線,以支援 AAOS。
下列程式碼片段說明服務如何驗證呼叫套件是系統應用程式:
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
}
以上程式碼片段摘錄自 GitHub 上 Android 通用音樂播放器範例應用程式中的 PackageValidator 類別。請參閱該類別的詳細資料範例,瞭解如何針對服務的 onGetRoot 方法實作套件驗證。
除了允許系統應用程式之外,您也必須允許 Google 助理連結至您的 MediaBrowserService。Google 助理會針對手機 (包括 Android Auto 和 AAOS) 使用不同的套件名稱。
實作 onLoadChildren
收到根節點物件之後,Android Auto 和 AAOS 會透過呼叫根節點物件的 onLoadChildren,建立頂層選單來取得子系。用戶端應用程式會使用子系節點物件來呼叫同一個方法,以此建構子選單。
內容階層中的每個節點都以 MediaBrowserCompat.MediaItem 物件表示。每個媒體項目都是透過一組專屬 ID 字串識別。用戶端應用程式會將這些 ID 字串視為不透明權杖。
當用戶端應用程式想要瀏覽子選單或播放媒體項目時,系統會傳遞該權杖。您的應用程式負責將權杖與適當的媒體項目建立關聯。
這個程式碼片段顯示 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);
}
如要查看這個方法的範例,請參閱 GitHub 上通用 Android 音樂播放器範例應用程式中的 onLoadChildren。
建構根選單
Android Auto 和 Android Automotive OS 對根選單結構設有特定限制。這些限制會透過根提示傳遞至 MediaBrowserService,而您可以透過傳入 onGetRoot() 的 Bundle 引數讀取這些提示。按照這些提示操作,系統便能將根層級內容顯示為導覽分頁。如未按照這些提示操作,系統可能會捨棄某些根層級內容,或減少這些內容偵測到的機會。

圖 1. 以導覽分頁的形式呈現根內容。
套用這些提示後,系統會將根層級內容顯示為導覽分頁。如未套用這些提示,系統可能會捨棄某些根層級內容,或減少這些內容偵測到的機會。系統會傳送以下提示:
- 根子項數量的限制:在大多數情況下,此數量上限為 4,也就是說,系統最多只會顯示 4 個分頁。 
- 支援的根子項標記:這個值應為 - MediaItem#FLAG_BROWSABLE,也就是說,只有可瀏覽項目 (而非可播放項目) 可以顯示為分頁。
- 自訂瀏覽動作數量限制:查看支援的自訂瀏覽動作數量。 
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...
}
您可以根據這些提示的值,選擇建立內容階層結構的邏輯分支,尤其是階層結構在 Android Auto 和 AAOS 以外的 MediaBrowser 整合作業中有所不同的情況。
舉例來說,假如您通常會顯示某個根層級的可播放項目,則可能會根據支援的標記提示值,將它嵌入某個根層級可瀏覽項目底下的巢狀結構。
除了根提示外,請按照下列指引以最佳方式呈現分頁:
- 每個分頁項目採用單色 (建議白色) 圖示 
- 在每個分頁項目加上簡短有意義的標籤。簡短標籤較不易遭到截斷 
