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

Trợ lý Google cho phép bạn 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à nhiều thiết bị khác. Ứng dụng này có sẵn khả năng để hiểu các lệnh về nội dung nghe nhìn ("phát bài hát 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 phiên phát nội dung đa phương tiện. Ứng dụng này có thể dùng intent hoặc dịch vụ để khởi 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 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 một phiên phát nội dung nghe nhìn

Mọi ứng dụng âm thanh và video đều phải triển khai một phiên phát nội dung nghe nhìn để Trợ lý có thể vận hành các nút đ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 được liệt kê trong phần này, nhưng bạn nên triển khai tất cả các API chuẩn bị và phát để đả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 của 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 phương tiện và chế độ điều khiển 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 nghe nhìn của ứng dụng phải khai báo những thao tác 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 nghe nhì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ụ điển hình về cách thiết lập một 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 nghe nhìn phải có những thao tác PLAY này và các lệnh gọi lại tương ứ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 phải triển khai những thao tác PREPARE này và các lệnh gọi lại của chúng:

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 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 nghe nhìn cho Google, hãy xem phần 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ị, bạn có thể giảm độ trễ phát sau khi đưa ra lệnh thoại. 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 nghe nhì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 bổ sung.

Ứng dụng của bạn nên phân tích cú pháp cụm từ 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. Sử dụng gói bổ sung và chuỗi truy vấn 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 danh sách 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 số các kết quả.

Phương thức onPlayFromSearch() 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 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. 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 phù hợp dựa trên cụm từ tìm kiếm.

Android Automotive OS và Android Auto hỗ trợ các thành phần bổ sung sau:

Đoạn mã sau đây cho thấy cách ghi đè phương thức onPlayFromSearch() trong quá trình triển khai MediaSession.Callback để phân tích cú pháp cụm từ 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 của bạn, 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 không có nội dung nghe nhìn nào đang phát, ứng dụng sẽ cố gắng phát mộ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ẽ 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ó 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 sẽ cố gắng khởi 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 nghe nhì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 này, ứng dụng của bạn phải 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 tính năng hỗ trợ các thao tác bằng giọng nói cũ

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ẽ cung cấp cho ứng dụng của bạn 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 một bộ lọc Ý định để tìm kiếm. Bạn nên khai báo việc hỗ trợ Bộ lọc ý định này trong tệp kê khai của ứng dụng.

Đưa mã này vào tệp kê khai cho một ứ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>

Các nút đ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 hoạt động, Trợ lý có thể đưa ra các lệnh thoại để kiểm soát quá trình phát và cập nhật siêu dữ liệu nội dung nghe nhìn. Để hoạt động này diễn ra, mã của bạn phải cho phép các thao tác sau và triển khai các lệnh gọi lại tương ứng:

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() Tiếp tục
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 cùng với 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 cập nhật siêu dữ liệu của phiên phát nội dung nghe nhì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ư tiêu đề 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ợ tính năng phân loại, ứng dụng nên đặt loại 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ể khác nhau tuỳ 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 sang bài tiếp theo và Chuyển sang bài trước

Rất nên 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 bài tiếp theo và Chuyển đến bài 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 sang bài tiếp theo và Chuyển sang bài trước
Video

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

Rất nên hỗ trợ: Chuyển đến bài tiếp theo và Chuyển đến bài trước

Bạn phải hỗ trợ nhiều hành động nhất có thể trong số các hành động nêu trên nếu sản phẩm của bạn cho phép, nhưng vẫn phải phản hồi một cách phù hợp đối với mọi hành động khác. Ví dụ: nếu chỉ người dùng trả phí mới có thể quay lại mục trước đó, thì bạn có thể đưa ra 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 để biết thêm hướng dẫn.

Các truy vấn mẫu bằng giọng nói để 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()
Nhạc

"Phát nhạc hoặc bài hát trên (tên ứng dụng)". Đây là một cụm từ tìm kiếm 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 "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 lại".
onSkipToNext() "(Bài hát | Tập | Bản nhạc) tiếp theo".
onSkipToPrevious() "(Bài hát | Tập | Bản nhạc) trước".
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ý các lỗi từ một phiên nội dung nghe nhìn khi chúng xảy ra và báo cáo cho người dùng. Đảm bảo rằng phiên đa phương tiện của bạn cập nhật trạng thái truyền tải và mã lỗi trong PlaybackState một cách chính xác, như mô tả trong phần Làm việc với phiên đa phương tiện. Trợ lý nhận dạng tất cả mã lỗi do 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ề trường hợp lỗi mà bạn phải đả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 thiết để 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 có sẵn
    • Đặ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 thao tác không được hỗ trợ hoặc ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED nếu thao tác được bảo vệ bằng tính năng đă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ụ: 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 để tạm dừng 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ụ: người dùng thuộc gói miễn phí yêu cầu nội dung chỉ dành cho người dùng thuộc gói trả phí.
    • Bạn không nên trả về lỗi mà thay vào đó, hãy ư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 quá trình phát bắt đầu.

Phát bằng một ý định

Trợ lý có thể khởi chạy một ứng dụng âm thanh hoặc video và bắt đầu phát bằng cách gửi một ý định có đườ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 di động, Trợ lý có thể sử dụng Google Tìm kiếm để truy xuất nội dung được đánh dấu cung cấp một 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 có một TV Search Provider (Trình cung cấp thông tin tìm kiếm trên TV) để hiển thị URI cho nội dung nghe nhìn. Trợ lý sẽ gửi một truy vấn đến nhà cung cấp nội dung. Nhà cung cấp này sẽ trả về một ý định chứa URI cho đường liên kết sâu và một thao tác không bắt buộc. Nếu truy vấn trả về một thao tác trong ý định, thì 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 một thao tác, thì Trợ lý sẽ thêm ACTION_VIEW vào Ý định.

Trợ lý sẽ thêm phần bổ sung EXTRA_START_PLAYBACK có giá trị true vào ý định mà Trợ lý 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 có 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 của bạn 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 ý đị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 sẽ 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 hoặc 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. 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 công cụ dòng lệnh adb để mô phỏng tình huống (hằng số 0x14000000 là phép toán OR theo bit boolean 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 từ một dịch vụ

Nếu ứng dụng của bạn có một 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 nghe nhìn không bao giờ đượ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 chạy dịch vụ trình duyệt nội dung nghe nhìn. Hãy nhớ luôn đặt các thao tác phát được hỗ trợ, kể 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 thao tác 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. Ứng dụng 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 phát nội dung nghe nhìn của ứng dụng.

Sơ đồ sau đây cho thấy thứ tự các lệnh gọi do Trợ lý tạo 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ả các lệnh gọi đều không đồng bộ. Trợ lý không chờ bất kỳ phản hồi nào từ ứng dụng của bạn.

Bắt đầu phát bằng 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 khi thông báo hoàn tất, Trợ lý sẽ phát hành một thao tác PHÁT. Phương thức 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ợ các thao tác ACTION_PREPARE_*, thì Trợ lý sẽ gọi thao tác PREPARE trước khi bắt đầu thông báo.

Kết nối với MediaBrowserService

Để sử dụng một dịch vụ nhằm khởi động ứng dụng, Trợ lý phải có thể kết nối với MediaBrowserService của ứng dụng và truy xuất MediaSession.Token của ứng dụng. Yêu cầu kết nối đượ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 tất cả yêu cầu kết nối
  • Chỉ chấp nhận 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ề một BrowserRoot để cho phép Trợ lý gửi lệnh đến phiên nội dung nghe nhì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 của bạn. Bạn phải trả về một BrowserRoot không có giá trị rỗng. Sau đâ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 gói ứng dụng và chữ ký của 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 nghe nhìn một cách rõ ràng bằng cách kiểm tra tên gói và chữ ký của dịch vụ đó. Ứ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 nội dung nghe nhìn của bạn. Mẫu Trình phát nhạc đa năng duy trì 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>