Merge "st-hal: Update device data structure from bitmask to list."
diff --git a/sound_trigger_hw.c b/sound_trigger_hw.c
index 9a81ec9..6f9b6f1 100644
--- a/sound_trigger_hw.c
+++ b/sound_trigger_hw.c
@@ -3,7 +3,7 @@
  * This file contains the API to load sound models with
  * DSP and start/stop detection of associated key phrases.
  *
- * Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -744,13 +744,19 @@
 static void handle_audio_concurrency(audio_event_type_t event_type,
     audio_event_info_t* config)
 {
-    struct listnode *p_ses_node = NULL;
+    struct listnode *p_ses_node = NULL, *node = NULL;
     st_session_t *p_ses = NULL;
     bool conc_allowed = false, lpi_changed = false, barge_in_mode = false;
     unsigned int num_sessions = 0;
+    struct audio_device_info *item = NULL;
 
-    ALOGV_IF(config != NULL, "%s: Enter, event type = %d, audio device = %d",
-             __func__, event_type, config->device_info.device);
+    if (config != NULL) {
+        ALOGV("%s: Event type = %d", __func__, event_type);
+        list_for_each (node, &config->device_info.devices) {
+            item = node_to_item(node, struct audio_device_info, list);
+            ALOGV("%s: Audio device = 0x%x", __func__, item->type);
+        }
+    }
 
     /*
     UC1:
@@ -1090,7 +1096,8 @@
      * of enabling the device for a use case, actual device will be selected
      * based on internal device selection policy
      */
-    platform_stdev_update_avail_device(stdev->platform, device, connect);
+    platform_stdev_update_device_list(device, "", &stdev->available_devices,
+        connect);
 
     if ((connect && (cur_device == device)) ||
          (!connect && (cur_device != device))) {
@@ -1216,10 +1223,7 @@
 {
     struct listnode *node = NULL;
     st_session_t *p_ses = NULL;
-    audio_devices_t ec_ref_devices = AUDIO_DEVICE_OUT_ALL_A2DP |
-                                     AUDIO_DEVICE_OUT_LINE |
-                                     AUDIO_DEVICE_OUT_SPEAKER |
-                                     AUDIO_DEVICE_IN_WIRED_HEADSET;
+
     if (config == NULL) {
         ALOGV("%s: null config event received!", __func__);
         return;
@@ -1227,14 +1231,24 @@
 
     pthread_mutex_lock(&stdev->lock);
     if (AUDIO_EVENT_PLAYBACK_STREAM_ACTIVE == event_type &&
-        stdev->active_rx_dev != (audio_devices_t)config->device_info.device) {
+        !platform_stdev_compare_devices(&config->device_info.devices,
+            &stdev->active_rx_dev_list)) {
         /*
          * if currently no active ADSP session available, then echo
          * reference will be enabled during session transition
          */
-        stdev->active_rx_dev = config->device_info.device;
+        platform_stdev_assign_devices(&stdev->active_rx_dev_list,
+            &config->device_info.devices);
         if (get_num_sessions_in_exec_mode(ST_EXEC_MODE_ADSP) > 0 &&
-            stdev->active_rx_dev & ec_ref_devices) {
+            (platform_stdev_is_a2dp_out_device_type(
+                 &stdev->active_rx_dev_list) ||
+             platform_stdev_compare_device_type(&stdev->active_rx_dev_list,
+                 AUDIO_DEVICE_OUT_LINE) ||
+             platform_stdev_compare_device_type(&stdev->active_rx_dev_list,
+                 AUDIO_DEVICE_OUT_SPEAKER) ||
+             platform_stdev_compare_device_type(&stdev->active_rx_dev_list,
+                 AUDIO_DEVICE_IN_WIRED_HEADSET))) {
+
             /* pause and resume ADSP sessions to send new echo reference */
             list_for_each(node, &stdev->sound_model_list) {
                 p_ses = node_to_item(node, st_session_t, list_node);
@@ -2736,10 +2750,16 @@
     stdev->session_id = 1;
     stdev->gcs_token = 1;
     stdev->exec_mode = ST_EXEC_MODE_MAX;
-    stdev->available_devices = AUDIO_DEVICE_IN_BUILTIN_MIC;
     stdev->client_req_exec_mode = ST_EXEC_MODE_NONE;
-    stdev->ec_ref_dev = AUDIO_DEVICE_OUT_SPEAKER;
-    stdev->active_rx_dev = AUDIO_DEVICE_OUT_SPEAKER;
+    list_init(&stdev->available_devices);
+    platform_stdev_update_device_list(AUDIO_DEVICE_IN_BUILTIN_MIC, "",
+        &stdev->available_devices, true);
+    list_init(&stdev->ec_ref_dev_list);
+    platform_stdev_update_device_list(AUDIO_DEVICE_OUT_SPEAKER, "",
+        &stdev->ec_ref_dev_list, true);
+    list_init(&stdev->active_rx_dev_list);
+    platform_stdev_update_device_list(AUDIO_DEVICE_OUT_SPEAKER, "",
+        &stdev->active_rx_dev_list, true);
     stdev->session_allowed = true;
     stdev->reset_backend = true;
     stdev->conc_voice_active = false;
@@ -2968,12 +2988,15 @@
             break;
         }
         if (config->u.value == AUDIO_DEVICE_OUT_LINE ||
-            config->u.value & AUDIO_DEVICE_OUT_ALL_A2DP) {
+            config->u.value == AUDIO_DEVICE_OUT_BLUETOOTH_A2DP ||
+            config->u.value == AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES ||
+            config->u.value == AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER) {
             pthread_mutex_lock(&stdev->lock);
-            stdev->ec_ref_dev |= config->u.value;
+            platform_stdev_update_device_list(config->u.value, "",
+                &stdev->ec_ref_dev_list, true);
             pthread_mutex_unlock(&stdev->lock);
-            ALOGD("%s: Device connected. Updated ec ref devices as %d",
-                  __func__, stdev->ec_ref_dev);
+            ALOGD("%s: Device connected. Updated ec ref devices with %d",
+                  __func__, config->u.value);
         } else {
             handle_device_switch(true, config);
         }
@@ -2986,16 +3009,19 @@
             break;
         }
         if (config->u.value == AUDIO_DEVICE_OUT_LINE ||
-            config->u.value & AUDIO_DEVICE_OUT_ALL_A2DP) {
+            config->u.value == AUDIO_DEVICE_OUT_BLUETOOTH_A2DP ||
+            config->u.value == AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES ||
+            config->u.value == AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER) {
             pthread_mutex_lock(&stdev->lock);
             /*
              * Currently spkr/lineout/a2dp are the supported ec ref devices
              * Set default EC ref device (spkr) if lineout/a2dp is disconnected
              */
-            stdev->ec_ref_dev &= ~config->u.value;
+            platform_stdev_update_device_list(config->u.value, "",
+                &stdev->ec_ref_dev_list, false);
             pthread_mutex_unlock(&stdev->lock);
-            ALOGD("%s: Device disconnected. Updated ec ref devices as %d",
-                  __func__, stdev->ec_ref_dev);
+            ALOGD("%s: Device disconnected. Updated ec ref devices by removing %d",
+                  __func__, config->u.value);
         } else {
             handle_device_switch(false, config);
         }
diff --git a/sound_trigger_hw.h b/sound_trigger_hw.h
index 4117d32..7e139c8 100644
--- a/sound_trigger_hw.h
+++ b/sound_trigger_hw.h
@@ -3,7 +3,7 @@
  * This file contains the API to load sound models with
  * DSP and start/stop detection of associated key phrases.
  *
- * Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -177,7 +177,7 @@
     volatile int gcs_token;
     pthread_mutex_t ref_cnt_lock;
     int *dev_ref_cnt;
-    audio_devices_t available_devices;
+    struct listnode available_devices;
     pthread_t transitions_thread;
     pthread_cond_t transitions_cond;
 
@@ -254,8 +254,8 @@
     mulaw_dec_process_t mulaw_dec_process;
 
     struct listnode adm_cfg_list;
-    audio_devices_t ec_ref_dev;
-    audio_devices_t active_rx_dev;
+    struct listnode ec_ref_dev_list;
+    struct listnode active_rx_dev_list;
 
     bool ssr_offline_received;
     int lpma_handle;
@@ -275,6 +275,12 @@
     sound_model_handle_t sound_model_handle
 );
 
+struct audio_device_info {
+    struct listnode list;
+    audio_devices_t type;
+    char address[AUDIO_DEVICE_MAX_ADDRESS_LEN];
+};
+
 void update_hw_mad_exec_mode(st_exec_mode_t mode, st_profile_type_t profile_type);
 
 #define ST_DEBUG_DUMP_LOCATION "/data/vendor/audio"
diff --git a/sound_trigger_platform.c b/sound_trigger_platform.c
index d09ae91..78dd82c 100644
--- a/sound_trigger_platform.c
+++ b/sound_trigger_platform.c
@@ -2,7 +2,7 @@
  *
  * This file contains the platform specific functionality.
  *
- * Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -3136,6 +3136,194 @@
     }
 }
 
+/* ---------------- device list APIs --------------- */
+static int list_length(struct listnode *list)
+{
+    struct listnode *node;
+    int length = 0;
+
+    if (list == NULL)
+        goto done;
+
+    for (node = list->next; node != list; node = node->next)
+        ++length;
+done:
+    return length;
+}
+
+/*
+ * Clear device list
+ * Operation: devices = {};
+ */
+static int clear_devices(struct listnode *devices)
+{
+    struct listnode *node = NULL;
+    struct audio_device_info *item = NULL;
+
+    if (devices == NULL)
+        return 0;
+
+    list_for_each (node, devices) {
+        item = node_to_item(node, struct audio_device_info, list);
+        if (item != NULL) {
+            list_remove(&item->list);
+            free(item);
+        }
+    }
+
+    return 0;
+}
+
+/*
+ * Returns true if A2DP output device is found in passed devices list
+ */
+bool platform_stdev_is_a2dp_out_device_type(struct listnode *devices)
+{
+    if (devices == NULL)
+        return false;
+
+    if (platform_stdev_compare_device_type(devices,
+            AUDIO_DEVICE_OUT_BLUETOOTH_A2DP) ||
+        platform_stdev_compare_device_type(devices,
+            AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES) ||
+        platform_stdev_compare_device_type(devices,
+            AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER))
+        return true;
+    else
+        return false;
+}
+
+/*
+ * Check if a device with given type is present in devices list
+ */
+bool platform_stdev_compare_device_type(struct listnode *devices,
+                                        audio_devices_t device_type)
+{
+    struct listnode *node = NULL;
+    struct audio_device_info *item = NULL;
+
+    if (devices == NULL)
+        return false;
+
+    list_for_each (node, devices) {
+        item = node_to_item(node, struct audio_device_info, list);
+        if (item != NULL && (item->type == device_type)) {
+            ALOGV("%s: device types %d match", __func__, device_type);
+            return true;
+        }
+    }
+    return false;
+}
+
+/*
+ * Returns true if lists are equal in terms of device type
+ * TODO: Check if device addresses are also equal in the future
+ */
+bool platform_stdev_compare_devices(struct listnode *d1, struct listnode *d2)
+{
+    struct listnode *node = NULL;
+    struct audio_device_info *item = NULL;
+
+    if (d1 == NULL && d2 == NULL)
+        return true;
+
+    if (d1 == NULL || d2 == NULL ||
+        (list_length(d1) != list_length(d2)))
+        return false;
+
+    list_for_each (node, d1) {
+        item = node_to_item(node, struct audio_device_info, list);
+        if (item != NULL &&
+            !platform_stdev_compare_device_type(d2, item->type))
+            return false;
+    }
+    return true;
+}
+
+/*
+ * Add or remove device from list denoted by head
+ */
+int platform_stdev_update_device_list(audio_devices_t type, char* address,
+                       struct listnode *head, bool add_device)
+{
+    struct listnode *node = NULL;
+    struct audio_device_info *item = NULL;
+    struct audio_device_info *device = NULL;
+    int ret = 0;
+
+    if (head == NULL)
+        goto done;
+
+    if (type == AUDIO_DEVICE_NONE) {
+        ALOGE("%s: Invalid device: %#x", __func__, type);
+        ret = -EINVAL;
+        goto done;
+    }
+
+    list_for_each (node, head) {
+        item = node_to_item(node, struct audio_device_info, list);
+        if (item != NULL && (item->type == type)) {
+            device = item;
+            break;
+        }
+    }
+
+    if (add_device) {
+        if (device == NULL) {
+            device = (struct audio_device_info *)
+                        calloc (1, sizeof(struct audio_device_info));
+            if (!device) {
+                ALOGE("%s: Cannot allocate memory for device_info", __func__);
+                ret = -ENOMEM;
+                goto done;
+            }
+            device->type = type;
+            list_add_tail(head, &device->list);
+        }
+        /*
+         * TODO: Use address in future if required. Currently NULL string used.
+         */
+        strlcpy(device->address, address, AUDIO_DEVICE_MAX_ADDRESS_LEN);
+        ALOGV("%s: Added device type %#x, address %s", __func__, type,
+            address);
+    } else {
+        if (device != NULL) {
+            list_remove(&device->list);
+            free(device);
+            ALOGV("%s: Removed device type %#x, address %s", __func__, type,
+                address);
+        }
+    }
+done:
+    return ret;
+}
+
+/*
+ * Assign source device list to destination device list
+ * Operation: dest list = source list
+ */
+int platform_stdev_assign_devices(struct listnode *dest,
+                                  const struct listnode *source)
+{
+    struct listnode *node;
+    struct audio_device_info *item = NULL;
+    int ret = 0;
+
+    if (source == NULL || dest == NULL)
+        return ret;
+
+    if (!list_empty(dest))
+        clear_devices(dest);
+
+    list_for_each (node, source) {
+        item = node_to_item(node, struct audio_device_info, list);
+        if (item != NULL)
+            ret = platform_stdev_update_device_list(item->type, item->address,
+                dest, true);
+    }
+    return ret;
+}
+
 #if (SNDRV_LSM_VERSION >= SNDRV_PROTOCOL_VERSION(0, 3, 0))
 static void platform_stdev_send_adm_app_type_cfg(void *platform)
 {
@@ -4096,37 +4284,26 @@
     struct platform_data *my_data = (struct platform_data *)platform;
     sound_trigger_device_t *stdev = my_data->stdev;
     audio_devices_t device = AUDIO_DEVICE_NONE;
-    audio_devices_t avail_devices = stdev->available_devices &
-                                     ~AUDIO_DEVICE_BIT_IN;
+    struct audio_device_info *item = NULL;
+    struct listnode *node = NULL;
 
-    if (avail_devices & AUDIO_DEVICE_IN_WIRED_HEADSET)
+    if (platform_stdev_compare_device_type(&stdev->available_devices,
+        AUDIO_DEVICE_IN_WIRED_HEADSET)) {
         device = AUDIO_DEVICE_IN_WIRED_HEADSET;
-    else if (avail_devices & AUDIO_DEVICE_IN_BUILTIN_MIC)
+    } else if (platform_stdev_compare_device_type(&stdev->available_devices,
+        AUDIO_DEVICE_IN_BUILTIN_MIC)) {
         device = AUDIO_DEVICE_IN_BUILTIN_MIC;
+    }
 
-    ALOGD("%s: available devices 0x%x, device 0x%x", __func__,
-                         stdev->available_devices, device);
+    ALOGD("%s: Device = 0x%x", __func__, device);
+    list_for_each (node, &stdev->available_devices) {
+        item = node_to_item(node, struct audio_device_info, list);
+        ALOGD("%s: Available device = 0x%x", __func__, item->type);
+    }
+
     return device;
 }
 
-int platform_stdev_update_avail_device
-(
-    void *platform,
-    audio_devices_t device,
-    bool connect
-)
-{
-    struct platform_data *my_data = (struct platform_data *)platform;
-    sound_trigger_device_t *stdev = my_data->stdev;
-
-    if (connect)
-        stdev->available_devices |= device;
-    else
-        stdev->available_devices &= ~device;
-
-    return 0;
-}
-
 static int get_backend_index_from_name
 (
     struct platform_data *my_data,
@@ -5440,19 +5617,23 @@
 static void check_and_append_ec_ref_device_name
 (
     void *platform,
-    char *ec_ref_mixer_path,
-    audio_devices_t rx_device
+    char *ec_ref_mixer_path
 )
 {
     audio_devices_t capture_device = 0;
+    struct platform_data *my_data = (struct platform_data *)platform;
 
     capture_device = platform_stdev_get_capture_device(platform);
-    if (capture_device == AUDIO_DEVICE_IN_WIRED_HEADSET)
+    if (capture_device == AUDIO_DEVICE_IN_WIRED_HEADSET) {
         strlcat(ec_ref_mixer_path, " headset",  DEVICE_NAME_MAX_SIZE);
-    else if (rx_device & AUDIO_DEVICE_OUT_ALL_A2DP)
+    } else if (platform_stdev_is_a2dp_out_device_type(
+                   &my_data->stdev->active_rx_dev_list)) {
         strlcat(ec_ref_mixer_path, " a2dp",  DEVICE_NAME_MAX_SIZE);
-    else if (rx_device & AUDIO_DEVICE_OUT_LINE)
+    } else if (platform_stdev_compare_device_type(
+                   &my_data->stdev->active_rx_dev_list,
+                   AUDIO_DEVICE_OUT_LINE)) {
         strlcat(ec_ref_mixer_path, " line",  DEVICE_NAME_MAX_SIZE);
+    }
 }
 
 int platform_stdev_update_ec_effect
@@ -5506,8 +5687,7 @@
                     sizeof(my_data->ec_ref_mixer_path));
 
             check_and_append_ec_ref_device_name(platform,
-                                                my_data->ec_ref_mixer_path,
-                                                stdev->active_rx_dev);
+                                                my_data->ec_ref_mixer_path);
 
             audio_route_apply_and_update_path(stdev->audio_route,
                     my_data->ec_ref_mixer_path);
diff --git a/sound_trigger_platform.h b/sound_trigger_platform.h
index 59dd5d5..7768e05 100644
--- a/sound_trigger_platform.h
+++ b/sound_trigger_platform.h
@@ -1,6 +1,6 @@
 /* sound_trigger_platform.h
  *
- * Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -424,6 +424,15 @@
     unsigned int param_id;
 } acdb_audio_cal_cfg_t;
 
+bool platform_stdev_is_a2dp_out_device_type(struct listnode *devices);
+bool platform_stdev_compare_device_type(struct listnode *devices,
+    audio_devices_t device_type);
+bool platform_stdev_compare_devices(struct listnode *d1, struct listnode *d2);
+int platform_stdev_update_device_list(audio_devices_t type, char* address,
+    struct listnode *head, bool add_device);
+int platform_stdev_assign_devices(struct listnode *dest,
+    const struct listnode *source);
+
 void *platform_stdev_init(struct sound_trigger_device *stdev);
 
 void platform_stdev_deinit(void *platform);
@@ -452,13 +461,6 @@
 
 audio_devices_t platform_stdev_get_capture_device(void *platform);
 
-int platform_stdev_update_avail_device
-(
-    void *platform,
-    audio_devices_t device,
-    bool connect
-);
-
 int platform_stdev_send_calibration
 (
     void *platform,
diff --git a/sound_trigger_prop_intf.h b/sound_trigger_prop_intf.h
index 7ad34dc..4cd599c 100644
--- a/sound_trigger_prop_intf.h
+++ b/sound_trigger_prop_intf.h
@@ -3,7 +3,7 @@
  * Interface for sound trigger related communication
  * across modules.
  *
- * Copyright (c) 2014, 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014, 2016-2020, The Linux Foundation. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -41,9 +41,8 @@
 #define MINOR_VERSION(ver) ((ver) & 0x00ff)
 
 /* Proprietary interface version used for compatibility with AHAL */
-#define STHAL_PROP_API_VERSION_1_0 MAKE_HAL_VERSION(1, 0)
-#define STHAL_PROP_API_VERSION_1_1 MAKE_HAL_VERSION(1, 1)
-#define STHAL_PROP_API_CURRENT_VERSION STHAL_PROP_API_VERSION_1_1
+#define STHAL_PROP_API_VERSION_2_0 MAKE_HAL_VERSION(2, 0)
+#define STHAL_PROP_API_CURRENT_VERSION STHAL_PROP_API_VERSION_2_0
 
 #define ST_EVENT_CONFIG_MAX_STR_VALUE 32
 
@@ -117,7 +116,7 @@
 };
 
 struct sound_trigger_device_info {
-    int device;
+    struct listnode devices;
 };
 
 struct sound_trigger_get_param_data {
diff --git a/st_hw_session_pcm.c b/st_hw_session_pcm.c
index f429bc2..2ed7243 100644
--- a/st_hw_session_pcm.c
+++ b/st_hw_session_pcm.c
@@ -2,7 +2,7 @@
  *
  * This file implements the hw session functionality of SVA using capture path
  *
- * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -293,7 +293,8 @@
             return -EINVAL;
         }
 
-        if (p_ses->stdev->ec_ref_dev & AUDIO_DEVICE_OUT_LINE)
+        if (platform_stdev_compare_device_type(p_ses->stdev->ec_ref_dev,
+            AUDIO_DEVICE_OUT_LINE))
             strlcat(st_device_name, " lineout",  DEVICE_NAME_MAX_SIZE);
 
         ALOGD("%s: enable device = %s", __func__,