OSDN Git Service

Add persistent state for Bluetooth high quality audio support
authorAntony Sargent <asargent@google.com>
Thu, 27 Apr 2017 00:18:23 +0000 (17:18 -0700)
committerAntony Sargent <asargent@google.com>
Tue, 2 May 2017 21:37:18 +0000 (14:37 -0700)
This CL implements the storage/retrieval of flags for whether Bluetooth
A2DP sink devices support optional codecs and if they should be turned
on when that device is connected. It also contains code to listen for
device connections and do two things: (1) save whether that device
supports optional codecs, and (2) either enable or disable those codecs
for playback if we had a stored preference for the device.

Bug=37441685
Test: manually

Change-Id: I5f3113342d53aeeb9ede68602da8c7ff8c853457

src/com/android/bluetooth/a2dp/A2dpService.java

index f42b594..187a139 100755 (executable)
 
 package com.android.bluetooth.a2dp;
 
+import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothCodecConfig;
 import android.bluetooth.BluetoothCodecStatus;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothUuid;
 import android.bluetooth.IBluetoothA2dp;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.os.ParcelUuid;
 import android.provider.Settings;
 import android.util.Log;
@@ -42,6 +47,46 @@ public class A2dpService extends ProfileService {
 
     private A2dpStateMachine mStateMachine;
     private Avrcp mAvrcp;
+
+    private BroadcastReceiver mConnectionStateChangedReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) {
+                return;
+            }
+            int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
+            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+            if (state != BluetoothProfile.STATE_CONNECTED || device == null) {
+                return;
+            }
+            // Each time a device connects, we want to re-check if it supports optional
+            // codecs (perhaps it's had a firmware update, etc.) and save that state if
+            // it differs from what we had saved before.
+            int previousSupport = getSupportsOptionalCodecs(device);
+            boolean supportsOptional = false;
+            for (BluetoothCodecConfig config :
+                    mStateMachine.getCodecStatus().getCodecsSelectableCapabilities()) {
+                if (!config.isMandatoryCodec()) {
+                    supportsOptional = true;
+                    break;
+                }
+            }
+            if (previousSupport == BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN
+                    || supportsOptional
+                            != (previousSupport == BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED)) {
+                setSupportsOptionalCodecs(device, supportsOptional);
+            }
+            if (supportsOptional) {
+                int enabled = getOptionalCodecsEnabled(device);
+                if (enabled == BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
+                    enableOptionalCodecs();
+                } else if (enabled == BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED) {
+                    disableOptionalCodecs();
+                }
+            }
+        }
+    };
+
     private static A2dpService sAd2dpService;
     static final ParcelUuid[] A2DP_SOURCE_UUID = {
         BluetoothUuid.AudioSource
@@ -63,6 +108,9 @@ public class A2dpService extends ProfileService {
         mAvrcp = Avrcp.make(this);
         mStateMachine = A2dpStateMachine.make(this, this);
         setA2dpService(this);
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
+        registerReceiver(mConnectionStateChangedReceiver, filter);
         return true;
     }
 
@@ -77,6 +125,7 @@ public class A2dpService extends ProfileService {
     }
 
     protected boolean cleanup() {
+        unregisterReceiver(mConnectionStateChangedReceiver);
         if (mStateMachine!= null) {
             mStateMachine.cleanup();
         }
@@ -249,6 +298,43 @@ public class A2dpService extends ProfileService {
         mStateMachine.disableOptionalCodecs();
     }
 
+    public int getSupportsOptionalCodecs(BluetoothDevice device) {
+        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
+        int support = Settings.Global.getInt(getContentResolver(),
+                Settings.Global.getBluetoothA2dpSupportsOptionalCodecsKey(device.getAddress()),
+                BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN);
+        return support;
+    }
+
+    public void setSupportsOptionalCodecs(BluetoothDevice device, boolean doesSupport) {
+        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
+        int value = doesSupport ? BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED
+                                : BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED;
+        Settings.Global.putInt(getContentResolver(),
+                Settings.Global.getBluetoothA2dpSupportsOptionalCodecsKey(device.getAddress()),
+                value);
+    }
+
+    public int getOptionalCodecsEnabled(BluetoothDevice device) {
+        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
+        return Settings.Global.getInt(getContentResolver(),
+                Settings.Global.getBluetoothA2dpOptionalCodecsEnabledKey(device.getAddress()),
+                BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN);
+    }
+
+    public void setOptionalCodecsEnabled(BluetoothDevice device, int value) {
+        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
+        if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN
+                && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED
+                && value != BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
+            Log.w(TAG, "Unexpected value passed to setOptionalCodecsEnabled:" + value);
+            return;
+        }
+        Settings.Global.putInt(getContentResolver(),
+                Settings.Global.getBluetoothA2dpOptionalCodecsEnabledKey(device.getAddress()),
+                value);
+    }
+
     //Binder object: Must be static class or memory leak may occur 
     private static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub 
         implements IProfileServiceBinder {
@@ -364,6 +450,24 @@ public class A2dpService extends ProfileService {
             if (service == null) return;
             service.disableOptionalCodecs();
         }
+
+        public int supportsOptionalCodecs(BluetoothDevice device) {
+            A2dpService service = getService();
+            if (service == null) return BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN;
+            return service.getSupportsOptionalCodecs(device);
+        }
+
+        public int getOptionalCodecsEnabled(BluetoothDevice device) {
+            A2dpService service = getService();
+            if (service == null) return BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN;
+            return service.getOptionalCodecsEnabled(device);
+        }
+
+        public void setOptionalCodecsEnabled(BluetoothDevice device, int value) {
+            A2dpService service = getService();
+            if (service == null) return;
+            service.setOptionalCodecsEnabled(device, value);
+        }
     };
 
     @Override