데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      에이닷 음악 서비스를 자동차 안으로, Android Auto 개발 노트

      ReturnZero 25.05.19
      2,517 7 0
      DEVOTEE 요약
      Android Auto는 스마트폰 앱을 차량 디스플레이에 최적화하여 투영해주는 플랫폼으로, 운전 중 내비게이션, 미디어 재생, 메시징 등 운전자 편의를 제공하며 주로 스마트폰과 차량을 연결하여 동작합니다. 반면, Android Automotive OS는 차량 자체에 안드로이드 OS가 내장되어 독립적으로 앱을 실행할 수 있는 플랫폼으로, 스마트폰 없이도 차량 내 Play Store에서 앱 설치 및 하드웨어 제어를 지원합니다. Android Auto는 스마트폰 중심의 브리지 역할을 하는 반면, Android Automotive OS는 차량 자체가 독립적 디바이스로 동작하며 기능을 수행합니다.

      Android Auto 란?

      개요

      Android Auto는 스마트폰의 앱을 차량의 디스플레이에 최적화된 형태로 보여주는 플랫폼입니다.

      안드로이드 8(API 26) 이상의 휴대폰에 Android Auto 앱을 설치하고(대부분의 휴대폰에 기본 설치되어 있음) 호환 차량에 휴대폰을 연결하면,

      운전자가 차량 화면을 통해 휴대폰의 지원 앱들을 사용할 수 있습니다.

      휴대폰이 차량 UI에 투영되는 형태로 동작하며, 미디어 재생, 내비게이션, 메시징 등 운전에 적합한 앱 기능을 제공합니다.

      Android Auto는 자체적으로 앱의 백엔드 로직을 가지고 있지 않고, 휴대폰 앱에 정의된 서비스를 호출해 콘텐츠를 표시하거나 제어합니다.

      예를 들어 운전자가 차량 화면에서 음악 앱 아이콘을 탭하면, 차량 내 Android Auto 인터페이스가 음악앱(휴대폰)에 연결하여 음악 목록을 가져오고 재생 제어를 수행합니다.

      동작 구조

      Android Auto의 구성 요소는 크게 휴대폰 측 앱 서비스와 차량 측 UI로 나눌 수 있습니다.

      휴대폰 측에서는 음악 재생을 처리하는 MediaBrowserService 와 Media3, ExoPlayer 같은 플레이어가 동작하고,

      차량 측에서는 Android Auto가 제공하는 표준화된 UI 템플릿(리스트, 플레이어 컨트롤 등)을 통해 콘텐츠를 표시합니다.

      이러한 구조 덕분에 앱 개발자는 차량용 별도 UI를 만들 필요 없이, 미디어 목록과 플레이어 제어만 적절히 제공하면 Android Auto가 알아서 표준 UI로 보여주게 됩니다.

      요약하면, Android Auto는 휴대폰 앱을 차량과 연결해주는 브리지 역할을 합니다.

      운전자는 차량 화면과 음성으로 앱을 제어하고, 실제 로직과 미디어 처리는 휴대폰 앱이 담당하는 형태입니다.

      이를 통해 운전 중에도 운전자 친화적인 방식으로 음악앱의 음악 스트리밍 서비스를 즐길 수 있습니다.

      대문 사진.png


      Android Automotive OS 와의 차이점

      Android Automotive OS

      차량 자체에 내장된 안드로이드 OS 입니다. 쉽게 말해, 차 안에 안드로이드 태블릿이 내장되어 있다고 보면 됩니다.

      사용자들은 휴대폰이 아닌 차량의 PlayStore 에서 직접 앱을 설치하며, 앱이 차량의 하드웨어에서 독립적으로 실행됩니다.

      예를 들어 볼보, 폴스타 등의 일부 차량은 Android Automotive OS 를 탑재하고 있어서, 휴대폰 연결 없이도 차량 자체에서 음악앱을 설치해 음악 스트리밍을 할 수 있습니다.

      차이점

      두 환경의 주요 차이점은 구동 위치와 앱 배포 방식입니다.

      Android Auto 는 휴대폰 앱을 필요로 하고 차량은 단순히 디스플레이/컨트롤 역할을 하지만, Android Automotive OS 는 차량이 독립적인 안드로이드 디바이스로서 앱이 직접 구동됩니다.

      따라서 Android Auto 용 앱은 일반 휴대폰 앱에 차량 연결용 서비스만 추가하면 되지만,

      Android Automotive OS 용 앱은 차량전용으로 Bundling 및 배포가 필요하며 일부 UI 컴포넌트(예: 설정/로그인 화면 등)를 차내에서 구현해야 합니다.

      구분

      Android Auto

      Android Automotive OS

      구동 위치

      스마트폰 앱 실행

      차량 자체 OS (Android Automotive OS)

      앱 설치 위치

      스마트폰 Google PlayStore

      차량 내장 PlayStore (Automotive 전용)

      차량 역할

      UI 표시 및 제어 중계

      앱 실행, 미디어 재생, 하드웨어 제어 모두 차량 내 처리

      연결 방식

      USB, Bluetooth, Wireless (Projection)

      독립적, 스마트폰 없이 사용 가능

      대표 차량

      대부분의 Android Auto 지원 차량

      Volvo, Polestar, GM 등 Automotive OS 탑재 차량

      호환 차량 리스트: https://www.android.com/intl/ko_kr/auto/compatibility/#compatibility-vehicles


      Android Auto 연동 Media 앱 개발

      제한사항

      • 지원 앱 카테고리: Media, Messaging, Navigation 등 Google 승인 카테고리만 가능합니다.

        • 특정 카테고리만 지원하며, Auto Car Head Unit 의 앱메뉴에서는 Auto 에서 사용가능한 앱만 표시됩니다.

        • Android Auto 앱메뉴 화면(사용가능 앱만 노출)

      • UI 직접 제어 불가: Android Auto 가 제공하는 템플릿 UI만 사용 가능

        • 휴대폰에서 전달하는 data 를 통해 Android Auto 에서 직접 UI 표시

      • 운전자 안전 규정 준수 필수: 입력 제한, 항목 제한, 갱신 빈도 제한 등 적용

        • Google의 Android Auto 플랫폼에서는 운전자 안전을 최우선으로 고려하기 때문에 앱 개발 시 여러 가지 제한사항과 준수해야 할 가이드라인이 존재하며, 구글 심사 시 엄격하게 심사된다고 알려져 있습니다.

          • 화면에 표시되는 목록 수 제한 (20개 이하 권장)

          • 화면 갱신 빈도 제한 (3초 이상 간격 권장)

          • 복잡한 사용자 입력 제한 (텍스트 입력 불가, 최소한의 버튼 제공)

          • etc.

        • 가이드 라인: https://developers.google.com/cars/design/design-foundations/interaction-principles


      Manifest 설정

      Auto 용 앱으로 등록을 위해서는 음악을 재생하는 Service 에 아래와 같은 설정이 필요합니다.

      <service android:name=".AdotMusicService"
               android:exported="true">
          <intent-filter>
              <action android:name="android.media.browse.MediaBrowserService" />
          </intent-filter>
      </service>
      • experted=true 설정

        • Auto system 에서 해당 Service binding 을 위해 필요

      • intent-filter 설정

        • Auto system 에서 해당 Service 를 인식하기 위한 필수 사항


      Service 설정

      class AdotMusicService : MediaBrowserServiceCompat() {
      
          private lateinit var mediaSession: MediaSessionCompat
      
          override fun onCreate() {
              super.onCreate()
              mediaSession = MediaSessionCompat(this, "AdotMusicService").apply {
                  // Callback 요청은 Auto 시스템에서 의해서 호출됨. 다른 곡 재생, play/stop 등 media 재생관련 처리를 여기서 진행
                  setCallback(object : MediaSessionCompat.Callback() {
                      override fun onPlayFromMediaId(mediaId: String?, extras: Bundle?) { /* 곡 재생 처리 */ }
                      override fun onPlay() { /* 재생 처리 */ }
                      override fun onPause() { /* 일시정지 처리 */ }
                      override fun onSeekTo(pos: Long) { /* 시크 처리 */ }
                  })
                  isActive = true
              }
              sessionToken = mediaSession.sessionToken
          }
          
      override fun onBind(intent: Intent?): IBinder? = if (isCarMode) super.onBind(intent) else null
      
          // MediaBrowserServiceCompat 의 주요 함수 1.
          override fun onGetRoot(clientPackageName: String, clientUid: Int, rootHints: Bundle?): BrowserRoot {
              return BrowserRoot("root_id", null)
          }
          
          // MediaBrowserServiceCompat 의 주요 함수 2.
          override fun onLoadChildren(parentId: String, result: Result<MutableList<MediaItem>>) {
              val mediaItems = getChildrenForParent(parentId)
              result.sendResult(mediaItems)
          }
      }
      MediaBrowserServiceCompat
      • Android Auto, Wear OS, Android TV 같은 외부 기기나 다른 앱이 당신의 앱의 오디오/비디오 콘텐츠를 탐색하고 제어할 수 있게 해주는 서비스 역할

      • 미디어 플레이어와의 연결 허브

        * MediaSessionCompat 를 함께 사용하여, 플레이, 일시정지, 다음곡 같은 제어를 받음

        * Android Auto 는 MediaBrowserServiceCompat 를 통해 미디어 앱을 인식

      Media 관련 주요 class

      역할

      클래스

      설명

      미디어 탐색 서비스

      MediaBrowserServiceCompat

      Auto, Wear, TV 같은 외부 클라이언트 연결 지점

      미디어 제어 세션 관리

      MediaSessionCompat

      Play, Pause, Next 등을 앱의 Player 와 연결

      클라이언트 연결/탐색

      MediaBrowserCompat

      클라이언트가 앱의 MediaBrowserServiceCompat 연결

      MediaBrowserServiceCompat 주요 callback 함수

      함수명

      호출 시점

      역할

      onGetRoot()

      Android Auto가 서비스에 연결할 때 최초 호출

      미디어 트리 최상위 노드의 ID를 반환

      onLoadChildren()

      특정 parentId 하위 미디어 요청 시 호출

      하위 MediaItem 리스트를 반환

      onSearch()

      차량 UI 또는 Google Assistant를 통한 음성 검색 시 호출

      쿼리 결과를 준비 (결과 리스트는 별도로 onLoadChildren() 에서 반환)

      onPlayFromMediaId()

      특정 MediaItem 선택 시 호출

      해당 미디어를 재생

      MediaSessionCompat.Callback 함수

      MediaSessionCompat.Callback은 MediaBrowserServiceCompat와 연결된 MediaSession의 제어를 처리합니다.

      Android Auto에서 사용자 입력(재생, 일시정지, 다음 곡 등)은 MediaSession을 통해 앱에 전달됩니다.

      함수명

      역할

      비고

      onPlay()

      재생 요청 처리

      MediaSession 활성화, Player resume 호출

      onPause()

      일시정지 요청

      Player pause 호출

      onSeekTo()

      특정 위치 이동

      Player seek 호출

      onPlayFromMediaId()

      특정 미디어 ID 재생 요청

      MediaBrowser로 선택 시 호출

      onPlayFromSearch()

      검색 결과 재생 요청

      Google Assistant 음성 명령 시 필수

      Auto 연결 확인

      Android 에서 제공하는 CarConnection class 를 통해 Auto 연결 상황을 observing 할 수 있습니다.

      class AutoConnectionManagerImpl : AutoConnectionManager {
      
          private val _connectType = MutableStateFlow(AutoConnectionType.NONE)
          override val connectType = _connectType.asStateFlow()
      
          private lateinit var carConnection: CarConnection
          private lateinit var observer: Observer<Int>
      
          override fun initialize(context: Context) {
              carConnection = CarConnection(context)
              observer = Observer { type ->
                  when (type) {
                      CarConnection.CONNECTION_TYPE_NOT_CONNECTED -> AutoConnectionType.DISCONNECT // 연결 끊김
                      CarConnection.CONNECTION_TYPE_PROJECTION -> AutoConnectionType.CONNECT // 연결됨
                      else -> AutoConnectionType.DISCONNECT
                  }.apply {
                      _connectType.value = this // update
                  }
              }
              // Observe car connecting state
              carConnection.type.observeForever(observer)
          }
      
          override fun destroy() {
              if (::carConnection.isInitialized) {
                  carConnection.type.removeObserver(observer) // 앱 종료 시 종료
              }
          }
      }


      Android Auto와 차량 간 데이터 & UI 흐름


      • 차량 Android Auto UI → MediaBrowser 연결 → onGetRoot() 호출 → Root tree ID 획득

        • ID 는 리스트 형태로 전달 가능하며, 각 ID 들은 하위 카테고리의 ID 가 됨

      • 차량 UI에서 카테고리 선택(하위 카테고리 ID) → onLoadChildren() 호출 → 카테고리에 해당 하는 MediaItem 리스트 반환

        • 전달한 MediaItem 은 Auto UI 상에 리스트로 노출됨(List or Grid)

      • Google Assistant 를 통한 검색 요청 → onSearch() 호출 → 검색한 결과 리스트 전달 → Auto 에 검색결과 노출

        • 검색 결과에 대한 분석은 Google Assistant 에서 처리하여 앱에 data 전달

          • "아이유의 너랑나 틀어줘"

            • artist: "아이유"

            • title: "너랑나"

      • 사용자가 곡 선택 → onPlayFromMediaId() 호출 → Player에 재생 요청

      • MediaSession → PlaybackState, Metadata 업데이트 → 차량 UI에 실시간 반영


      미디어 화면

      추천 리스트


      • Auto 메인화면에 노출

      • 추천목록은 앱내부에서 Control 할 수 없고 앱내부에서 설정한 리스트 아이템에서 재생가능한 아이템 중 임의로 pick 하여 노출

      확인 시 모든 카테고리의 첫번째, 두번째 item 을 모아서 노출되고 있으나, Auto 시스템 마다 다를 수 있음

      현재 재생 목록

      현재재생목록.png

      • MediaSession 을 통해 queue 에 media item 지정 시 보여지는 화면

      • title 도 별도 지정 가능


      Custom Category

      스크린샷 2025-05-15 오후 1.22.16.png

      • 하위 카테고리 Tree 구조를 가지는 화면

      • GRID/LIST type 으로 UI 표시 가능

        • Media Item 구성할 때 지정(Description)

      • Depth 의 지정은 제한되어 있지 않으나, Depth 가 클수록 사용자는 운전중 많은 interaction 이 필요하므로 지양 필요

      • 최대 카테고리 개수는 시스템에서 내려주며, 현재 확인 시 최대 4개. 4개 이상은 카테고리로 지정할 수 없음


      재생화면

      재생화면.png

      • Play, Next, Previous 이외의 버튼(e.g. Favorite, Suffle, Repeat mode 등)은 Custom Action 을 추가하여 가능

      // Add repeat custom action
      customActionState.repeatMode?.let { repeatMode ->
          addCustomAction(
              PlaybackStateCompat.CustomAction.Builder(
                  CUSTOM_ACTION_REPEAT,
                  context.getString(R.string.music_noti_repeat),
                  when (repeatMode) {
                      Repeat.NONE -> AdotIcons.System.Close.Line
                      Repeat.ONE -> AdotIcons.Media.RepeatOnce.Line
                      Repeat.ALL -> AdotIcons.Media.Repeat.Line
                  }
              )
                  .setExtras(bundleOf(CUSTOM_ACTION_REPEAT_EXTRA_CURRENT to repeatMode.name))
                  .build()
                  )
              } 

      Error 화면

      오류화면.png

      • MediaSession 을 통해 Error 상황을 사용자에게 노출할 수 있음

      mediaSession?.setPlaybackState(
          PlaybackStateCompat.Builder()
              .setState(
                  PlaybackStateCompat.STATE_ERROR, // error
                  0L,
                  0f,
              )
              .setErrorMessage(errorCode, message) // 사용자에게 노출됨 message 지정
              .build()
      )


      Troubleshooting

      Service 내 Binder 처리

      Auto System 에서 앱의 Service 에 Binding 하여 여러 요청을 진행하기 때문에 binder 객체를 반드시 전달 해야합니다.

      class AdotMusicService : MediaBrowserServiceCompat() {
          ...
          // exported 상태인 Service 이므로 Auto system 연결 시에만 Binding 가능하도록 함 
          override fun onBind(intent: Intent?): IBinder? = if (isCarMode) super.onBind(intent) else null
      }

      AudioFocus 처리

      Auto System 에서 Media App 으로 동작 시 음악 재생은 AudioManager.AUDIOFOCUS_GAIN 으로 요청되어야 Auto System 에서 Media 로 인식하여 올바르게 동작합니다.

      다른 Focus 로 요청 시에 Media 로 인식하지 않아 차량에서 Media 재생이 아닌 다른 형태로 재생사운드가 노출되고, 차량의 Media Control Key(핸들등에 존재하는 재생/이전/다음 버튼)가 동작하지 않습니다.

      Google Assistant Search

      onPlayFromSearch() - MediaSessionCompat.Callback

      Google Assistant 를 통한 발화동작으로 "아이유 노래 틀어줘" 와 같은 발화시도 시 앱으로 event 가 전달되게 설정하려면 Manifest 설정이 필요합니다.

      // Manifest 에 아래 intent-filter 등록을 하지 않으면 onPlayFromSearch() 호출이 되지 않는다.
      <activity
          android:name=".ui.MusicPlayerActivity"
          ...>
       
          <!-- MediaSession callback 에서 Google Assistant 를 통한
          onPlayFromSearch callback 을 받기 위해 추가. service 에 등록 시 미동작 -->
          <intent-filter>
              <action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" />
              <category android:name="android.intent.category.DEFAULT" />
          </intent-filter>
      </activity>

      Service 에 등록하면 onPlayFromSearch() 미호출. 아무 activity 에나 추가하면 동작됩니다.

      onSearch() - MediaBrowserServiceCompat's override function

      onGetRoot 에서 android.media.browse.SEARCH_SUPPORTED 를 bundle 에 포함하여 전달하는 경우 발화 시도 시 onSearch 함수가 호출됩니다.

      결과 지정 시 Player 화면에 '검색 결과' 버튼이 노출되며 클릭 시, 지정한 리스트가 노출됩니다.

      /**
       * onGetRoot 에서 [MediaConstants.BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED] 설정 시 호출됨
       * "검색결과" 항목이 Auto 의 Playback 에 생김
       * "아이유 노래 틀어줘" -> "아이유" query 가 넘어옴
       */
      override fun onSearch(
          query: String,
          extras: Bundle?,
          result: Result<MutableList<MediaBrowserCompat.MediaItem>>
      ) {
          // result parameter 에 검색결과 list 설정 필요
          result.sendResult(searchList)
      }

      검색결과.png


      Emulator 사용 방법

      차량에 항상 연결하여 개발을 진행하기엔 어려움이 존재합니다.

      그래서 Android 에서는 PC 환경에서 사용가능한 Desktop Head Unit(DHU) emulator 를 제공하고 있습니다.

      1. Android Studio 에서 DHU 설치

        DHU 설치.png

      2. 휴대폰에서 Android Auto 개발자 모드 활성화 + 헤드 유닛 서버 시작

        • 일반 개발자 모드 활성화와 같이 개발자 메뉴 활성화를 위해 'Android Auto' 설정의 '버전 정보' 여러번 클릭

        • 이후 우측 상단 메뉴 클릭 후 '헤드 유닛 서버 시작' 메뉴 선택

        • 정상 실행 시 헤드 유닛 서비스가 Notification 에 노출

          개발자모드.png

      3. CLI 를 통한 DHU 실행

      // port 지정(고정값)
      adb forward tcp:5277 tcp:5277
      
      // DHU 실행
      mac: /Users/xxx/Library/Android/sdk/extras/google/auto/desktop-head-unit
      windows: C:\Users\xxx\AppData\Local\Android\Sdk\extras\google\auto\desktop-head-unit

      DHU 실행 경로는 Android SDK 가 설치된 PC 환경마다 다르므로 확인이 필요합니다.


      참고자료

      https://developers.google.com/cars/design/android-auto?hl=ko

      https://developers.google.com/cars/design/design-foundations?hl=ko

      https://developer.android.com/training/cars/testing?hl=ko


      SKT 에이닷 앱의 Android Auto 연동 내부 개발 및 테스트 환경 기준으로 작성되었으며, 실제 상용 배포된 기능은 아닙니다. Android Auto 의 이해와 Media 서비스 개발 시 참고용으로 작성되었습니다.

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      ReturnZero 님의 최신 블로그

      더보기
      동영상 기고하기