Match socket buffer sizes between audio HAL and BT server

Adjust our mixer buffer size to be one quarter that of
the socket buffer size for quadruple buffering.

Increase socket buffer size from 20*512 to 28*512 to
smooth out variability in data draw from AudioFlinger.

Bug: 28286313
Change-Id: I8a9ca9e1f4639a0724cfe126acc670c2058cb0fb
diff --git a/audio_a2dp_hw/audio_a2dp_hw.c b/audio_a2dp_hw/audio_a2dp_hw.c
index df2b92b..59efd35 100644
--- a/audio_a2dp_hw/audio_a2dp_hw.c
+++ b/audio_a2dp_hw/audio_a2dp_hw.c
@@ -638,10 +638,16 @@
 static size_t out_get_buffer_size(const struct audio_stream *stream)
 {
     struct a2dp_stream_out *out = (struct a2dp_stream_out *)stream;
+    // period_size is the AudioFlinger mixer buffer size.
+    const size_t period_size = out->common.buffer_sz / AUDIO_STREAM_OUTPUT_BUFFER_PERIODS;
+    const size_t mixer_unit_size = 16 /* frames */ * 4 /* framesize */;
 
-    DEBUG("buffer_size : %zu", out->common.buffer_sz);
+    DEBUG("socket buffer size: %zu  period size: %zu", out->common.buffer_sz, period_size);
+    if (period_size % mixer_unit_size != 0) {
+        ERROR("period size %zu not a multiple of %zu", period_size, mixer_unit_size);
+    }
 
-    return out->common.buffer_sz;
+    return period_size;
 }
 
 static uint32_t out_get_channels(const struct audio_stream *stream)
diff --git a/audio_a2dp_hw/audio_a2dp_hw.h b/audio_a2dp_hw/audio_a2dp_hw.h
index 901fafb..11d438a 100644
--- a/audio_a2dp_hw/audio_a2dp_hw.h
+++ b/audio_a2dp_hw/audio_a2dp_hw.h
@@ -38,7 +38,43 @@
 #define AUDIO_STREAM_DEFAULT_RATE          44100
 #define AUDIO_STREAM_DEFAULT_FORMAT        AUDIO_FORMAT_PCM_16_BIT
 #define AUDIO_STREAM_DEFAULT_CHANNEL_FLAG  AUDIO_CHANNEL_OUT_STEREO
-#define AUDIO_STREAM_OUTPUT_BUFFER_SZ      (20*512)
+
+// AUDIO_STREAM_OUTPUT_BUFFER_SZ controls the size of the audio socket buffer.
+// If one assumes the write buffer is always full during normal BT playback,
+// then increasing this value increases our playback latency.
+//
+// FIXME: AUDIO_STREAM_OUTPUT_BUFFER_SZ should be controlled by the actual audio
+// sample rate rather than being constant.
+//
+// FIXME: The BT HAL should consume data at a constant rate.
+// AudioFlinger assumes that the HAL draws data at a constant rate, which is true
+// for most audio devices; however, the BT engine reads data at a variable rate
+// (over the short term), which confuses both AudioFlinger as well as applications
+// which deliver data at a (generally) fixed rate.
+//
+// 20 * 512 is not sufficient size to smooth the variability for some BT devices,
+// resulting in mixer sleep and throttling. We increase this to 28 * 512 to help
+// reduce the effect of variable data consumption.
+#define AUDIO_STREAM_OUTPUT_BUFFER_SZ      (28*512)
+
+// AUDIO_STREAM_OUTPUT_BUFFER_PERIODS controls how the socket buffer is divided
+// for AudioFlinger data delivery. The AudioFlinger mixer delivers data in chunks
+// of AUDIO_STREAM_OUTPUT_BUFFER_SZ / AUDIO_STREAM_OUTPUT_BUFFER_PERIODS. If
+// the number of periods is 2, the socket buffer represents "double buffering"
+// of the AudioFlinger mixer buffer.
+//
+// In general, AUDIO_STREAM_OUTPUT_BUFFER_PERIODS * 16 * 4 should be a divisor of
+// AUDIO_STREAM_OUTPUT_BUFFER_SZ.
+//
+// These values should be chosen such that
+//
+// AUDIO_STREAM_BUFFER_SIZE * 1000 / (AUDIO_STREAM_OUTPUT_BUFFER_PERIODS
+//         * AUDIO_STREAM_DEFAULT_RATE * 4) > 20 (ms)
+//
+// to avoid introducing the FastMixer in AudioFlinger. Using the FastMixer results in
+// unnecessary latency and CPU overhead for Bluetooth.
+#define AUDIO_STREAM_OUTPUT_BUFFER_PERIODS 4
+
 #define AUDIO_SKT_DISCONNECTED             (-1)
 
 typedef enum {
diff --git a/udrv/ulinux/uipc.c b/udrv/ulinux/uipc.c
index 4256aed..e932c90 100644
--- a/udrv/ulinux/uipc.c
+++ b/udrv/ulinux/uipc.c
@@ -193,6 +193,13 @@
          return -1;
     }
 
+    // match socket buffer size option with client
+    const int size = AUDIO_STREAM_OUTPUT_BUFFER_SZ;
+    int ret = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char*)&size, (int)sizeof(size));
+    if (ret < 0) {
+        BTIF_TRACE_ERROR("setsockopt failed (%s)", strerror(errno));
+    }
+
     //BTIF_TRACE_EVENT("new fd %d", fd);
 
     return fd;