Merge "Dynamic Audio Buffer (2/3)"
diff --git a/jni/com_android_bluetooth_btservice_AdapterService.cpp b/jni/com_android_bluetooth_btservice_AdapterService.cpp
index 42d0ce7..38969dd 100644
--- a/jni/com_android_bluetooth_btservice_AdapterService.cpp
+++ b/jni/com_android_bluetooth_btservice_AdapterService.cpp
@@ -1239,6 +1239,16 @@
   return output_bytes;
 }
 
+static jboolean setBufferMillisNative(JNIEnv* env, jobject obj, jint codec,
+                                      jint size) {
+  ALOGV("%s", __func__);
+
+  if (!sBluetoothInterface) return JNI_FALSE;
+
+  int ret = sBluetoothInterface->set_dynamic_audio_buffer_size(codec, size);
+  return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
 static jint connectSocketNative(JNIEnv* env, jobject obj, jbyteArray address,
                                 jint type, jbyteArray uuid, jint port,
                                 jint flag, jint callingUid) {
@@ -1365,6 +1375,7 @@
     {"interopDatabaseClearNative", "()V", (void*)interopDatabaseClearNative},
     {"interopDatabaseAddNative", "(I[BI)V", (void*)interopDatabaseAddNative},
     {"obfuscateAddressNative", "([B)[B", (void*)obfuscateAddressNative},
+    {"setBufferMillisNative", "(II)Z", (void*)setBufferMillisNative},
     {"getMetricIdNative", "([B)I", (void*)getMetricIdNative},
     {"connectSocketNative", "([BI[BIII)I", (void*)connectSocketNative},
     {"createSocketChannelNative", "(ILjava/lang/String;[BIII)I",
diff --git a/src/com/android/bluetooth/a2dp/A2dpService.java b/src/com/android/bluetooth/a2dp/A2dpService.java
index 5e5c68a..d68e68d 100644
--- a/src/com/android/bluetooth/a2dp/A2dpService.java
+++ b/src/com/android/bluetooth/a2dp/A2dpService.java
@@ -27,6 +27,7 @@
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothUuid;
+import android.bluetooth.BufferConstraints;
 import android.bluetooth.IBluetoothA2dp;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -873,6 +874,44 @@
         mDatabaseManager.setA2dpOptionalCodecsEnabled(device, value);
     }
 
+    /**
+     * Get dynamic audio buffer size supported type
+     *
+     * @return support <p>Possible values are
+     * {@link BluetoothA2dp#DYNAMIC_BUFFER_SUPPORT_NONE},
+     * {@link BluetoothA2dp#DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD},
+     * {@link BluetoothA2dp#DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING}.
+     */
+    public int getDynamicBufferSupport() {
+        enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
+                "Need BLUETOOTH_PRIVILEGED permission");
+        return mAdapterService.getDynamicBufferSupport();
+    }
+
+    /**
+     * Get dynamic audio buffer size
+     *
+     * @return BufferConstraints
+     */
+    public BufferConstraints getBufferConstraints() {
+        enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
+                "Need BLUETOOTH_PRIVILEGED permission");
+        return mAdapterService.getBufferConstraints();
+    }
+
+    /**
+     * Set dynamic audio buffer size
+     *
+     * @param codec Audio codec
+     * @param value buffer millis
+     * @return true if the settings is successful, false otherwise
+     */
+    public boolean setBufferMillis(int codec, int value) {
+        enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
+                "Need BLUETOOTH_PRIVILEGED permission");
+        return mAdapterService.setBufferMillis(codec, value);
+    }
+
     // Handle messages from native (JNI) to Java
     void messageFromNative(A2dpStackEvent stackEvent) {
         Objects.requireNonNull(stackEvent.device,
@@ -1385,6 +1424,30 @@
             }
             service.setOptionalCodecsEnabled(device, value);
         }
+
+        public int getDynamicBufferSupport() {
+            A2dpService service = getService();
+            if (service == null) {
+                return BluetoothA2dp.DYNAMIC_BUFFER_SUPPORT_NONE;
+            }
+            return service.getDynamicBufferSupport();
+        }
+
+        public BufferConstraints getBufferConstraints() {
+            A2dpService service = getService();
+            if (service == null) {
+                return null;
+            }
+            return service.getBufferConstraints();
+        }
+
+        public boolean setBufferMillis(int codec, int value) {
+            A2dpService service = getService();
+            if (service == null) {
+                return false;
+            }
+            return service.setBufferMillis(codec, value);
+        }
     }
 
     @Override
diff --git a/src/com/android/bluetooth/btservice/AbstractionLayer.java b/src/com/android/bluetooth/btservice/AbstractionLayer.java
index e15104d..203d3b0 100644
--- a/src/com/android/bluetooth/btservice/AbstractionLayer.java
+++ b/src/com/android/bluetooth/btservice/AbstractionLayer.java
@@ -48,6 +48,8 @@
     static final int BT_PROPERTY_REMOTE_VERSION_INFO = 0x0C;
     static final int BT_PROPERTY_LOCAL_LE_FEATURES = 0x0D;
 
+    static final int BT_PROPERTY_DYNAMIC_AUDIO_BUFFER = 0x10;
+
     public static final int BT_DEVICE_TYPE_BREDR = 0x01;
     public static final int BT_DEVICE_TYPE_BLE = 0x02;
     public static final int BT_DEVICE_TYPE_DUAL = 0x03;
diff --git a/src/com/android/bluetooth/btservice/AdapterProperties.java b/src/com/android/bluetooth/btservice/AdapterProperties.java
index 2737082..f2a8c2a 100644
--- a/src/com/android/bluetooth/btservice/AdapterProperties.java
+++ b/src/com/android/bluetooth/btservice/AdapterProperties.java
@@ -34,6 +34,8 @@
 import android.bluetooth.BluetoothPbapClient;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothSap;
+import android.bluetooth.BufferConstraint;
+import android.bluetooth.BufferConstraints;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -52,7 +54,9 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 class AdapterProperties {
@@ -115,6 +119,11 @@
     private boolean mIsLePeriodicAdvertisingSupported;
     private int mLeMaximumAdvertisingDataLength;
 
+    private int mIsDynamicAudioBufferSizeSupported;
+    private int mDynamicAudioBufferSizeSupportedCodecsGroup1;
+    private int mDynamicAudioBufferSizeSupportedCodecsGroup2;
+    private List<BufferConstraint> mBufferConstraintList;
+
     private boolean mReceiverRegistered;
     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -513,6 +522,44 @@
     }
 
     /**
+     * @return Dynamic Audio Buffer support
+     */
+    int getDynamicBufferSupport() {
+        if (!mA2dpOffloadEnabled) {
+            // TODO: Enable Dynamic Audio Buffer for A2DP software encoding when ready.
+            mIsDynamicAudioBufferSizeSupported =
+                BluetoothA2dp.DYNAMIC_BUFFER_SUPPORT_NONE;
+        } else {
+            if ((mDynamicAudioBufferSizeSupportedCodecsGroup1 != 0)
+                    || (mDynamicAudioBufferSizeSupportedCodecsGroup2 != 0)) {
+                mIsDynamicAudioBufferSizeSupported =
+                    BluetoothA2dp.DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD;
+            } else {
+                mIsDynamicAudioBufferSizeSupported =
+                    BluetoothA2dp.DYNAMIC_BUFFER_SUPPORT_NONE;
+            }
+        }
+        return mIsDynamicAudioBufferSizeSupported;
+    }
+
+    /**
+     * @return Dynamic Audio Buffer Capability
+     */
+    BufferConstraints getBufferConstraints() {
+        return new BufferConstraints(mBufferConstraintList);
+    }
+
+    /**
+     * Set the dynamic audio buffer size
+     *
+     * @param codec the codecs to set
+     * @param size the size to set
+     */
+    boolean setBufferMillis(int codec, int value) {
+        return mService.setBufferMillisNative(codec, value);
+    }
+
+    /**
      * @return the mBondedDevices
      */
     BluetoothDevice[] getBondedDevices() {
@@ -860,6 +907,10 @@
                         updateFeatureSupport(val);
                         break;
 
+                    case AbstractionLayer.BT_PROPERTY_DYNAMIC_AUDIO_BUFFER:
+                        updateDynamicAudioBufferSupport(val);
+                        break;
+
                     case AbstractionLayer.BT_PROPERTY_LOCAL_IO_CAPS:
                         mLocalIOCapability = Utils.byteArrayToInt(val);
                         debugLog("mLocalIOCapability set to " + mLocalIOCapability);
@@ -894,6 +945,10 @@
         mIsLePeriodicAdvertisingSupported = ((0xFF & ((int) val[17])) != 0);
         mLeMaximumAdvertisingDataLength =
                 (0xFF & ((int) val[18])) + ((0xFF & ((int) val[19])) << 8);
+        mDynamicAudioBufferSizeSupportedCodecsGroup1 =
+                ((0xFF & ((int) val[21])) << 8) + (0xFF & ((int) val[20]));
+        mDynamicAudioBufferSizeSupportedCodecsGroup2 =
+                ((0xFF & ((int) val[23])) << 8) + (0xFF & ((int) val[22]));
 
         Log.d(TAG, "BT_PROPERTY_LOCAL_LE_FEATURES: update from BT controller"
                 + " mNumOfAdvertisementInstancesSupported = "
@@ -909,10 +964,36 @@
                 + mIsLe2MPhySupported + " mIsLeCodedPhySupported = " + mIsLeCodedPhySupported
                 + " mIsLeExtendedAdvertisingSupported = " + mIsLeExtendedAdvertisingSupported
                 + " mIsLePeriodicAdvertisingSupported = " + mIsLePeriodicAdvertisingSupported
-                + " mLeMaximumAdvertisingDataLength = " + mLeMaximumAdvertisingDataLength);
+                + " mLeMaximumAdvertisingDataLength = " + mLeMaximumAdvertisingDataLength
+                + " mDynamicAudioBufferSizeSupportedCodecsGroup1 = "
+                + mDynamicAudioBufferSizeSupportedCodecsGroup1
+                + " mDynamicAudioBufferSizeSupportedCodecsGroup2 = "
+                + mDynamicAudioBufferSizeSupportedCodecsGroup2);
         invalidateIsOffloadedFilteringSupportedCache();
     }
 
+    private void updateDynamicAudioBufferSupport(byte[] val) {
+        // bufferConstraints is the table indicates the capability of all the codecs
+        // with buffer time. The raw is codec number, and the column is buffer type. There are 3
+        // buffer types - default/maximum/minimum.
+        // The maximum number of raw is BUFFER_CODEC_MAX_NUM(32).
+        // The maximum number of column is BUFFER_TYPE_MAX(3).
+        // The array element indicates the buffer time, the size is two octet.
+        mBufferConstraintList = new ArrayList<BufferConstraint>();
+
+        for (int i = 0; i < BufferConstraints.BUFFER_CODEC_MAX_NUM; i++) {
+            int defaultBufferTime = ((0xFF & ((int) val[i * 6 + 1])) << 8)
+                    + (0xFF & ((int) val[i * 6]));
+            int maximumBufferTime = ((0xFF & ((int) val[i * 6 + 3])) << 8)
+                    + (0xFF & ((int) val[i * 6 + 2]));
+            int minimumBufferTime = ((0xFF & ((int) val[i * 6 + 5])) << 8)
+                    + (0xFF & ((int) val[i * 6 + 4]));
+            BufferConstraint bufferConstraint = new BufferConstraint(defaultBufferTime,
+                    maximumBufferTime, minimumBufferTime);
+            mBufferConstraintList.add(bufferConstraint);
+        }
+    }
+
     void onBluetoothReady() {
         debugLog("onBluetoothReady, state=" + BluetoothAdapter.nameForState(getState())
                 + ", ScanMode=" + mScanMode);
diff --git a/src/com/android/bluetooth/btservice/AdapterService.java b/src/com/android/bluetooth/btservice/AdapterService.java
index dbcfd58..05fb852 100644
--- a/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/src/com/android/bluetooth/btservice/AdapterService.java
@@ -32,6 +32,7 @@
 import android.app.PendingIntent;
 import android.app.Service;
 import android.app.admin.DevicePolicyManager;
+import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothActivityEnergyInfo;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothAdapter.ActiveDeviceUse;
@@ -40,6 +41,7 @@
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothProtoEnums;
 import android.bluetooth.BluetoothUuid;
+import android.bluetooth.BufferConstraints;
 import android.bluetooth.IBluetooth;
 import android.bluetooth.IBluetoothCallback;
 import android.bluetooth.IBluetoothConnectionCallback;
@@ -3194,6 +3196,38 @@
     }
 
     /**
+     * Get dynamic audio buffer size supported type
+     *
+     * @return support <p>Possible values are
+     * {@link BluetoothA2dp#DYNAMIC_BUFFER_SUPPORT_NONE},
+     * {@link BluetoothA2dp#DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD},
+     * {@link BluetoothA2dp#DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING}.
+     */
+    public int getDynamicBufferSupport() {
+        return mAdapterProperties.getDynamicBufferSupport();
+    }
+
+    /**
+     * Get dynamic audio buffer size
+     *
+     * @return BufferConstraints
+     */
+    public BufferConstraints getBufferConstraints() {
+        return mAdapterProperties.getBufferConstraints();
+    }
+
+    /**
+     * Set dynamic audio buffer size
+     *
+     * @param codec Audio codec
+     * @param value buffer millis
+     * @return true if the settings is successful, false otherwise
+     */
+    public boolean setBufferMillis(int codec, int value) {
+        return mAdapterProperties.setBufferMillis(codec, value);
+    }
+
+    /**
      *  Get an incremental id of Bluetooth metrics and log
      *
      *  @param device Bluetooth device
@@ -3286,6 +3320,8 @@
 
     private native byte[] obfuscateAddressNative(byte[] address);
 
+    native boolean setBufferMillisNative(int codec, int value);
+
     private native int getMetricIdNative(byte[] address);
 
     /*package*/ native int connectSocketNative(