Add bluetooth prefs for Android Automotive

Add extra to BluetoothMediaBrowserService to redirect to settings when
Bluetooth is not connected

Fixes: 134514401
Test: verify extra in the media session state at startup and after
disconnection.

Change-Id: Iffbd6704eb82e02233b738c7ee06f5c4d8e3c0b5
(cherry picked from commit 6b9199e0548439300d41d70c7915012e2434ab78)

Merged-In: Iffbd6704eb82e02233b738c7ee06f5c4d8e3c0b5
Change-Id: Ie5364fa2c0339c81e4444548d5e132caad789994
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 8ce4707..6de6114 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -320,6 +320,16 @@
                 <action android:name="android.media.browse.MediaBrowserService" />
             </intent-filter>
         </service>
+
+        <activity
+            android:name=".BluetoothPrefs"
+            android:exported="@bool/profile_supported_a2dp_sink"
+            android:enabled="@bool/profile_supported_a2dp_sink">
+            <intent-filter>
+                <action android:name="android.intent.action.APPLICATION_PREFERENCES"/>
+            </intent-filter>
+        </activity>
+
         <service
             android:process="@string/process"
             android:name = ".avrcp.AvrcpTargetService"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1389e0d..0204f98 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -249,4 +249,5 @@
     <string name="bluetooth_disconnected">Bluetooth audio disconnected"</string>
     <string name="a2dp_sink_mbs_label">Bluetooth Audio</string>
     <string name="bluetooth_opp_file_limit_exceeded">Files bigger than 4GB cannot be transferred</string>
+    <string name="bluetooth_connect_action">Connect to Bluetooth</string>
 </resources>
diff --git a/src/com/android/bluetooth/BluetoothPrefs.java b/src/com/android/bluetooth/BluetoothPrefs.java
new file mode 100644
index 0000000..2c7c87a
--- /dev/null
+++ b/src/com/android/bluetooth/BluetoothPrefs.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * Activity that routes to Bluetooth settings when launched
+ */
+public class BluetoothPrefs extends Activity {
+
+    public static final String BLUETOOTH_SETTING_ACTION = "android.settings.BLUETOOTH_SETTINGS";
+    public static final String BLUETOOTH_SETTING_CATEGORY = "android.intent.category.DEFAULT";
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Intent launchIntent = new Intent();
+        launchIntent.setAction(BLUETOOTH_SETTING_ACTION);
+        launchIntent.addCategory(BLUETOOTH_SETTING_CATEGORY);
+        startActivity(launchIntent);
+        finish();
+    }
+}
diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
index 23f1ce7..ae94a4d 100644
--- a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
+++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
@@ -233,11 +233,7 @@
         mAddressedPlayer.updateCurrentTrack(null);
         mBrowseTree.mNowPlayingNode.setCached(false);
         BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode);
-        PlaybackStateCompat.Builder pbb = new PlaybackStateCompat.Builder();
-        pbb.setState(PlaybackStateCompat.STATE_ERROR, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN,
-                1.0f).setActions(0);
-        pbb.setErrorMessage(mService.getString(R.string.bluetooth_disconnected));
-        BluetoothMediaBrowserService.notifyChanged(pbb.build());
+        BluetoothMediaBrowserService.addressedPlayerChanged(null);
         mService.sBrowseTree.mRootNode.removeChild(
                 mBrowseTree.mRootNode);
         BluetoothMediaBrowserService.notifyChanged(mService
diff --git a/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java b/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java
index b8a3337..a0b1224 100644
--- a/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java
+++ b/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java
@@ -16,6 +16,8 @@
 
 package com.android.bluetooth.avrcpcontroller;
 
+import android.app.PendingIntent;
+import android.content.Intent;
 import android.media.MediaMetadata;
 import android.media.browse.MediaBrowser.MediaItem;
 import android.os.Bundle;
@@ -29,6 +31,7 @@
 
 import androidx.media.MediaBrowserServiceCompat;
 
+import com.android.bluetooth.BluetoothPrefs;
 import com.android.bluetooth.R;
 
 import java.util.ArrayList;
@@ -60,6 +63,12 @@
     // Browsing related structures.
     private List<MediaSessionCompat.QueueItem> mMediaQueue = new ArrayList<>();
 
+    // Error messaging extras
+    public static final String ERROR_RESOLUTION_ACTION_INTENT =
+            "android.media.extras.ERROR_RESOLUTION_ACTION_INTENT";
+    public static final String ERROR_RESOLUTION_ACTION_LABEL =
+            "android.media.extras.ERROR_RESOLUTION_ACTION_LABEL";
+
     /**
      * Initialize this BluetoothMediaBrowserService, creating our MediaSessionCompat, MediaPlayer
      * and MediaMetaData, and setting up mechanisms to talk with the AvrcpControllerService.
@@ -76,11 +85,7 @@
                 | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
         mSession.setQueueTitle(getString(R.string.bluetooth_a2dp_sink_queue_name));
         mSession.setQueue(mMediaQueue);
-        PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder();
-        playbackStateBuilder.setState(PlaybackStateCompat.STATE_ERROR,
-                PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1.0f).setActions(0);
-        playbackStateBuilder.setErrorMessage(getString(R.string.bluetooth_disconnected));
-        mSession.setPlaybackState(playbackStateBuilder.build());
+        setErrorPlaybackState();
         sBluetoothMediaBrowserService = this;
     }
 
@@ -94,6 +99,24 @@
         }
     }
 
+    private void setErrorPlaybackState() {
+        Bundle extras = new Bundle();
+        extras.putString(ERROR_RESOLUTION_ACTION_LABEL,
+                getString(R.string.bluetooth_connect_action));
+        Intent launchIntent = new Intent();
+        launchIntent.setAction(BluetoothPrefs.BLUETOOTH_SETTING_ACTION);
+        launchIntent.addCategory(BluetoothPrefs.BLUETOOTH_SETTING_CATEGORY);
+        PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0,
+                launchIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+        extras.putParcelable(ERROR_RESOLUTION_ACTION_INTENT, pendingIntent);
+        PlaybackStateCompat errorState = new PlaybackStateCompat.Builder()
+                .setErrorMessage(getString(R.string.bluetooth_disconnected))
+                .setExtras(extras)
+                .setState(PlaybackStateCompat.STATE_ERROR, 0, 0)
+                .build();
+        mSession.setPlaybackState(errorState);
+    }
+
     @Override
     public synchronized void onLoadChildren(final String parentMediaId,
             final Result<List<MediaBrowserCompat.MediaItem>> result) {
@@ -138,6 +161,9 @@
 
     static synchronized void addressedPlayerChanged(MediaSessionCompat.Callback callback) {
         if (sBluetoothMediaBrowserService != null) {
+            if (callback == null) {
+                sBluetoothMediaBrowserService.setErrorPlaybackState();
+            }
             sBluetoothMediaBrowserService.mSession.setCallback(callback);
         } else {
             Log.w(TAG, "addressedPlayerChanged Unavailable");