Update the A2DP Codec Config API

Previously, the JNI upcall would contain only the current codec config.
In the new API, the upcall contains:
 1. The current codec config
 2. The list of codecs containing the local codecs capabilities
 3. The list of codecs containing the selectable codecs capabilities.
    This list is the intersection of the local codecs capabilities
    and the capabilities of the paired device.

Also, refactored the Java internals to accomodate the extra information:
 * Added new class BluetoothCodecStatus that contains the extra info:
   current codec config, local codecs capabilities and selectable
   codecs capabilities
 * Renamed method getCodecConfig() to getCodecStatus() and return the
   corresponding BluetoothCodecStatus object.
 * Updates to class BluetoothCodecConfig:
   new methods isValid(), getCodecName(), and updated toString()
   so it is more user friendly
 * Removed BluetoothCodecConfig.EXTRA_CODEC_CONFIG and
   EXTRA_PREVIOUS_CODEC_CONFIG.
   The former is superseded by BluetoothCodecStatus.EXTRA_CODEC_STATUS;
   the latter is not really used.

Test: A2DP streaming with headsets and switching the codecs
Change-Id: Ia1af2c22e521e863e28a360610aca49f7e62d31b
diff --git a/compiled-classes-phone b/compiled-classes-phone
index ec3371e..2cbfc22 100644
--- a/compiled-classes-phone
+++ b/compiled-classes-phone
@@ -633,6 +633,7 @@
 android.bluetooth.BluetoothClass
 android.bluetooth.BluetoothClass$1
 android.bluetooth.BluetoothCodecConfig
+android.bluetooth.BluetoothCodecStatus
 android.bluetooth.BluetoothDevice
 android.bluetooth.BluetoothDevice$1
 android.bluetooth.BluetoothDevice$2
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 1165fce..4960159 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -105,10 +105,9 @@
      * Intent used to broadcast the change in the Audio Codec state of the
      * A2DP Source profile.
      *
-     * <p>This intent will have 3 extras:
+     * <p>This intent will have 2 extras:
      * <ul>
-     *   <li> {@link #EXTRA_CODEC_CONFIG} - The current codec configuration. </li>
-     *   <li> {@link #EXTRA_PREVIOUS_CODEC_CONFIG} - The previous codec configuration. </li>
+     *   <li> {@link BluetoothCodecStatus#EXTRA_CODEC_STATUS} - The codec status. </li>
      *   <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device if the device is currently
      *   connected, otherwise it is not included.</li>
      * </ul>
@@ -564,24 +563,24 @@
     }
 
     /**
-     * Gets the current codec configuration.
+     * Gets the current codec status (configuration and capability).
      *
-     * @return the current codec configuration
+     * @return the current codec status
      * @hide
      */
-    public BluetoothCodecConfig getCodecConfig() {
-        if (DBG) Log.d(TAG, "getCodecConfig");
+    public BluetoothCodecStatus getCodecStatus() {
+        if (DBG) Log.d(TAG, "getCodecStatus");
         try {
             mServiceLock.readLock().lock();
             if (mService != null && isEnabled()) {
-                return mService.getCodecConfig();
+                return mService.getCodecStatus();
             }
             if (mService == null) {
                 Log.w(TAG, "Proxy not attached to service");
             }
             return null;
         } catch (RemoteException e) {
-            Log.e(TAG, "Error talking to BT service in getCodecConfig()", e);
+            Log.e(TAG, "Error talking to BT service in getCodecStatus()", e);
             return null;
         } finally {
             mServiceLock.readLock().unlock();
diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java
index a37a0b3..a482103 100644
--- a/core/java/android/bluetooth/BluetoothCodecConfig.java
+++ b/core/java/android/bluetooth/BluetoothCodecConfig.java
@@ -29,24 +29,6 @@
  * {@hide}
  */
 public final class BluetoothCodecConfig implements Parcelable {
-
-    /**
-     * Extra for the codec configuration intents of the individual profiles.
-     *
-     * This extra represents the current codec configuration of the A2DP
-     * profile.
-     */
-    public static final String EXTRA_CODEC_CONFIG = "android.bluetooth.codec.extra.CODEC_CONFIG";
-
-    /**
-     * Extra for the codec configuration intents of the individual profiles.
-     *
-     * This extra represents the previous codec configuration of the A2DP
-     * profile.
-     */
-    public static final String EXTRA_PREVIOUS_CODEC_CONFIG =
-        "android.bluetooth.codec.extra.PREVIOUS_CODEC_CONFIG";
-
     // Add an entry for each source codec here.
     // NOTE: The values should be same as those listed in the following file:
     //   hardware/libhardware/include/hardware/bt_av.h
@@ -128,13 +110,93 @@
                             mCodecSpecific2, mCodecSpecific3, mCodecSpecific4);
     }
 
+    /**
+     * Checks whether the object contains valid codec configuration.
+     *
+     * @return true if the object contains valid codec configuration,
+     * otherwise false.
+     */
+    public boolean isValid() {
+        return (mSampleRate != SAMPLE_RATE_NONE) &&
+            (mBitsPerSample != BITS_PER_SAMPLE_NONE) &&
+            (mChannelMode != CHANNEL_MODE_NONE);
+    }
+
+    /**
+     * Adds capability string to an existing string.
+     *
+     * @param prevStr the previous string with the capabilities. Can be
+     * a null pointer.
+     * @param capStr the capability string to append to prevStr argument.
+     * @return the result string in the form "prevStr|capStr".
+     */
+    private static String appendCapabilityToString(String prevStr,
+                                                   String capStr) {
+        if (prevStr == null) {
+            return capStr;
+        }
+        return prevStr + "|" + capStr;
+    }
+
     @Override
     public String toString() {
-        return "{mCodecType:" + mCodecType +
+        String sampleRateStr = null;
+        if (mSampleRate == SAMPLE_RATE_NONE) {
+            sampleRateStr = appendCapabilityToString(sampleRateStr, "NONE");
+        }
+        if ((mSampleRate & SAMPLE_RATE_44100) != 0) {
+            sampleRateStr = appendCapabilityToString(sampleRateStr, "44100");
+        }
+        if ((mSampleRate & SAMPLE_RATE_48000) != 0) {
+            sampleRateStr = appendCapabilityToString(sampleRateStr, "48000");
+        }
+        if ((mSampleRate & SAMPLE_RATE_88200) != 0) {
+            sampleRateStr = appendCapabilityToString(sampleRateStr, "88200");
+        }
+        if ((mSampleRate & SAMPLE_RATE_96000) != 0) {
+            sampleRateStr = appendCapabilityToString(sampleRateStr, "96000");
+        }
+        if ((mSampleRate & SAMPLE_RATE_176400) != 0) {
+            sampleRateStr = appendCapabilityToString(sampleRateStr, "176400");
+        }
+        if ((mSampleRate & SAMPLE_RATE_192000) != 0) {
+            sampleRateStr = appendCapabilityToString(sampleRateStr, "192000");
+        }
+
+        String bitsPerSampleStr = null;
+        if (mBitsPerSample == BITS_PER_SAMPLE_NONE) {
+            bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "NONE");
+        }
+        if ((mBitsPerSample & BITS_PER_SAMPLE_16) != 0) {
+            bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "16");
+        }
+        if ((mBitsPerSample & BITS_PER_SAMPLE_24) != 0) {
+            bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "24");
+        }
+        if ((mBitsPerSample & BITS_PER_SAMPLE_32) != 0) {
+            bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "32");
+        }
+
+        String channelModeStr = null;
+        if (mChannelMode == CHANNEL_MODE_NONE) {
+            channelModeStr = appendCapabilityToString(channelModeStr, "NONE");
+        }
+        if ((mChannelMode & CHANNEL_MODE_MONO) != 0) {
+            channelModeStr = appendCapabilityToString(channelModeStr, "MONO");
+        }
+        if ((mChannelMode & CHANNEL_MODE_STEREO) != 0) {
+            channelModeStr = appendCapabilityToString(channelModeStr, "STEREO");
+        }
+
+        return "{codecName:" + getCodecName() +
+            ",mCodecType:" + mCodecType +
             ",mCodecPriority:" + mCodecPriority +
             ",mSampleRate:" + String.format("0x%x", mSampleRate) +
+            "(" + sampleRateStr + ")" +
             ",mBitsPerSample:" + String.format("0x%x", mBitsPerSample) +
+            "(" + bitsPerSampleStr + ")" +
             ",mChannelMode:" + String.format("0x%x", mChannelMode) +
+            "(" + channelModeStr + ")" +
             ",mCodecSpecific1:" + mCodecSpecific1 +
             ",mCodecSpecific2:" + mCodecSpecific2 +
             ",mCodecSpecific3:" + mCodecSpecific3 +
@@ -181,7 +243,32 @@
     }
 
     /**
-     * Returns the codec type.
+     * Gets the codec name.
+     *
+     * @return the codec name
+     */
+    public String getCodecName() {
+        switch (mCodecType) {
+        case SOURCE_CODEC_TYPE_SBC:
+            return "SBC";
+        case SOURCE_CODEC_TYPE_AAC:
+            return "AAC";
+        case SOURCE_CODEC_TYPE_APTX:
+            return "aptX";
+        case SOURCE_CODEC_TYPE_APTX_HD:
+            return "aptX HD";
+        case SOURCE_CODEC_TYPE_LDAC:
+            return "LDAC";
+        case SOURCE_CODEC_TYPE_INVALID:
+            return "INVALID CODEC";
+        default:
+            break;
+        }
+        return "UNKNOWN CODEC(" + mCodecType + ")";
+    }
+
+    /**
+     * Gets the codec type.
      * See {@link android.bluetooth.BluetoothCodecConfig#SOURCE_CODEC_TYPE_SBC}.
      *
      * @return the codec type
@@ -191,7 +278,7 @@
     }
 
     /**
-     * Returns the codec selection priority.
+     * Gets the codec selection priority.
      * The codec selection priority is relative to other codecs: larger value
      * means higher priority. If 0, reset to default.
      *
@@ -202,7 +289,7 @@
     }
 
     /**
-     * Returns the codec sample rate. The value can be a bitmask with all
+     * Gets the codec sample rate. The value can be a bitmask with all
      * supported sample rates:
      * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_NONE} or
      * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_44100} or
@@ -219,7 +306,7 @@
     }
 
     /**
-     * Returns the codec bits per sample. The value can be a bitmask with all
+     * Gets the codec bits per sample. The value can be a bitmask with all
      * bits per sample supported:
      * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_NONE} or
      * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_16} or
@@ -233,7 +320,7 @@
     }
 
     /**
-     * Returns the codec channel mode. The value can be a bitmask with all
+     * Gets the codec channel mode. The value can be a bitmask with all
      * supported channel modes:
      * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_NONE} or
      * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_MONO} or
@@ -246,7 +333,7 @@
     }
 
     /**
-     * Returns a codec specific value1.
+     * Gets a codec specific value1.
      *
      * @return a codec specific value1.
      */
@@ -255,7 +342,7 @@
     }
 
     /**
-     * Returns a codec specific value2.
+     * Gets a codec specific value2.
      *
      * @return a codec specific value2
      */
@@ -264,7 +351,7 @@
     }
 
     /**
-     * Returns a codec specific value3.
+     * Gets a codec specific value3.
      *
      * @return a codec specific value3
      */
@@ -273,7 +360,7 @@
     }
 
     /**
-     * Returns a codec specific value4.
+     * Gets a codec specific value4.
      *
      * @return a codec specific value4
      */
diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.aidl b/core/java/android/bluetooth/BluetoothCodecStatus.aidl
new file mode 100644
index 0000000..f9c3a3d
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothCodecStatus.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 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 android.bluetooth;
+
+parcelable BluetoothCodecStatus;
diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java
new file mode 100644
index 0000000..c8cd8d1
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothCodecStatus.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2017 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 android.bluetooth;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Represents the codec status (configuration and capability) for a Bluetooth
+ * A2DP source device.
+ *
+ * {@see BluetoothA2dp}
+ *
+ * {@hide}
+ */
+public final class BluetoothCodecStatus implements Parcelable {
+    /**
+     * Extra for the codec configuration intents of the individual profiles.
+     *
+     * This extra represents the current codec status of the A2DP
+     * profile.
+     */
+    public static final String EXTRA_CODEC_STATUS =
+        "android.bluetooth.codec.extra.CODEC_STATUS";
+
+    private final BluetoothCodecConfig mCodecConfig;
+    private final BluetoothCodecConfig[] mCodecsLocalCapabilities;
+    private final BluetoothCodecConfig[] mCodecsSelectableCapabilities;
+
+    public BluetoothCodecStatus(BluetoothCodecConfig codecConfig,
+                                BluetoothCodecConfig[] codecsLocalCapabilities,
+                                BluetoothCodecConfig[] codecsSelectableCapabilities) {
+        mCodecConfig = codecConfig;
+        mCodecsLocalCapabilities = codecsLocalCapabilities;
+        mCodecsSelectableCapabilities = codecsSelectableCapabilities;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof BluetoothCodecStatus) {
+            BluetoothCodecStatus other = (BluetoothCodecStatus)o;
+            return (Objects.equals(other.mCodecConfig, mCodecConfig) &&
+                    Objects.equals(other.mCodecsLocalCapabilities,
+                                   mCodecsLocalCapabilities) &&
+                    Objects.equals(other.mCodecsSelectableCapabilities,
+                                   mCodecsSelectableCapabilities));
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mCodecConfig, mCodecsLocalCapabilities,
+                            mCodecsLocalCapabilities);
+    }
+
+    @Override
+    public String toString() {
+        return "{mCodecConfig:" + mCodecConfig +
+            ",mCodecsLocalCapabilities:" + Arrays.toString(mCodecsLocalCapabilities) +
+            ",mCodecsSelectableCapabilities:" + Arrays.toString(mCodecsSelectableCapabilities) +
+            "}";
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<BluetoothCodecStatus> CREATOR =
+            new Parcelable.Creator<BluetoothCodecStatus>() {
+        public BluetoothCodecStatus createFromParcel(Parcel in) {
+            final BluetoothCodecConfig codecConfig = in.readTypedObject(BluetoothCodecConfig.CREATOR);
+            final BluetoothCodecConfig[] codecsLocalCapabilities = in.createTypedArray(BluetoothCodecConfig.CREATOR);
+            final BluetoothCodecConfig[] codecsSelectableCapabilities = in.createTypedArray(BluetoothCodecConfig.CREATOR);
+
+            return new BluetoothCodecStatus(codecConfig,
+                                            codecsLocalCapabilities,
+                                            codecsSelectableCapabilities);
+        }
+        public BluetoothCodecStatus[] newArray(int size) {
+            return new BluetoothCodecStatus[size];
+        }
+    };
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeTypedObject(mCodecConfig, 0);
+        out.writeTypedArray(mCodecsLocalCapabilities, 0);
+        out.writeTypedArray(mCodecsSelectableCapabilities, 0);
+    }
+
+    /**
+     * Gets the current codec configuration.
+     *
+     * @return the current codec configuration
+     */
+    public BluetoothCodecConfig getCodecConfig() {
+        return mCodecConfig;
+    }
+
+    /**
+     * Gets the codecs local capabilities.
+     *
+     * @return an array with the codecs local capabilities
+     */
+    public BluetoothCodecConfig[] getCodecsLocalCapabilities() {
+        return mCodecsLocalCapabilities;
+    }
+
+    /**
+     * Gets the codecs selectable capabilities.
+     *
+     * @return an array with the codecs selectable capabilities
+     */
+    public BluetoothCodecConfig[] getCodecsSelectableCapabilities() {
+        return mCodecsSelectableCapabilities;
+    }
+}
diff --git a/core/java/android/bluetooth/IBluetoothA2dp.aidl b/core/java/android/bluetooth/IBluetoothA2dp.aidl
index 5b524eb..dbb5b7d 100644
--- a/core/java/android/bluetooth/IBluetoothA2dp.aidl
+++ b/core/java/android/bluetooth/IBluetoothA2dp.aidl
@@ -17,6 +17,7 @@
 package android.bluetooth;
 
 import android.bluetooth.BluetoothCodecConfig;
+import android.bluetooth.BluetoothCodecStatus;
 import android.bluetooth.BluetoothDevice;
 
 /**
@@ -37,6 +38,6 @@
     oneway void adjustAvrcpAbsoluteVolume(int direction);
     oneway void setAvrcpAbsoluteVolume(int volume);
     boolean isA2dpPlaying(in BluetoothDevice device);
-    BluetoothCodecConfig getCodecConfig();
+    BluetoothCodecStatus getCodecStatus();
     oneway void setCodecConfigPreference(in BluetoothCodecConfig codecConfig);
 }