Trợ lý Google và các ứng dụng đa phương tiện

Trợ lý Google cho phép bạn sử dụng lệnh thoại để điều khiển nhiều thiết bị, chẳng hạn như Google Home, điện thoại của bạn và nhiều thiết bị khác. Chiến dịch này có tích hợp sẵn tính năng hiểu các lệnh phát nội dung đa phương tiện ("phát nội dung nào đó của Beyoncé") và hỗ trợ các nút điều khiển nội dung nghe nhìn (như tạm dừng, bỏ qua, tua đi, thích).

Trợ lý giao tiếp với các ứng dụng đa phương tiện trên Android bằng một nội dung nghe nhìn . Công cụ này có thể sử dụng ý định hoặc dịch vụ để khởi chạy ứng dụng của bạn và bắt đầu phát. Để có kết quả tốt nhất, ứng dụng của bạn nên triển khai tất cả các tính năng được mô tả trên trang này.

Sử dụng phiên nội dung nghe nhìn

Mọi ứng dụng âm thanh và video đều phải triển khai phiên phát nội dung đa phương tiện để Trợ lý có thể hoạt động các nút điều khiển truyền tải sau khi bắt đầu phát.

Xin lưu ý rằng mặc dù Trợ lý chỉ sử dụng những hành động được liệt kê trong phần này, phương pháp hay nhất là triển khai tất cả các API chuẩn bị và phát lại để đảm bảo khả năng tương thích với các ứng dụng khác. Đối với mọi thao tác mà bạn không hỗ trợ, các lệnh gọi lại phiên phát nội dung đa phương tiện chỉ cần trả về một lỗi bằng cách sử dụng ERROR_CODE_NOT_SUPPORTED.

Bật các chế độ điều khiển nội dung nghe nhìn và truyền tải bằng cách đặt những cờ này trong phần cài đặt của ứng dụng Đối tượng MediaSession:

Kotlin

session.setFlags(
        MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or
        MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
)

Java

session.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
    MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);

Phiên đa phương tiện của ứng dụng phải khai báo các thao tác mà ứng dụng hỗ trợ và triển khai các lệnh gọi lại phiên đa phương tiện tương ứng. Khai báo các thao tác được hỗ trợ trong setActions().

Chiến lược phát hành đĩa đơn Trình phát nhạc Universal Android dự án mẫu là ví dụ hay về cách thiết lập phiên phát nội dung đa phương tiện.

Thao tác phát

Để bắt đầu phát từ một dịch vụ, phiên phát nội dung đa phương tiện phải có các thao tác PLAY sau đây và lệnh gọi lại chúng:

Hành động Lệnh gọi lại
ACTION_PLAY onPlay()
ACTION_PLAY_FROM_SEARCH onPlayFromSearch()
ACTION_PLAY_FROM_URI (*) onPlayFromUri()

Phiên của bạn cũng nên triển khai các thao tác PREPARE sau đây và lệnh gọi lại của các thao tác đó:

Hành động Lệnh gọi lại
ACTION_PREPARE onPrepare()
ACTION_PREPARE_FROM_SEARCH onPrepareFromSearch()
ACTION_PREPARE_FROM_URI (*) onPrepareFromUri()

(*) Các thao tác dựa trên URI của Trợ lý Google chỉ hoạt động đối với công ty cung cấp URI cho Google. Để tìm hiểu thêm về cách mô tả nội dung nghe nhìn của bạn cho Google hãy xem Hành động đối với nội dung đa phương tiện.

Bằng cách triển khai các API chuẩn bị, độ trễ phát sau lệnh thoại có thể giảm được. Các ứng dụng đa phương tiện muốn cải thiện độ trễ khi phát có thể sử dụng để bắt đầu lưu nội dung vào bộ nhớ đệm và chuẩn bị phát nội dung nghe nhìn.

Phân tích cú pháp truy vấn tìm kiếm

Khi người dùng tìm kiếm một mục nội dung đa phương tiện cụ thể, chẳng hạn như “Phát nhạc jazz trên [tên ứng dụng của bạn]" hoặc "Nghe [tên bài hát]", onPrepareFromSearch() hoặc onPlayFromSearch() nhận được một tham số truy vấn và một gói dữ liệu bổ sung.

Ứng dụng của bạn nên phân tích cú pháp truy vấn tìm kiếm bằng giọng nói và bắt đầu phát bằng cách làm theo các các bước:

  1. Sử dụng gói dữ liệu bổ sung và chuỗi cụm từ tìm kiếm được trả về từ tính năng tìm kiếm bằng giọng nói để lọc kết quả.
  2. Xây dựng hàng đợi phát dựa trên những kết quả này.
  3. Phát mục nội dung nghe nhìn phù hợp nhất trong phần kết quả.

onPlayFromSearch() phương thức lấy một thông số bổ sung có thông tin chi tiết hơn từ giọng nói tìm kiếm. Những thông tin bổ sung này giúp bạn tìm thấy nội dung âm thanh trong ứng dụng để phát. Nếu kết quả tìm kiếm không thể cung cấp dữ liệu này, bạn có thể triển khai logic để phân tích cú pháp truy vấn tìm kiếm thô và phát các bản nhạc phù hợp dựa trên truy vấn.

Các tính năng bổ sung sau đây được hỗ trợ trong Android Automotive OS và Android Auto:

Đoạn mã sau đây cho biết cách ghi đè onPlayFromSearch() trong MediaSession.Callback của bạn để phân tích cú pháp truy vấn tìm kiếm bằng giọng nói và bắt đầu phát lại:

Kotlin

override fun onPlayFromSearch(query: String?, extras: Bundle?) {
    if (query.isNullOrEmpty()) {
        // The user provided generic string e.g. 'Play music'
        // Build appropriate playlist queue
    } else {
        // Build a queue based on songs that match "query" or "extras" param
        val mediaFocus: String? = extras?.getString(MediaStore.EXTRA_MEDIA_FOCUS)
        if (mediaFocus == MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE) {
            isArtistFocus = true
            artist = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST)
        } else if (mediaFocus == MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE) {
            isAlbumFocus = true
            album = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM)
        }

        // Implement additional "extras" param filtering
    }

    // Implement your logic to retrieve the queue
    var result: String? = when {
        isArtistFocus -> artist?.also {
            searchMusicByArtist(it)
        }
        isAlbumFocus -> album?.also {
            searchMusicByAlbum(it)
        }
        else -> null
    }
    result = result ?: run {
        // No focus found, search by query for song title
        query?.also {
            searchMusicBySongTitle(it)
        }
    }

    if (result?.isNotEmpty() == true) {
        // Immediately start playing from the beginning of the search results
        // Implement your logic to start playing music
        playMusic(result)
    } else {
        // Handle no queue found. Stop playing if the app
        // is currently playing a song
    }
}

Java

@Override
public void onPlayFromSearch(String query, Bundle extras) {
    if (TextUtils.isEmpty(query)) {
        // The user provided generic string e.g. 'Play music'
        // Build appropriate playlist queue
    } else {
        // Build a queue based on songs that match "query" or "extras" param
        String mediaFocus = extras.getString(MediaStore.EXTRA_MEDIA_FOCUS);
        if (TextUtils.equals(mediaFocus,
                MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE)) {
            isArtistFocus = true;
            artist = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST);
        } else if (TextUtils.equals(mediaFocus,
                MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE)) {
            isAlbumFocus = true;
            album = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM);
        }

        // Implement additional "extras" param filtering
    }

    // Implement your logic to retrieve the queue
    if (isArtistFocus) {
        result = searchMusicByArtist(artist);
    } else if (isAlbumFocus) {
        result = searchMusicByAlbum(album);
    }

    if (result == null) {
        // No focus found, search by query for song title
        result = searchMusicBySongTitle(query);
    }

    if (result != null && !result.isEmpty()) {
        // Immediately start playing from the beginning of the search results
        // Implement your logic to start playing music
        playMusic(result);
    } else {
        // Handle no queue found. Stop playing if the app
        // is currently playing a song
    }
}

Để xem ví dụ chi tiết hơn về cách triển khai tính năng tìm kiếm bằng giọng nói để phát âm thanh nội dung trong ứng dụng của bạn, hãy xem Universal Android Music Player mẫu.

Xử lý các truy vấn trống

Nếu là onPrepare(), onPlay(), onPrepareFromSearch() hoặc onPlayFromSearch() được gọi mà không có truy vấn tìm kiếm, ứng dụng đa phương tiện của bạn sẽ phát đoạn mã "hiện tại" nội dung đa phương tiện. Nếu hiện không có nội dung nghe nhìn, ứng dụng sẽ cố gắng phát nội dung nào đó, chẳng hạn như dưới dạng bài hát từ danh sách phát gần đây nhất hoặc hàng đợi ngẫu nhiên. Trợ lý sử dụng các API này khi người dùng yêu cầu "Phát nhạc trên [tên ứng dụng của bạn]" mà không thông tin bổ sung.

Khi người dùng nói "Phát nhạc trên [tên ứng dụng của bạn]", Android Automotive OS hoặc Android Auto cố gắng chạy ứng dụng của bạn và phát âm thanh bằng cách gọi onPlayFromSearch() của ứng dụng . Tuy nhiên, vì người dùng không nói tên của mục nội dung đa phương tiện, nên thẻ onPlayFromSearch() sẽ nhận được thông số truy vấn trống. Trong những trường hợp này, ứng dụng của bạn nên phản hồi bằng cách phát ngay âm thanh, chẳng hạn như bài hát trong danh sách phát hoặc một hàng đợi ngẫu nhiên.

Khai báo tính năng hỗ trợ cũ cho thao tác bằng giọng nói

Trong hầu hết các trường hợp, việc xử lý các thao tác phát được mô tả ở trên sẽ giúp ứng dụng của bạn có tất cả chức năng phát mà ứng dụng cần. Tuy nhiên, một số hệ thống yêu cầu ứng dụng của bạn phải chứa Bộ lọc ý định cho hoạt động tìm kiếm. Bạn nên khai báo tính năng hỗ trợ cho Ý định này trong các tệp kê khai của ứng dụng.

Đưa mã này vào tệp kê khai cho ứng dụng dành cho điện thoại:

<activity>
    <intent-filter>
        <action android:name=
             "android.media.action.MEDIA_PLAY_FROM_SEARCH" />
        <category android:name=
             "android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

Phương thức điều khiển phương tiện giao thông

Sau khi phiên phát nội dung nghe nhìn của ứng dụng ở trạng thái đang hoạt động, Trợ lý có thể đưa ra lệnh thoại để điều khiển chế độ phát và cập nhật siêu dữ liệu của nội dung nghe nhìn. Để làm được điều này, mã sẽ cho phép các hành động sau đây và triển khai các mã tương ứng lệnh gọi lại:

Hành động Lệnh gọi lại Mô tả
ACTION_SKIP_TO_NEXT onSkipToNext() Video tiếp theo
ACTION_SKIP_TO_PREVIOUS onSkipToPrevious() Bài hát trước
ACTION_PAUSE, ACTION_PLAY_PAUSE onPause() Tạm dừng
ACTION_STOP onStop() Dừng
ACTION_PLAY onPlay() Kích hoạt lại
ACTION_SEEK_TO onSeekTo() Tua lại 30 giây
ACTION_SET_RATING onSetRating(android.support.v4.media.RatingCompat) Thích/không thích.
ACTION_SET_CAPTIONING_ENABLED onSetCaptioningEnabled(boolean) Bật/tắt phụ đề.

Xin lưu ý:

  • Để các lệnh tìm kiếm hoạt động, PlaybackState cần được cập nhật bằng state, position, playback speed, and update time. Ứng dụng phải gọi setPlaybackState() khi trạng thái thay đổi.
  • Ứng dụng đa phương tiện cũng phải luôn cập nhật siêu dữ liệu của phiên phát nội dung đa phương tiện. Tính năng này hỗ trợ các câu hỏi như "bài hát nào đang phát?" Ứng dụng phải gọi setMetadata() khi các trường áp dụng (chẳng hạn như tên bản nhạc, nghệ sĩ và tên) thay đổi.
  • Bạn phải đặt MediaSession.setRatingType() để cho biết loại điểm xếp hạng mà ứng dụng hỗ trợ, đồng thời ứng dụng phải triển khai onSetRating(). Nếu không hỗ trợ mức phân loại, thì ứng dụng phải đặt loại mức phân loại thành RATING_NONE.

Các thao tác bằng giọng nói mà bạn hỗ trợ có thể sẽ thay đổi theo loại nội dung.

Loại nội dung Việc cần làm
Âm nhạc

Phải hỗ trợ: Phát, Tạm dừng, Dừng, Chuyển đến Tiếp theo và Chuyển đến Trước

Rất nên hỗ trợ các tính năng: Tua đến

Podcast

Phải hỗ trợ: Phát, Tạm dừng, Dừng và Tua đến

Đề xuất hỗ trợ cho: Bỏ qua đến Tiếp theo và Bỏ qua đến Trước

Sách nói Phải hỗ trợ: Phát, Tạm dừng, Dừng và Tua đến
Đài Phải hỗ trợ: Phát, Tạm dừng và Dừng
Tin tức Phải hỗ trợ: Phát, Tạm dừng, Dừng, Chuyển đến Tiếp theo và Chuyển đến Trước
Video

Phải hỗ trợ: Phát, Tạm dừng, Dừng, Tua đến, Tua lại và Tua nhanh

Rất nên hỗ trợ các tính năng: Chuyển đến Tiếp theo và Chuyển đến Trước

Bạn phải hỗ trợ nhiều hành động nêu trên giống như các dịch vụ sản phẩm của mình cho phép nhưng vẫn phản hồi linh hoạt với mọi thao tác khác. Ví dụ: nếu chỉ người dùng cao cấp có thể quay lại mục trước đó, bạn có thể tăng lỗi nếu người dùng cấp miễn phí yêu cầu Trợ lý trả lại mặt hàng trước đó. Hãy xem mục xử lý lỗi để biết thêm hướng dẫn.

Thử truy vấn bằng giọng nói mẫu

Bảng sau đây trình bày một số truy vấn mẫu mà bạn nên sử dụng khi kiểm thử kết quả triển khai:

Lệnh gọi lại MediaSession Cụm từ “Ok Google” để sử dụng
onPlay()

“Chơi”.

"Tiếp tục."

onPlayFromSearch()
onPlayFromUri()
Music

“Phát nhạc hoặc bài hát trên (tên ứng dụng).” Đây là một truy vấn trống.

“Phát (bài hát | nghệ sĩ | đĩa nhạc | thể loại | danh sách phát) trên (tên ứng dụng).”

Đài “Phát (tần số | đài) trên (tên ứng dụng).”
Audiobook

"Đọc sách nói của tôi trên (tên ứng dụng)."

"Đọc (sách nói) trên (tên ứng dụng)."

Podcast "Phát (podcast) trên (tên ứng dụng)."
onPause() "Tạm dừng."
onStop() "Dừng."
onSkipToNext() "Next (bài hát | tập | bản nhạc)."
onSkipToPrevious() "Trước (bài hát | tập | bản nhạc)."
onSeekTo()

"Khởi động lại".

“Tua đi ## giây.”

“Quay lại ## phút.”

Không áp dụng (hãy lưu MediaMetadata đã cập nhật) “Tôi đang nghe gì thế?”

Lỗi

Trợ lý xử lý lỗi trong một phiên nội dung nghe nhìn khi phiên đó xảy ra rồi báo cáo cho người dùng. Hãy đảm bảo rằng phiên phát nội dung đa phương tiện cập nhật trạng thái truyền tải và mã lỗi trong PlaybackState của mình một cách chính xác, như được mô tả trong Làm việc với một phiên phát nội dung nghe nhìn. Trợ lý nhận ra tất cả mã lỗi được trả về bởi getErrorCode().

Các trường hợp thường bị xử lý sai

Sau đây là một số ví dụ về các trường hợp lỗi mà bạn nên đảm bảo mình đang xử lý chính xác:

  • Người dùng cần đăng nhập
    • Đặt mã lỗi PlaybackState thành ERROR_CODE_AUTHENTICATION_EXPIRED.
    • Đặt thông báo lỗi PlaybackState.
    • Nếu cần để phát, hãy đặt trạng thái PlaybackState thành STATE_ERROR. nếu không, hãy giữ nguyên phần còn lại của PlaybackState.
  • Người dùng yêu cầu một thao tác không thực hiện được
    • Đặt mã lỗi PlaybackState một cách thích hợp. Ví dụ: đặt giá trị PlaybackState đến ERROR_CODE_NOT_SUPPORTED nếu thao tác này không được hỗ trợ hoặc ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED nếu hành động này được bảo vệ bằng cách đăng nhập.
    • Đặt thông báo lỗi PlaybackState.
    • Giữ nguyên phần còn lại của PlaybackState.
  • Người dùng yêu cầu nội dung không có trong ứng dụng
    • Đặt mã lỗi PlaybackState một cách thích hợp. Ví dụ: hãy sử dụng ERROR_CODE_NOT_AVAILABLE_IN_REGION.
    • Đặt thông báo lỗi PlaybackState.
    • Đặt trạng thái PlaybackSate thành STATE_ERROR để làm gián đoạn quá trình phát. nếu không, hãy giữ nguyên phần còn lại của PlaybackState.
  • Người dùng yêu cầu nội dung không có kết quả khớp chính xác. Ví dụ: một người dùng cấp miễn phí yêu cầu nội dung chỉ dành cho người dùng cấp cao cấp.
    • Bạn không nên trả về lỗi mà nên ưu tiên đang tìm nội dung tương tự để chơi. Trợ lý sẽ xử lý việc nói nhiều nhất phản hồi bằng giọng nói phù hợp trước khi bắt đầu phát.

Phát lại có ý định

Trợ lý có thể chạy ứng dụng âm thanh hoặc video và bắt đầu phát bằng cách gửi một ý định bằng một đường liên kết sâu.

Ý định và đường liên kết sâu của ý định có thể đến từ nhiều nguồn:

  • Khi Trợ lý khởi động một ứng dụng di động, ứng dụng này có thể sử dụng Google Tìm kiếm để truy xuất nội dung đã đánh dấu cung cấp hành động xem kèm theo một đường liên kết.
  • Khi Trợ lý khởi động một ứng dụng TV, ứng dụng đó phải bao gồm Nhà cung cấp dịch vụ tìm kiếm truyền hình để hiển thị URI cho nội dung đa phương tiện. Trợ lý gửi truy vấn đến trình cung cấp nội dung sẽ trả về một ý định chứa URI cho liên kết sâu và thao tác không bắt buộc. Nếu truy vấn trả về một thao tác trong ý định, Trợ lý sẽ gửi hành động đó và URI trở lại ứng dụng của bạn. Nếu nhà cung cấp không chỉ định một hành động, Trợ lý sẽ thêm ACTION_VIEW vào Intent.

Trợ lý sẽ thêm EXTRA_START_PLAYBACK bổ sung với giá trị true đến ý định mà ứng dụng gửi đến ứng dụng của bạn. Ứng dụng của bạn sẽ bắt đầu phát khi nhận một ý định bằng EXTRA_START_PLAYBACK.

Xử lý ý định trong khi hoạt động

Người dùng có thể yêu cầu Trợ lý phát nội dung nào đó trong khi ứng dụng của bạn vẫn đang phát nội dung từ yêu cầu trước đó. Tức là ứng dụng của bạn có thể nhận được các ý định mới để bắt đầu phát trong khi hoạt động phát của ứng dụng đã được khởi chạy và đang hoạt động.

Các hoạt động hỗ trợ ý định có đường liên kết sâu phải ghi đè onNewIntent() để xử lý các yêu cầu mới.

Khi bắt đầu phát, Trợ lý có thể thêm cờ đến ý định mà ứng dụng gửi đến ứng dụng của bạn. Cụ thể, tính năng này có thể thêm FLAG_ACTIVITY_CLEAR_TOP hoặc FLAG_ACTIVITY_NEW_TASK hoặc cả hai. Mặc dù mã của bạn không cần xử lý những cờ này mà hệ thống Android sẽ phản hồi chúng. Điều này có thể ảnh hưởng đến hành vi của ứng dụng khi có yêu cầu phát thứ hai kèm theo URI mới trong khi URI trước đó vẫn đang phát. Bạn nên kiểm thử cách ứng dụng phản hồi trong trường hợp này. Bạn có thể dùng lệnh adb công cụ dòng để mô phỏng tình huống (hằng số 0x14000000 là boolean bitwise HOẶC của hai cờ):

adb shell 'am start -a android.intent.action.VIEW --ez android.intent.extra.START_PLAYBACK true -d "<first_uri>"' -f 0x14000000
adb shell 'am start -a android.intent.action.VIEW --ez android.intent.extra.START_PLAYBACK true -d "<second_uri>"' -f 0x14000000

Phát từ một dịch vụ

Nếu ứng dụng của bạn có media browser service cho phép kết nối từ Trợ lý, Trợ lý có thể khởi động ứng dụng bằng cách giao tiếp với media session Dịch vụ trình duyệt nội dung đa phương tiện không được chạy Hoạt động. Trợ lý sẽ chạy Hoạt động của bạn dựa trên PendingIntent mà bạn xác định bằng setSessionActivity().

Hãy nhớ đặt MediaSession.Token khi bạn khởi chạy dịch vụ trình duyệt nội dung đa phương tiện. Nhớ đặt các thao tác phát được hỗ trợ mọi lúc, kể cả trong quá trình khởi chạy. Trợ lý mong đợi nội dung nghe nhìn của bạn để thiết lập hành động phát trước khi Trợ lý gửi lần phát đầu tiên .

Để bắt đầu từ một dịch vụ, Trợ lý sẽ triển khai các API ứng dụng trình duyệt nội dung đa phương tiện. Thư viện này thực hiện các lệnh gọi TransportControls để kích hoạt lệnh gọi lại hành động PLAY trên phiên đa phương tiện của ứng dụng.

Biểu đồ dưới đây hiển thị thứ tự các cuộc gọi do Trợ lý tạo ra và lệnh gọi lại phiên đa phương tiện tương ứng. (Chỉ gửi lệnh gọi lại chuẩn bị nếu ứng dụng của bạn hỗ trợ các ngôn ngữ đó.) Tất cả các lệnh gọi đều không đồng bộ. Trợ lý không chờ phản hồi của ứng dụng.

Bắt đầu phát bằng một phiên phát nội dung nghe nhìn

Khi người dùng ra lệnh thoại để phát, Trợ lý sẽ phản hồi bằng một thông báo ngắn. Ngay khi thông báo hoàn tất, Trợ lý sẽ đưa ra hành động PLAY. Nó không chờ bất kỳ trạng thái phát cụ thể nào.

Nếu ứng dụng của bạn hỗ trợ các hành động ACTION_PREPARE_*, Trợ lý sẽ gọi hành động PREPARE trước khi bắt đầu thông báo.

Kết nối với MediaBrowserService

Để dùng một dịch vụ nhằm khởi động ứng dụng của bạn, Trợ lý phải có khả năng kết nối với MediaBrowserService và truy xuất MediaSession.Token của phương thức đó. Yêu cầu kết nối được xử lý trong onGetRoot() . Có hai cách để xử lý yêu cầu:

  • Chấp nhận tất cả yêu cầu kết nối
  • Chỉ chấp nhận các yêu cầu kết nối từ ứng dụng Trợ lý

Chấp nhận tất cả yêu cầu kết nối

Bạn phải trả về BrowserRoot để cho phép Trợ lý gửi lệnh đến phiên phát nội dung đa phương tiện của bạn. Cách dễ nhất là cho phép tất cả ứng dụng MediaBrowser kết nối với MediaBrowserService. Bạn phải trả về một BrowserRoot không rỗng. Dưới đây là mã hiện hành từ Universal Music Player:

Kotlin

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

    // To ensure you are not allowing any arbitrary app to browse your app's contents, you
    // need to check the origin:
    if (!packageValidator.isCallerAllowed(this, clientPackageName, clientUid)) {
        // If the request comes from an untrusted package, return an empty browser root.
        // If you return null, then the media browser will not be able to connect and
        // no further calls will be made to other media browsing methods.
        Log.i(TAG, "OnGetRoot: Browsing NOT ALLOWED for unknown caller. Returning empty "
                + "browser root so all apps can use MediaController. $clientPackageName")
        return MediaBrowserServiceCompat.BrowserRoot(MEDIA_ID_EMPTY_ROOT, null)
    }

    // Return browser roots for browsing...
}

Java

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

    // To ensure you are not allowing any arbitrary app to browse your app's contents, you
    // need to check the origin:
    if (!packageValidator.isCallerAllowed(this, clientPackageName, clientUid)) {
        // If the request comes from an untrusted package, return an empty browser root.
        // If you return null, then the media browser will not be able to connect and
        // no further calls will be made to other media browsing methods.
        LogHelper.i(TAG, "OnGetRoot: Browsing NOT ALLOWED for unknown caller. "
                + "Returning empty browser root so all apps can use MediaController."
                + clientPackageName);
        return new MediaBrowserServiceCompat.BrowserRoot(MEDIA_ID_EMPTY_ROOT, null);
    }

    // Return browser roots for browsing...
}

Chấp nhận gói ứng dụng Trợ lý và chữ ký

Bạn có thể cho phép Trợ lý kết nối với dịch vụ trình duyệt nội dung đa phương tiện của mình một cách rõ ràng bằng cách kiểm tra tên gói và chữ ký của Trợ lý. Ứng dụng của bạn sẽ nhận được tên gói trong phương thức onGetRoot của MediaBrowserService. Bạn phải trả về BrowserRoot để cho phép Trợ lý gửi lệnh đến phiên phát nội dung đa phương tiện của bạn. Chiến lược phát hành đĩa đơn Trình phát nhạc Universal Music mẫu sẽ duy trì danh sách tên và chữ ký gói đã biết. Dưới đây là tên gói và chữ ký mà Trợ lý Google sử dụng.

<signature name="Google" package="com.google.android.googlequicksearchbox">
    <key release="false">19:75:b2:f1:71:77:bc:89:a5:df:f3:1f:9e:64:a6:ca:e2:81:a5:3d:c1:d1:d5:9b:1d:14:7f:e1:c8:2a:fa:00</key>
    <key release="true">f0:fd:6c:5b:41:0f:25:cb:25:c3:b5:33:46:c8:97:2f:ae:30:f8:ee:74:11:df:91:04:80:ad:6b:2d:60:db:83</key>
</signature>

<signature name="Google Assistant on Android Automotive OS" package="com.google.android.carassistant">
    <key release="false">17:E2:81:11:06:2F:97:A8:60:79:7A:83:70:5B:F8:2C:7C:C0:29:35:56:6D:46:22:BC:4E:CF:EE:1B:EB:F8:15</key>
    <key release="true">74:B6:FB:F7:10:E8:D9:0D:44:D3:40:12:58:89:B4:23:06:A6:2C:43:79:D0:E5:A6:62:20:E3:A6:8A:BF:90:E2</key>
</signature>