audio_a2dp_hw: add device lock

Prevent conflict between closing a stream (adev_close_output_stream)
and setting parameters (adev_set_parameters).

Test: Manual - Bluetooth A2DP streaming and changing codec parameters
Bug: 36723276
Change-Id: Id8b9fcdf594e3d5fde139719d324420468c4c0aa
(cherry picked from commit b1a900fcc9903f0988b6f8c1a470b9c718aedf1f)
diff --git a/audio_a2dp_hw/audio_a2dp_hw.cc b/audio_a2dp_hw/audio_a2dp_hw.cc
index 24a5014..2a0c7e0 100644
--- a/audio_a2dp_hw/audio_a2dp_hw.cc
+++ b/audio_a2dp_hw/audio_a2dp_hw.cc
@@ -95,7 +95,10 @@
 struct a2dp_stream_out;
 
 struct a2dp_audio_device {
+  // Important: device must be first as an audio_hw_device* may be cast to
+  // a2dp_audio_device* when the type is implicitly known.
   struct audio_hw_device device;
+  std::recursive_mutex* mutex;  // See note below on mutex acquisition order.
   struct a2dp_stream_in* input;
   struct a2dp_stream_out* output;
 };
@@ -109,7 +112,7 @@
 /* move ctrl_fd outside output stream and keep open until HAL unloaded ? */
 
 struct a2dp_stream_common {
-  std::recursive_mutex* mutex;
+  std::recursive_mutex* mutex;  // See note below on mutex acquisition order.
   int ctrl_fd;
   int audio_fd;
   size_t buffer_sz;
@@ -129,6 +132,15 @@
   struct a2dp_stream_common common;
 };
 
+/*
+ * Mutex acquisition order:
+ *
+ * The a2dp_audio_device (adev) mutex must be acquired before
+ * the a2dp_stream_common (out or in) mutex.
+ *
+ * This may differ from other audio HALs.
+ */
+
 /*****************************************************************************
  *  Static variables
  *****************************************************************************/
@@ -1354,7 +1366,8 @@
   int ret = 0;
 
   INFO("opening output");
-
+  // protect against adev->output and stream_out from being inconsistent
+  std::lock_guard<std::recursive_mutex> lock(*a2dp_dev->mutex);
   out = (struct a2dp_stream_out*)calloc(1, sizeof(struct a2dp_stream_out));
 
   if (!out) return -ENOMEM;
@@ -1454,17 +1467,21 @@
   struct a2dp_audio_device* a2dp_dev = (struct a2dp_audio_device*)dev;
   struct a2dp_stream_out* out = (struct a2dp_stream_out*)stream;
 
-  INFO("closing output (state %d)", out->common.state);
+  // prevent interference with adev_set_parameters.
+  std::lock_guard<std::recursive_mutex> lock(*a2dp_dev->mutex);
+  {
+    std::lock_guard<std::recursive_mutex> lock(*out->common.mutex);
+    const a2dp_state_t state = out->common.state;
+    INFO("closing output (state %d)", (int)state);
+    if ((state == AUDIO_A2DP_STATE_STARTED) ||
+        (state == AUDIO_A2DP_STATE_STOPPING)) {
+      stop_audio_datapath(&out->common);
+    }
 
-  std::unique_lock<std::recursive_mutex> lock(*out->common.mutex);
-  if ((out->common.state == AUDIO_A2DP_STATE_STARTED) ||
-      (out->common.state == AUDIO_A2DP_STATE_STOPPING)) {
-    stop_audio_datapath(&out->common);
+    skt_disconnect(out->common.ctrl_fd);
+    out->common.ctrl_fd = AUDIO_SKT_DISCONNECTED;
   }
 
-  skt_disconnect(out->common.ctrl_fd);
-  out->common.ctrl_fd = AUDIO_SKT_DISCONNECTED;
-  lock.unlock();
   a2dp_stream_common_destroy(&out->common);
   free(stream);
   a2dp_dev->output = NULL;
@@ -1475,9 +1492,12 @@
 static int adev_set_parameters(struct audio_hw_device* dev,
                                const char* kvpairs) {
   struct a2dp_audio_device* a2dp_dev = (struct a2dp_audio_device*)dev;
-  struct a2dp_stream_out* out = a2dp_dev->output;
   int retval = 0;
 
+  // prevent interference with adev_close_output_stream
+  std::lock_guard<std::recursive_mutex> lock(*a2dp_dev->mutex);
+  struct a2dp_stream_out* out = a2dp_dev->output;
+
   if (out == NULL) return retval;
 
   INFO("state %d", out->common.state);
@@ -1562,6 +1582,8 @@
 
   FNLOG();
 
+  // protect against adev->input and stream_in from being inconsistent
+  std::lock_guard<std::recursive_mutex> lock(*a2dp_dev->mutex);
   in = (struct a2dp_stream_in*)calloc(1, sizeof(struct a2dp_stream_in));
 
   if (!in) return -ENOMEM;
@@ -1617,16 +1639,20 @@
                                     struct audio_stream_in* stream) {
   struct a2dp_audio_device* a2dp_dev = (struct a2dp_audio_device*)dev;
   struct a2dp_stream_in* in = (struct a2dp_stream_in*)stream;
-  a2dp_state_t state = in->common.state;
 
-  INFO("closing input (state %d)", state);
+  std::lock_guard<std::recursive_mutex> lock(*a2dp_dev->mutex);
+  {
+    std::lock_guard<std::recursive_mutex> lock(*in->common.mutex);
+    const a2dp_state_t state = in->common.state;
+    INFO("closing input (state %d)", (int)state);
 
-  if ((state == AUDIO_A2DP_STATE_STARTED) ||
-      (state == AUDIO_A2DP_STATE_STOPPING))
-    stop_audio_datapath(&in->common);
+    if ((state == AUDIO_A2DP_STATE_STARTED) ||
+        (state == AUDIO_A2DP_STATE_STOPPING))
+      stop_audio_datapath(&in->common);
 
-  skt_disconnect(in->common.ctrl_fd);
-  in->common.ctrl_fd = AUDIO_SKT_DISCONNECTED;
+    skt_disconnect(in->common.ctrl_fd);
+    in->common.ctrl_fd = AUDIO_SKT_DISCONNECTED;
+  }
   a2dp_stream_common_destroy(&in->common);
   free(stream);
   a2dp_dev->input = NULL;
@@ -1642,8 +1668,11 @@
 }
 
 static int adev_close(hw_device_t* device) {
+  struct a2dp_audio_device* a2dp_dev = (struct a2dp_audio_device*)device;
   FNLOG();
 
+  delete a2dp_dev->mutex;
+  a2dp_dev->mutex = nullptr;
   free(device);
   return 0;
 }
@@ -1664,6 +1693,8 @@
 
   if (!adev) return -ENOMEM;
 
+  adev->mutex = new std::recursive_mutex;
+
   adev->device.common.tag = HARDWARE_DEVICE_TAG;
   adev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0;
   adev->device.common.module = (struct hw_module_t*)module;