diff --git a/ultrasonic/src/main/AndroidManifest.xml b/ultrasonic/src/main/AndroidManifest.xml
index f3a0d39c..f9abed6b 100644
--- a/ultrasonic/src/main/AndroidManifest.xml
+++ b/ultrasonic/src/main/AndroidManifest.xml
@@ -60,7 +60,7 @@
         </service>
 
         <service
-            android:name=".service.AutoMediaPlayerService"
+            android:name=".service.AutoMediaBrowserService"
             android:label="Ultrasonic Auto Media Player Service"
             android:exported="true">
 
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/ApplicationModule.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/ApplicationModule.kt
index e78404bf..47b08cde 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/ApplicationModule.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/ApplicationModule.kt
@@ -4,6 +4,7 @@ import org.koin.android.ext.koin.androidContext
 import org.koin.dsl.module
 import org.moire.ultrasonic.data.ActiveServerProvider
 import org.moire.ultrasonic.subsonic.ImageLoaderProvider
+import org.moire.ultrasonic.util.MediaSessionEventDistributor
 import org.moire.ultrasonic.util.NowPlayingEventDistributor
 import org.moire.ultrasonic.util.PermissionUtil
 import org.moire.ultrasonic.util.ThemeChangedEventDistributor
@@ -17,4 +18,5 @@ val applicationModule = module {
     single { PermissionUtil(androidContext()) }
     single { NowPlayingEventDistributor() }
     single { ThemeChangedEventDistributor() }
+    single { MediaSessionEventDistributor() }
 }
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/AutoMediaBrowserService.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/AutoMediaBrowserService.kt
new file mode 100644
index 00000000..6afcb29a
--- /dev/null
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/AutoMediaBrowserService.kt
@@ -0,0 +1,186 @@
+package org.moire.ultrasonic.service
+
+import android.os.Bundle
+import android.os.Handler
+import android.support.v4.media.MediaBrowserCompat
+import android.support.v4.media.MediaDescriptionCompat
+import android.support.v4.media.session.MediaSessionCompat
+import androidx.media.MediaBrowserServiceCompat
+import androidx.media.utils.MediaConstants
+import org.koin.android.ext.android.inject
+import org.moire.ultrasonic.util.MediaSessionEventDistributor
+import org.moire.ultrasonic.util.MediaSessionEventListener
+import timber.log.Timber
+
+
+const val MY_MEDIA_ROOT_ID = "MY_MEDIA_ROOT_ID"
+const val MY_MEDIA_ALBUM_ID = "MY_MEDIA_ALBUM_ID"
+const val MY_MEDIA_ARTIST_ID = "MY_MEDIA_ARTIST_ID"
+const val MY_MEDIA_ALBUM_ITEM = "MY_MEDIA_ALBUM_ITEM"
+const val MY_MEDIA_LIBRARY_ID = "MY_MEDIA_LIBRARY_ID"
+const val MY_MEDIA_PLAYLIST_ID = "MY_MEDIA_PLAYLIST_ID"
+
+class AutoMediaBrowserService : MediaBrowserServiceCompat() {
+
+    private lateinit var mediaSessionEventListener: MediaSessionEventListener
+    private val mediaSessionEventDistributor: MediaSessionEventDistributor by inject()
+    private val lifecycleSupport: MediaPlayerLifecycleSupport by inject()
+
+    override fun onCreate() {
+        super.onCreate()
+
+        mediaSessionEventListener = object : MediaSessionEventListener {
+            override fun onMediaSessionTokenCreated(token: MediaSessionCompat.Token) {
+                Timber.i("AutoMediaBrowserService onMediaSessionTokenCreated called")
+                if (sessionToken == null) {
+                    Timber.i("AutoMediaBrowserService onMediaSessionTokenCreated session token was null, set it to %s", token.toString())
+                    sessionToken = token
+                }
+            }
+
+            override fun onPlayFromMediaIdRequested(mediaId: String?, extras: Bundle?) {
+                // TODO implement
+                Timber.i("AutoMediaBrowserService onPlayFromMediaIdRequested called")
+            }
+
+            override fun onPlayFromSearchRequested(query: String?, extras: Bundle?) {
+                // TODO implement
+                Timber.i("AutoMediaBrowserService onPlayFromSearchRequested called")
+            }
+        }
+
+        mediaSessionEventDistributor.subscribe(mediaSessionEventListener)
+
+        val handler = Handler()
+        handler.postDelayed({
+            Timber.i("AutoMediaBrowserService starting lifecycleSupport and MediaPlayerService...")
+            // TODO it seems Android Auto handles autostart, but we must check that
+            lifecycleSupport.onCreate()
+            MediaPlayerService.getInstance()
+        }, 100)
+
+        Timber.i("AutoMediaBrowserService onCreate called")
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+        mediaSessionEventDistributor.unsubscribe(mediaSessionEventListener)
+        Timber.i("AutoMediaBrowserService onDestroy called")
+    }
+
+    override fun onGetRoot(
+        clientPackageName: String,
+        clientUid: Int,
+        rootHints: Bundle?
+    ): BrowserRoot? {
+        Timber.i("AutoMediaBrowserService onGetRoot called")
+
+        // TODO: The number of horizontal items available on the Andoid Auto screen. Check and handle.
+        val maximumRootChildLimit = rootHints!!.getInt(
+            MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT,
+            4
+        )
+
+        // TODO: The type of the horizontal items children on the Android Auto screen. Check and handle.
+        val supportedRootChildFlags = rootHints!!.getInt(
+            MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS,
+            MediaBrowserCompat.MediaItem.FLAG_BROWSABLE
+        )
+
+        val extras = Bundle()
+        extras.putInt(
+            MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE,
+            MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM)
+        extras.putInt(
+            MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE,
+            MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM)
+
+        return BrowserRoot(MY_MEDIA_ROOT_ID, extras)
+    }
+
+    override fun onLoadChildren(
+        parentId: String,
+        result: Result<MutableList<MediaBrowserCompat.MediaItem>>
+    ) {
+        Timber.i("AutoMediaBrowserService onLoadChildren called")
+
+        if (parentId == MY_MEDIA_ROOT_ID) {
+            return getRootItems(result)
+        } else {
+            return getAlbumLists(result)
+        }
+    }
+
+    override fun onSearch(
+        query: String,
+        extras: Bundle?,
+        result: Result<MutableList<MediaBrowserCompat.MediaItem>>
+    ) {
+        super.onSearch(query, extras, result)
+    }
+
+    private fun getRootItems(result: Result<MutableList<MediaBrowserCompat.MediaItem>>) {
+        val mediaItems: MutableList<MediaBrowserCompat.MediaItem> = ArrayList()
+
+        // TODO implement this with proper texts, icons, etc
+        mediaItems.add(
+            MediaBrowserCompat.MediaItem(
+                MediaDescriptionCompat.Builder()
+                    .setTitle("Library")
+                    .setMediaId(MY_MEDIA_LIBRARY_ID)
+                    .build(),
+                MediaBrowserCompat.MediaItem.FLAG_BROWSABLE
+            )
+        )
+
+        mediaItems.add(
+            MediaBrowserCompat.MediaItem(
+                MediaDescriptionCompat.Builder()
+                    .setTitle("Artists")
+                    .setMediaId(MY_MEDIA_ARTIST_ID)
+                    .build(),
+                MediaBrowserCompat.MediaItem.FLAG_BROWSABLE
+            )
+        )
+
+        mediaItems.add(
+            MediaBrowserCompat.MediaItem(
+                MediaDescriptionCompat.Builder()
+                    .setTitle("Albums")
+                    .setMediaId(MY_MEDIA_ALBUM_ID)
+                    .build(),
+                MediaBrowserCompat.MediaItem.FLAG_BROWSABLE
+            )
+        )
+
+        mediaItems.add(
+            MediaBrowserCompat.MediaItem(
+                MediaDescriptionCompat.Builder()
+                    .setTitle("Playlists")
+                    .setMediaId(MY_MEDIA_PLAYLIST_ID)
+                    .build(),
+                MediaBrowserCompat.MediaItem.FLAG_BROWSABLE
+            )
+        )
+
+        result.sendResult(mediaItems)
+    }
+
+    private fun getAlbumLists(result: Result<MutableList<MediaBrowserCompat.MediaItem>>) {
+        val mediaItems: MutableList<MediaBrowserCompat.MediaItem> = ArrayList()
+
+        val description = MediaDescriptionCompat.Builder()
+            .setTitle("Test")
+            .setMediaId(MY_MEDIA_ALBUM_ITEM + 1)
+            .build()
+
+        mediaItems.add(
+            MediaBrowserCompat.MediaItem(
+                description,
+                MediaBrowserCompat.MediaItem.FLAG_BROWSABLE
+            )
+        )
+
+        result.sendResult(mediaItems)
+    }
+}
\ No newline at end of file
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/LocalMediaPlayer.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/LocalMediaPlayer.kt
index 1de7bac3..005967b9 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/LocalMediaPlayer.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/LocalMediaPlayer.kt
@@ -171,7 +171,7 @@ class LocalMediaPlayer(
             val mainHandler = Handler(context.mainLooper)
 
             val myRunnable = Runnable {
-                onPlayerStateChanged!!(playerState, currentPlaying)
+                onPlayerStateChanged?.invoke(playerState, currentPlaying)
             }
             mainHandler.post(myRunnable)
         }
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt
index 101a8f5d..493591fc 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/service/MediaPlayerService.kt
@@ -14,14 +14,13 @@ import android.content.Intent
 import android.os.Build
 import android.os.Bundle
 import android.os.IBinder
-import android.support.v4.media.MediaBrowserCompat
+import android.support.v4.media.MediaDescriptionCompat
 import android.support.v4.media.MediaMetadataCompat
 import android.support.v4.media.session.MediaSessionCompat
 import android.support.v4.media.session.PlaybackStateCompat
 import android.view.KeyEvent
 import androidx.core.app.NotificationCompat
 import androidx.core.app.NotificationManagerCompat
-import androidx.media.MediaBrowserServiceCompat
 import kotlin.collections.ArrayList
 import org.koin.android.ext.android.inject
 import org.moire.ultrasonic.R
@@ -37,7 +36,12 @@ import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X3
 import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X4
 import org.moire.ultrasonic.receiver.MediaButtonIntentReceiver
 import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
-import org.moire.ultrasonic.util.*
+import org.moire.ultrasonic.util.Constants
+import org.moire.ultrasonic.util.MediaSessionEventDistributor
+import org.moire.ultrasonic.util.NowPlayingEventDistributor
+import org.moire.ultrasonic.util.ShufflePlayBuffer
+import org.moire.ultrasonic.util.SimpleServiceBinder
+import org.moire.ultrasonic.util.Util
 import timber.log.Timber
 
 /**
@@ -56,9 +60,10 @@ class MediaPlayerService : Service() {
     private val localMediaPlayer by inject<LocalMediaPlayer>()
     private val nowPlayingEventDistributor by inject<NowPlayingEventDistributor>()
     private val mediaPlayerLifecycleSupport by inject<MediaPlayerLifecycleSupport>()
+    private val mediaSessionEventDistributor: MediaSessionEventDistributor by inject()
 
     private var mediaSession: MediaSessionCompat? = null
-    private var mediaSessionToken: MediaSessionCompat.Token? = null
+    var mediaSessionToken: MediaSessionCompat.Token? = null
     private var isInForeground = false
     private var notificationBuilder: NotificationCompat.Builder? = null
 
@@ -91,6 +96,12 @@ class MediaPlayerService : Service() {
 
         localMediaPlayer.onNextSongRequested = Runnable { setNextPlaying() }
 
+        // TODO maybe MediaSession must be in an independent class after all...
+        // It seems this must be initialized in the stopped state too, e.g. for Android Auto.
+        // So it is best to init this early.
+        initMediaSessions()
+        updateMediaSession(null, PlayerState.IDLE)
+
         // Create Notification Channel
         createNotificationChannel()
 
@@ -113,6 +124,8 @@ class MediaPlayerService : Service() {
             localMediaPlayer.release()
             downloader.stop()
             shufflePlayBuffer.onDestroy()
+
+            mediaSessionEventDistributor.ReleaseCachedMediaSessionToken()
             mediaSession?.release()
             mediaSession = null
         } catch (ignored: Throwable) {
@@ -467,7 +480,7 @@ class MediaPlayerService : Service() {
     fun updateMediaSession(currentPlaying: DownloadFile?, playerState: PlayerState) {
         Timber.d("Updating the MediaSession")
 
-        if (mediaSession == null) initMediaSessions()
+        val playbackState = PlaybackStateCompat.Builder()
 
         // Set Metadata
         val metadata = MediaMetadataCompat.Builder()
@@ -483,6 +496,9 @@ class MediaPlayerService : Service() {
                 metadata.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, song.album)
                 metadata.putString(MediaMetadataCompat.METADATA_KEY_TITLE, song.title)
                 metadata.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, cover)
+
+                playbackState.setActiveQueueItemId(downloader.currentPlayingIndex.toLong())
+
             } catch (e: Exception) {
                 Timber.e(e, "Error setting the metadata")
             }
@@ -492,13 +508,15 @@ class MediaPlayerService : Service() {
         mediaSession!!.setMetadata(metadata.build())
 
         // Create playback State
-        val playbackState = PlaybackStateCompat.Builder()
         val state: Int
         val isActive: Boolean
 
         var actions: Long = PlaybackStateCompat.ACTION_PLAY_PAUSE or
             PlaybackStateCompat.ACTION_SKIP_TO_NEXT or
-            PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
+            PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS or
+            PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID or
+            PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH or
+            PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM
 
         // Map our playerState to native PlaybackState
         // TODO: Synchronize these APIs
@@ -534,7 +552,8 @@ class MediaPlayerService : Service() {
             }
         }
 
-        playbackState.setState(state, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1.0f)
+        // TODO playerPosition should be updated more frequently (currently this function is called only when the playing track changes)
+        playbackState.setState(state, playerPosition.toLong(), 1.0f)
 
         // Set actions
         playbackState.setActions(actions)
@@ -545,6 +564,14 @@ class MediaPlayerService : Service() {
         // Set Active state
         mediaSession!!.isActive = isActive
 
+        // TODO Implement Now Playing queue handling properly
+        mediaSession!!.setQueueTitle("Now Playing")
+        mediaSession!!.setQueue(downloader.downloadList.mapIndexed { id, file ->
+            MediaSessionCompat.QueueItem(MediaDescriptionCompat.Builder()
+                .setTitle(file.song.title)
+                .build(), id.toLong())
+        })
+
         Timber.d("Setting the MediaSession to active = %s", isActive)
     }
 
@@ -795,6 +822,7 @@ class MediaPlayerService : Service() {
 
         mediaSession = MediaSessionCompat(applicationContext, "UltrasonicService")
         mediaSessionToken = mediaSession!!.sessionToken
+        mediaSessionEventDistributor.RaiseMediaSessionTokenCreatedEvent(mediaSessionToken!!)
 
         updateMediaButtonReceiver()
 
@@ -810,35 +838,21 @@ class MediaPlayerService : Service() {
 
                 Timber.v("Media Session Callback: onPlay")
             }
-/*
+
             override fun onPlayFromMediaId(mediaId: String?, extras: Bundle?) {
                 super.onPlayFromMediaId(mediaId, extras)
 
-                val result = autoMediaBrowser!!.getBundleData(extras)
-                if (result != null) {
-                    val mediaId = result.first
-                    val directoryList = result.second
-
-                    resetPlayback()
-                    val songs: MutableList<MusicDirectory.Entry> = mutableListOf()
-                    var found = false
-                    for (item in directoryList) {
-                        if (found || item.id == mediaId) {
-                            found = true
-                            songs.add(item)
-                        }
-                    }
-                    downloader.download(songs, false, false, false, true)
-
-                    getPendingIntentForMediaAction(
-                        applicationContext,
-                        KeyEvent.KEYCODE_MEDIA_PLAY,
-                        keycode
-                    ).send()
-                }
-                Timber.v("Media Session Callback: onPlayFromMediaId")
+                Timber.d("Media Session Callback: onPlayFromMediaId")
+                mediaSessionEventDistributor.RaisePlayFromMediaIdRequestedEvent(mediaId, extras)
             }
-*/
+
+            override fun onPlayFromSearch(query: String?, extras: Bundle?) {
+                super.onPlayFromSearch(query, extras)
+
+                Timber.d("Media Session Callback: onPlayFromSearch")
+                mediaSessionEventDistributor.RaisePlayFromSearchRequestedEvent(query, extras)
+            }
+
             override fun onPause() {
                 super.onPause()
                 getPendingIntentForMediaAction(
@@ -886,6 +900,11 @@ class MediaPlayerService : Service() {
                 mediaPlayerLifecycleSupport.handleKeyEvent(event)
                 return true
             }
+
+            override fun onSkipToQueueItem(id: Long) {
+                super.onSkipToQueueItem(id)
+                play(id.toInt())
+            }
         }
         )
     }
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/MediaSessionTokenCreatedEventDistributor.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/MediaSessionTokenCreatedEventDistributor.kt
new file mode 100644
index 00000000..f90470a4
--- /dev/null
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/MediaSessionTokenCreatedEventDistributor.kt
@@ -0,0 +1,49 @@
+package org.moire.ultrasonic.util
+
+import android.os.Bundle
+import android.support.v4.media.session.MediaSessionCompat
+
+/**
+ * This class distributes MediaSession related events to its subscribers.
+ * It is a primitive implementation of a pub-sub event bus
+ */
+class MediaSessionEventDistributor {
+    var eventListenerList: MutableList<MediaSessionEventListener> =
+        listOf<MediaSessionEventListener>().toMutableList()
+
+    var cachedToken: MediaSessionCompat.Token? = null
+
+    fun subscribe(listener: MediaSessionEventListener) {
+        eventListenerList.add(listener)
+
+        synchronized(this) {
+            if (cachedToken != null)
+                listener.onMediaSessionTokenCreated(cachedToken!!)
+        }
+    }
+
+    fun unsubscribe(listener: MediaSessionEventListener) {
+        eventListenerList.remove(listener)
+    }
+
+    fun ReleaseCachedMediaSessionToken() {
+        synchronized(this) {
+            cachedToken = null
+        }
+    }
+
+    fun RaiseMediaSessionTokenCreatedEvent(token: MediaSessionCompat.Token) {
+        synchronized(this) {
+            cachedToken = token
+            eventListenerList.forEach { listener -> listener.onMediaSessionTokenCreated(token) }
+        }
+    }
+
+    fun RaisePlayFromMediaIdRequestedEvent(mediaId: String?, extras: Bundle?) {
+        eventListenerList.forEach { listener -> listener.onPlayFromMediaIdRequested(mediaId, extras) }
+    }
+
+    fun RaisePlayFromSearchRequestedEvent(query: String?, extras: Bundle?) {
+        eventListenerList.forEach { listener -> listener.onPlayFromSearchRequested(query, extras) }
+    }
+}
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/MediaSessionTokenCreatedEventListener.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/MediaSessionTokenCreatedEventListener.kt
new file mode 100644
index 00000000..1b1c922d
--- /dev/null
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/MediaSessionTokenCreatedEventListener.kt
@@ -0,0 +1,13 @@
+package org.moire.ultrasonic.util
+
+import android.os.Bundle
+import android.support.v4.media.session.MediaSessionCompat
+
+/**
+ * Callback interface for MediaSession related event subscribers
+ */
+interface MediaSessionEventListener {
+    fun onMediaSessionTokenCreated(token: MediaSessionCompat.Token)
+    fun onPlayFromMediaIdRequested(mediaId: String?, extras: Bundle?)
+    fun onPlayFromSearchRequested(query: String?, extras: Bundle?)
+}