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, nu
ll)
자바
@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
메서드를 호출할 때 null이 아닌 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 포함) 및 Android Automotive OS(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
)
}
자바
@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()
에 전달된 번들 인수를 통해 읽힐 수 있습니다. 이러한 힌트를 따르면 시스템이 루트 콘텐츠를 탐색 탭으로 표시할 수 있습니다. 이러한 힌트를 따르지 않으면 일부 루트 콘텐츠가 삭제되거나 시스템의 검색 가능성이 낮아질 수 있습니다.
그림 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...
}
자바
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
통합 간에 다르다면 이러한 힌트 값에 따라 콘텐츠 계층 구조의 구조에 관한 로직을 브랜칭할 수 있습니다.
예를 들어 재생 가능한 루트 항목을 일반적으로 표시하는 경우 지원되는 플래그 힌트 값으로 인해 대신 탐색 가능한 루트 항목 아래에 중첩할 수 있습니다.
루트 힌트 외에도 다음 가이드라인을 사용하여 탭을 최적으로 렌더링하세요.
각 탭 항목의 흑백 (흰색 권장) 아이콘
각 탭 항목에 짧고 의미 있는 라벨 (짧은 라벨은 라벨이 잘릴 가능성을 줄임)