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, v.v. Trình phát này có sẵn chức năng tìm hiểu 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 chế độ điều khiển nội dung đa phương tiện (như tạm dừng, bỏ qua, tua đi, bật ngón cái).

Trợ lý giao tiếp với các ứng dụng đa phương tiện trên Android thông qua một phiên phát nội dung đa phương tiện. Hộp thoại này có thể dùng ý định hoặc dịch vụ để chạy ứng dụng và bắt đầu phát. Để có kết quả tốt nhất, ứng dụng của bạn phải 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 phải triển khai một phiên phát nội dung đa phương tiện để Trợ lý có thể vận hành các bộ điều khiển truyền tải sau khi quá trình phát bắt đầu.

Xin lưu ý rằng mặc dù Trợ lý chỉ sử dụng các thao tác nêu trong phần này, nhưng 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 có thể chỉ trả về 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 các cờ này trong đối tượng MediaSession của ứng dụng:

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

Dự án mẫu Universal Android Music Player là một ví dụ hay về cách thiết lập một phiên phát nội dung nghe nhì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 và các lệnh gọi lại của chúng:

Hành động Số nhận cuộc gọi lại
ACTION_PLAY onPlay()
ACTION_PLAY_FROM_SEARCH onPlayFromSearch()
ACTION_PLAY_FROM_URI (*) onPlayFromUri()

Phiên hoạt động của bạn cũng sẽ triển khai các thao tác PREPARE sau và các lệnh gọi lại của chúng:

Hành động Số nhận cuộc gọi lại
ACTION_PREPARE onPrepare()
ACTION_PREPARE_FROM_SEARCH onPrepareFromSearch()
ACTION_PREPARE_FROM_URI (*) onPrepareFromUri()

(*) Các hành động dựa trên URI của Trợ lý Google chỉ dùng được cho những công ty cung cấp URI cho Google. Để tìm hiểu thêm về cách mô tả nội dung đa phương tiện của bạn cho Google, hãy xem bài viết 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 lại sau một 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ễ phát có thể sử dụng thêm thời gian để 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 cụm từ 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]”, phương thức gọi lại onPrepareFromSearch() hoặc onPlayFromSearch() sẽ 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 bước sau:

  1. Bạn có thể 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. Tạo 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 kết quả.

Phương thức onPlayFromSearch() sẽ lấy một tham số bổ sung có thông tin chi tiết hơn từ tính năng tìm kiếm bằng giọng nói. Các thành phần bổ sung này giúp bạn tìm thấy nội dung âm thanh trong ứng dụng để phát lại. 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 cụm từ tìm kiếm thô và phát các bản nhạc thích hợp dựa trên cụm từ tìm kiếm đó.

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 đè phương thức onPlayFromSearch() trong quá trình triển khai MediaSession.Callback để 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:

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

Để biết 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 nội dung âm thanh trong ứng dụng, hãy xem mẫu Universal Android Music Player.

Xử lý truy vấn trống

Nếu onPrepare(), onPlay(), onPrepareFromSearch() hoặc onPlayFromSearch() được gọi mà không có cụm từ tìm kiếm, thì ứng dụng đa phương tiện của bạn sẽ phát nội dung nghe nhìn "hiện tại". 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ư một bài hát trong danh sách phát gần đây nhất hoặc một 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 cần thêm thông tin.

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 sẽ tìm cách chạy ứng dụng của bạn và phát âm thanh bằng cách gọi phương thức 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 phương thức onPlayFromSearch() sẽ nhận được một tham số truy vấn trống. Trong những trường hợp như vậy, ứng dụng của bạn sẽ phản hồi bằng cách phát âm thanh ngay lập tức, chẳng hạn như một bài hát trong danh sách phát gần đây nhất hoặc một hàng đợi ngẫu nhiên.

Khai báo dịch vụ hỗ trợ cũ cho thao tác bằng giọng nói

Trong hầu hết trường hợp, việc xử lý các thao tác phát như mô tả ở trên sẽ cung cấp cho ứng dụng 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 khả năng hỗ trợ cho bộ lọc Ý đị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 đ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>

Đ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 đ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 về nội dung nghe nhìn. Để làm được việc này, mã của bạn phải cho phép các thao tác sau đây và triển khai các lệnh gọi lại tương ứng:

Hành động Số nhận cuộc gọi lại Nội dung 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. Công cụ 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 thích hợp (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 mức phân loại mà ứng dụng hỗ trợ và ứ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 nên đặ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ể thay đổi tuỳ theo loại nội dung.

Loại nội dung Hành động bắt buộc
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

Đề xuất hỗ trợ cho: Tua đến

Podcast

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

Đề xuất hỗ trợ cho: Chuyển đến Tiếp theo và Chuyển đế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 đi

Đề xuất hỗ trợ cho: Chuyển tới Tiếp theo và Chuyển đến Trước

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

Mẫu truy vấn bằng giọng nói nên thử

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ử quá trình triển khai:

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

“Phát.”

"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).”

Kênh phát thanh “Phát (frequency | trạm) 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() "Tiếp theo (bài hát | tập | tập nhạc)."
onSkipToPrevious() "Trước (bài hát | tập | bài hát)."
onSeekTo()

“Khởi động lại.”

"Tua đi ## giây."

"Quay lại ## phút."

Không áp dụng (luôn cập nhật MediaMetadata) "Tôi đang nghe gì thế?"

Lỗi

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

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

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

  • 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 khả dụng
    • Hãy đặt mã lỗi PlaybackState một cách thích hợp. Ví dụ: đặt PlaybackState thành ERROR_CODE_NOT_SUPPORTED nếu hành động không được hỗ trợ hoặc ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED nếu hành động được bảo vệ khi đă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
    • Hãy đặt mã lỗi PlaybackState một cách thích hợp. Ví dụ: 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 khi 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 tìm nội dung tương tự để phát. Trợ lý sẽ xử lý việc nói câu trả lời bằng giọng nói phù hợp nhất 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 khởi động một ứng dụng dành cho thiết bị di động, Trợ lý 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 truyền hình, ứng dụng của bạn phải bao gồm Nhà cung cấp dịch vụ tìm kiếm trên TV để hiển thị URI cho nội dung nghe nhìn. Trợ lý gửi một truy vấn đến trình cung cấp nội dung. Truy vấn này sẽ trả về một ý định chứa URI cho đường liên kết sâu và một hành động 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 thao tác đó và URI trở lại ứng dụng của bạn. Nếu nhà cung cấp không chỉ định thao tác, Trợ lý sẽ thêm ACTION_VIEW vào Ý định.

Trợ lý sẽ thêm EXTRA_START_PLAYBACK bổ sung có giá trị true vào ý định 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 được ý định bằng EXTRA_START_PLAYBACK.

Xử lý ý định khi đang 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 vẫn đang phát nội dung từ một yêu cầu trước đó. Điều này có nghĩa 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 bằng đường liên kết sâu nên 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ác cờ bổ sung vào ý định mà Trợ lý gửi đến ứng dụng của bạn. Cụ thể, Trợ lý có thể thêm FLAG_ACTIVITY_CLEAR_TOP, FLAG_ACTIVITY_NEW_TASK hoặc cả hai. Mặc dù mã của bạn không cần xử lý các cờ này, nhưng hệ thống Android sẽ phản hồi các cờ đó. Điều này có thể ảnh hưởng đến hành vi của ứng dụng khi yêu cầu phát thứ hai có URI mới đến trong khi URI trước đó vẫn đang phát. Trong trường hợp này, bạn nên kiểm thử cách ứng dụng của mình phản hồi. Bạn có thể sử dụng công cụ dòng lệnh adb để mô phỏng tình huống (hằng số 0x14000000 là giá trị boolean bitwise HOẶC của 2 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 lại từ một dịch vụ

Nếu ứng dụng của bạn có media browser service cho phép các kết nối từ Trợ lý, thì Trợ lý có thể khởi động ứng dụng bằng cách giao tiếp với media session của dịch vụ. Dịch vụ trình duyệt nội dung đa phương tiện tuyệt đối không được chạy một 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 động dịch vụ trình duyệt nội dung đa phương tiện. Hãy nhớ luôn đặt các thao tác phát được hỗ trợ, bao gồm cả trong quá trình khởi chạy. Trợ lý yêu cầu ứng dụng đa phương tiện của bạn đặt các hành động phát trước khi Trợ lý gửi lệnh 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. Dịch vụ 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 trong phiên phát nội dung nghe nhìn của ứng dụng.

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

Bắt đầu phát với một phiên nội dung nghe nhìn

Khi người dùng đưa 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 sau khi thông báo hoàn tất, Trợ lý sẽ đưa ra một hành động PLAY. Tính năng này 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ợ 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, Trợ lý phải kết nối được với MediaBrowserService của ứng dụng đó và truy xuất MediaSession.Token. Các yêu cầu kết nối sẽ được xử lý trong phương thức onGetRoot() của dịch vụ. Có hai cách để xử lý yêu cầu:

  • Chấp nhận mọi 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 mọi yêu cầu kết nối

Bạn phải trả về một 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ả các ứng dụng MediaBrowser kết nối với MediaBrowserService. Bạn phải trả về một BrowserRoot khác rỗng. Dưới đây là mã áp dụng 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 chữ ký và gói ứng dụng Trợ lý

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 bạn một cách rõ ràng bằng cách kiểm tra tên gói và chữ ký của gói. Ứ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ề một 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. Mẫu Universal Music Player duy trì một danh sách các tên gói và chữ ký đã 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>