audio: support hifi audio record with USB

Add support to record hifi audio when USB is connected

Bug: 37304195
Test: test playback and capture with and without USB headset
Change-Id: I4f4142aba495e9625b17ba007280f76fbc66a641
diff --git a/hal/audio_extn/audio_extn.h b/hal/audio_extn/audio_extn.h
index 85a5881..100741f 100644
--- a/hal/audio_extn/audio_extn.h
+++ b/hal/audio_extn/audio_extn.h
@@ -79,8 +79,8 @@
 #define audio_extn_usb_enable_sidetone(device, enable)                 (0)
 #define audio_extn_usb_set_sidetone_gain(parms, value, len)            (0)
 #define audio_extn_usb_is_capture_supported()                          (false)
-#define audio_extn_usb_get_max_channels()                              (0)
-#define audio_extn_usb_get_max_bit_width()                             (0)
+#define audio_extn_usb_get_max_channels(dir)                           (0)
+#define audio_extn_usb_get_max_bit_width(dir)                          (0)
 #define audio_extn_usb_sup_sample_rates(t, s, l)                       (0)
 #else
 void audio_extn_usb_init(void *adev);
@@ -95,9 +95,9 @@
 int audio_extn_usb_set_sidetone_gain(struct str_parms *parms,
                                      char *value, int len);
 bool audio_extn_usb_is_capture_supported();
-int audio_extn_usb_get_max_channels();
-int audio_extn_usb_get_max_bit_width();
-int audio_extn_usb_sup_sample_rates(int type, uint32_t *sr, uint32_t l);
+int audio_extn_usb_get_max_channels(bool is_playback);
+int audio_extn_usb_get_max_bit_width(bool is_playback);
+int audio_extn_usb_sup_sample_rates(bool is_playback, uint32_t *sr, uint32_t l);
 #endif
 
 
diff --git a/hal/audio_extn/usb.c b/hal/audio_extn/usb.c
index a1af141..4e4c70d 100644
--- a/hal/audio_extn/usb.c
+++ b/hal/audio_extn/usb.c
@@ -290,6 +290,7 @@
     char *bit_width_str = NULL;
     struct usb_device_config * usb_device_info;
     bool check = false;
+    int tries=5;
 
     memset(path, 0, sizeof(path));
     ALOGV("%s: for %s", __func__, (type == USB_PLAYBACK) ?
@@ -298,12 +299,20 @@
     /* TODO: convert the below to using alsa_utils */
     ret = snprintf(path, sizeof(path), "/proc/asound/card%u/stream0",
              card);
-    if(ret < 0) {
+    if (ret < 0) {
         ALOGE("%s: failed on snprintf (%d) to path %s\n",
           __func__, ret, path);
         goto done;
     }
 
+    while (tries--) {
+        if (access(path, F_OK) < 0) {
+            ALOGW("stream %s doesn't exist retrying\n", path);
+            sleep(1);
+            continue;
+        }
+    }
+
     fd = open(path, O_RDONLY);
     if (fd <0) {
         ALOGE("%s: error failed to open config file %s error: %d\n",
@@ -518,18 +527,26 @@
     return;
 }
 
+static inline bool usb_output_device(audio_devices_t device) {
+    // ignore accessory for now
+    if (device == AUDIO_DEVICE_OUT_USB_ACCESSORY) {
+        return false;
+    }
+    return audio_is_usb_out_device(device);
+}
+
+static inline bool usb_input_device(audio_devices_t device) {
+    // ignore accessory for now
+    if (device == AUDIO_DEVICE_IN_USB_ACCESSORY) {
+        return false;
+    }
+    return audio_is_usb_in_device(device);
+}
+
 static bool usb_valid_device(audio_devices_t device)
 {
-    if ((popcount(device) == 1) && (device & AUDIO_DEVICE_OUT_USB_DEVICE))
-        return true;
-
-    if ((device & AUDIO_DEVICE_BIT_IN) != 0) {
-        device &= ~AUDIO_DEVICE_BIT_IN;
-        if (popcount(device) == 1 && (device & AUDIO_DEVICE_IN_USB_DEVICE) != 0)
-            return true;
-    }
-
-    return false;
+    return usb_output_device(device) ||
+           usb_input_device(device);
 }
 
 static void usb_print_active_device(void){
@@ -821,7 +838,7 @@
         card_info = node_to_item(node_i, struct usb_card_config, list);
         ALOGV("%s: card_dev_type (0x%x), card_no(%d)",
                __func__,  card_info->usb_device_type, card_info->usb_card);
-        if (card_info->usb_device_type == AUDIO_DEVICE_OUT_USB_DEVICE) {
+        if (usb_output_device(card_info->usb_device_type)) {
             if ((i = card_info->usb_sidetone_index[USB_SIDETONE_ENABLE_INDEX]) != -1) {
                 struct mixer_ctl *ctl = mixer_get_ctl_by_name(
                                 card_info->usb_snd_mixer,
@@ -866,8 +883,8 @@
                  "%s: card_dev_type (0x%x), card_no(%d)",
                  __func__,  card_info->usb_device_type, card_info->usb_card);
         /* Currently only apply the first playback sound card configuration */
-        if ((is_playback && card_info->usb_device_type == AUDIO_DEVICE_OUT_USB_DEVICE) ||
-            ((!is_playback) && card_info->usb_device_type == AUDIO_DEVICE_IN_USB_DEVICE)){
+        if ((is_playback && usb_output_device(card_info->usb_device_type)) ||
+            (!is_playback && usb_input_device(card_info->usb_device_type))) {
             usb_audio_backend_apply_policy(&card_info->usb_device_conf_list,
                                            bit_width,
                                            sample_rate,
@@ -884,7 +901,7 @@
 #define _MAX(x, y) (((x) >= (y)) ? (x) : (y))
 #define _MIN(x, y) (((x) <= (y)) ? (x) : (y))
 
-int audio_extn_usb_get_max_channels()
+int audio_extn_usb_get_max_channels(bool is_playback)
 {
     struct listnode *node_i, *node_j;
     struct usb_device_config *dev_info;
@@ -892,6 +909,11 @@
     unsigned int max_ch = 1;
     list_for_each(node_i, &usbmod->usb_card_conf_list) {
             card_info = node_to_item(node_i, struct usb_card_config, list);
+            if (usb_output_device(card_info->usb_device_type) && !is_playback)
+                continue;
+            else if (usb_input_device(card_info->usb_device_type) && is_playback)
+                continue;
+
             list_for_each(node_j, &card_info->usb_device_conf_list) {
                 dev_info = node_to_item(node_j, struct usb_device_config, list);
                 max_ch = _MAX(max_ch, dev_info->channel_count);
@@ -901,7 +923,7 @@
     return max_ch;
 }
 
-int audio_extn_usb_get_max_bit_width()
+int audio_extn_usb_get_max_bit_width(bool is_playback)
 {
     struct listnode *node_i, *node_j;
     struct usb_device_config *dev_info;
@@ -909,6 +931,11 @@
     unsigned int max_bw = 16;
     list_for_each(node_i, &usbmod->usb_card_conf_list) {
             card_info = node_to_item(node_i, struct usb_card_config, list);
+            if (usb_output_device(card_info->usb_device_type) && !is_playback)
+                continue;
+            else if (usb_input_device(card_info->usb_device_type) && is_playback)
+                continue;
+
             list_for_each(node_j, &card_info->usb_device_conf_list) {
                 dev_info = node_to_item(node_j, struct usb_device_config, list);
                 max_bw = _MAX(max_bw, dev_info->bit_width);
@@ -918,7 +945,7 @@
     return max_bw;
 }
 
-int audio_extn_usb_sup_sample_rates(int type,
+int audio_extn_usb_sup_sample_rates(bool is_playback,
                                     uint32_t *sample_rates,
                                     uint32_t sample_rate_size)
 {
@@ -926,8 +953,7 @@
     struct usb_device_config *dev_info;
     struct usb_card_config *card_info;
 
-    if (type != USB_PLAYBACK && type != USB_CAPTURE)
-        return -1;
+    int type = is_playback ? USB_PLAYBACK : USB_CAPTURE;
 
     ALOGV("%s supported_sample_rates_mask 0x%x", __func__, supported_sample_rates_mask[type]);
     uint32_t bm = supported_sample_rates_mask[type];
@@ -997,22 +1023,24 @@
         goto exit;
     }
     list_init(&usb_card_info->usb_device_conf_list);
-    if (device & AUDIO_DEVICE_OUT_USB_DEVICE) {
+    if (usb_output_device(device)) {
         if (!usb_get_device_playback_config(usb_card_info, card)){
             usb_card_info->usb_card = card;
-            usb_card_info->usb_device_type = AUDIO_DEVICE_OUT_USB_DEVICE;
+            usb_card_info->usb_device_type = device;
             usb_get_sidetone_mixer(usb_card_info);
             list_add_tail(&usbmod->usb_card_conf_list, &usb_card_info->list);
             goto exit;
         }
-    } else if (device & AUDIO_DEVICE_IN_USB_DEVICE) {
+    } else if (usb_input_device(device)) {
         if (!usb_get_device_capture_config(usb_card_info, card)) {
             usb_card_info->usb_card = card;
-            usb_card_info->usb_device_type = AUDIO_DEVICE_IN_USB_DEVICE;
+            usb_card_info->usb_device_type = device;
             usbmod->is_capture_supported = true;
             list_add_tail(&usbmod->usb_card_conf_list, &usb_card_info->list);
             goto exit;
         }
+    } else {
+        ALOGW("%s: unknown device 0x%x", __func__, device);
     }
     /* free memory in error case */
     if (usb_card_info != NULL)
@@ -1060,6 +1088,9 @@
                 free(node_to_item(node_j, struct usb_device_config, list));
             }
             list_remove(node_i);
+            if (card_info->usb_snd_mixer) {
+                mixer_close(card_info->usb_snd_mixer);
+            }
             free(node_to_item(node_i, struct usb_card_config, list));
         }
     }
diff --git a/hal/audio_hw.c b/hal/audio_hw.c
index 3318cc1..2f5e6d1 100644
--- a/hal/audio_hw.c
+++ b/hal/audio_hw.c
@@ -247,6 +247,7 @@
     [USECASE_AUDIO_RECORD] = "audio-record",
     [USECASE_AUDIO_RECORD_LOW_LATENCY] = "low-latency-record",
     [USECASE_AUDIO_RECORD_MMAP] = "mmap-record",
+    [USECASE_AUDIO_RECORD_HIFI] = "hifi-record",
 
     [USECASE_AUDIO_HFP_SCO] = "hfp-sco",
     [USECASE_AUDIO_HFP_SCO_WB] = "hfp-sco-wb",
@@ -279,10 +280,12 @@
     uint32_t value;
 };
 
-static const struct string_to_enum out_channels_name_to_enum_table[] = {
+static const struct string_to_enum channels_name_to_enum_table[] = {
     STRING_TO_ENUM(AUDIO_CHANNEL_OUT_STEREO),
     STRING_TO_ENUM(AUDIO_CHANNEL_OUT_5POINT1),
     STRING_TO_ENUM(AUDIO_CHANNEL_OUT_7POINT1),
+    STRING_TO_ENUM(AUDIO_CHANNEL_IN_STEREO),
+    //TBD - string values for channel_in > 2?
 };
 
 static int set_voice_volume_l(struct audio_device *adev, float volume);
@@ -964,48 +967,95 @@
     return ret;
 }
 
-static int read_usb_sup_sample_rates(struct stream_out *out)
+static ssize_t read_usb_sup_sample_rates(bool is_playback,
+                                         uint32_t *supported_sample_rates,
+                                         uint32_t max_rates)
 {
-    uint32_t *sr = out->supported_sample_rates;
-    size_t count = audio_extn_usb_sup_sample_rates(0 /*playback*/,
-                                                   sr,
-                                                   MAX_SUPPORTED_SAMPLE_RATES);
+    ssize_t count = audio_extn_usb_sup_sample_rates(is_playback,
+                                                    supported_sample_rates,
+                                                    max_rates);
 #if !LOG_NDEBUG
-
-    for (size_t i=0; i<count; i++) {
-        ALOGV("%s %d", __func__, out->supported_sample_rates[i]);
+    for (ssize_t i=0; i<count; i++) {
+        ALOGV("%s %s %d", __func__, is_playback ? "P" : "C",
+              supported_sample_rates[i]);
     }
 #endif
-    return count > 0 ? 0 : -1;
+    return count;
 }
 
-static int read_usb_sup_channel_masks(struct stream_out *out)
+static int read_usb_sup_channel_masks(bool is_playback,
+                                      audio_channel_mask_t *supported_channel_masks,
+                                      uint32_t max_masks __unused)
 {
-    int channels = audio_extn_usb_get_max_channels();
-    out->supported_channel_masks[0] =
-            channels < 3 ? audio_channel_out_mask_from_count(channels) :
-                           audio_channel_mask_for_index_assignment_from_count(channels);
-    return 0;
+    int channels = audio_extn_usb_get_max_channels(is_playback);
+    if (is_playback) {
+        supported_channel_masks[0] =
+                channels < 3 ? audio_channel_out_mask_from_count(channels) :
+                               audio_channel_mask_for_index_assignment_from_count(channels);
+    } else {
+        supported_channel_masks[0] = audio_channel_in_mask_from_count(channels);
+    }
+    ALOGV("%s: %s supported ch %d", __func__,
+          is_playback ? "P" : "C", channels);
+    return 1;
 }
 
-static int read_usb_sup_formats(struct stream_out *out)
+static int read_usb_sup_formats(bool is_playback,
+                                audio_format_t *supported_formats,
+                                uint32_t max_formats __unused)
 {
-    int bitwidth = audio_extn_usb_get_max_bit_width();
+    int bitwidth = audio_extn_usb_get_max_bit_width(is_playback);
     switch (bitwidth) {
         case 24:
             // XXX : usb.c returns 24 for s24 and s24_le?
-            out->supported_formats[0] = AUDIO_FORMAT_PCM_24_BIT_PACKED;
+            supported_formats[0] = AUDIO_FORMAT_PCM_24_BIT_PACKED;
             break;
         case 32:
-            out->supported_formats[0] = AUDIO_FORMAT_PCM_32_BIT;
+            supported_formats[0] = AUDIO_FORMAT_PCM_32_BIT;
             break;
         case 16:
         default :
-            out->supported_formats[0] = AUDIO_FORMAT_PCM_16_BIT;
+            supported_formats[0] = AUDIO_FORMAT_PCM_16_BIT;
             break;
     }
+    ALOGV("%s: %s supported format %d", __func__,
+          is_playback ? "P" : "C", bitwidth);
+    return 1;
+}
 
-    return 0;
+static int read_usb_sup_params_and_compare(bool is_playback,
+                                           audio_format_t *format,
+                                           audio_format_t *supported_formats,
+                                           uint32_t max_formats,
+                                           audio_channel_mask_t *mask,
+                                           audio_channel_mask_t *supported_channel_masks,
+                                           uint32_t max_masks,
+                                           uint32_t *rate,
+                                           uint32_t *supported_sample_rates,
+                                           uint32_t max_rates) {
+    int ret = 0;
+    int num_formats;
+    int num_masks;
+    int num_rates;
+    int i;
+
+    num_formats = read_usb_sup_formats(is_playback, supported_formats,
+                                       max_formats);
+    num_masks = read_usb_sup_channel_masks(is_playback, supported_channel_masks,
+                                           max_masks);
+    num_rates = read_usb_sup_sample_rates(is_playback,
+                                          supported_sample_rates, max_rates);
+
+#define LUT(table, len, what, dflt)                  \
+    for (i=0; i<len && (table[i] != what); i++);    \
+    if (i==len) { ret |= (what == dflt ? 0 : -1); what=table[0]; }
+
+    LUT(supported_formats, num_formats, *format, AUDIO_FORMAT_DEFAULT);
+    LUT(supported_channel_masks, num_masks, *mask, AUDIO_CHANNEL_NONE);
+    LUT(supported_sample_rates, num_rates, *rate, 0);
+
+#undef LUT
+    return ret < 0 ? -EINVAL : 0; // HACK TBD
 }
 
 static audio_usecase_t get_voice_usecase_id_from_list(struct audio_device *adev)
@@ -2138,29 +2188,25 @@
     return status;
 }
 
-static char* out_get_parameters(const struct audio_stream *stream, const char *keys)
-{
-    struct stream_out *out = (struct stream_out *)stream;
-    struct str_parms *query = str_parms_create_str(keys);
-    char *str;
+static bool stream_get_parameter_channels(struct str_parms *query,
+                                          struct str_parms *reply,
+                                          audio_channel_mask_t *supported_channel_masks) {
+    int ret = -1;
     char value[256];
-    struct str_parms *reply = str_parms_create();
-    bool replied = false;
-    size_t i, j;
-    int ret;
     bool first = true;
-    ALOGV("%s: enter: keys - %s", __func__, keys);
-    ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, value, sizeof(value));
-    if (ret >= 0) {
+    size_t i, j;
+
+    if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_CHANNELS)) {
+        ret = 0;
         value[0] = '\0';
         i = 0;
-        while (out->supported_channel_masks[i] != 0) {
-            for (j = 0; j < ARRAY_SIZE(out_channels_name_to_enum_table); j++) {
-                if (out_channels_name_to_enum_table[j].value == out->supported_channel_masks[i]) {
+        while (supported_channel_masks[i] != 0) {
+            for (j = 0; j < ARRAY_SIZE(channels_name_to_enum_table); j++) {
+                if (channels_name_to_enum_table[j].value == supported_channel_masks[i]) {
                     if (!first) {
                         strcat(value, "|");
                     }
-                    strcat(value, out_channels_name_to_enum_table[j].name);
+                    strcat(value, channels_name_to_enum_table[j].name);
                     first = false;
                     break;
                 }
@@ -2168,13 +2214,21 @@
             i++;
         }
         str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_CHANNELS, value);
-        replied = true;
     }
+    return ret >= 0;
+}
 
-    ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_SUP_FORMATS, value, sizeof(value));
-    if (ret >= 0) {
+static bool stream_get_parameter_formats(struct str_parms *query,
+                                         struct str_parms *reply,
+                                         audio_format_t *supported_formats) {
+    int ret = -1;
+    char value[256];
+    int i;
+
+    if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_FORMATS)) {
+        ret = 0;
         value[0] = '\0';
-        switch (out->supported_formats[0]) {
+        switch (supported_formats[0]) {
             case AUDIO_FORMAT_PCM_16_BIT:
                 strcat(value, "AUDIO_FORMAT_PCM_16_BIT");
                 break;
@@ -2186,24 +2240,31 @@
                 break;
             default:
                 ALOGE("%s: unsupported format %#x", __func__,
-                      out->supported_formats[0]);
+                      supported_formats[0]);
                 break;
         }
         str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_FORMATS, value);
-        replied = true;
     }
+    return ret >= 0;
+}
 
-    ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES,
-                            value, sizeof(value));
-    if (ret >= 0) {
+static bool stream_get_parameter_rates(struct str_parms *query,
+                                       struct str_parms *reply,
+                                       uint32_t *supported_sample_rates) {
+
+    int i;
+    char value[256];
+    int ret = -1;
+    if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES)) {
+        ret = 0;
         value[0] = '\0';
         i=0;
         int cursor = 0;
-        while (out->supported_sample_rates[i]) {
+        while (supported_sample_rates[i]) {
             int avail = sizeof(value) - cursor;
             ret = snprintf(value + cursor, avail, "%s%d",
                            cursor > 0 ? "|" : "",
-                           out->supported_sample_rates[i]);
+                           supported_sample_rates[i]);
             if (ret < 0 || ret >= avail) {
                 // if cursor is at the last element of the array
                 //    overwrite with \0 is duplicate work as
@@ -2220,9 +2281,25 @@
         }
         str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES,
                           value);
-        replied = true;
     }
+    return ret >= 0;
+}
 
+static char* out_get_parameters(const struct audio_stream *stream, const char *keys)
+{
+    struct stream_out *out = (struct stream_out *)stream;
+    struct str_parms *query = str_parms_create_str(keys);
+    char *str;
+    struct str_parms *reply = str_parms_create();
+    bool replied = false;
+    ALOGV("%s: enter: keys - %s", __func__, keys);
+
+    replied |= stream_get_parameter_channels(query, reply,
+                                             &out->supported_channel_masks[0]);
+    replied |= stream_get_parameter_formats(query, reply,
+                                            &out->supported_formats[0]);
+    replied |= stream_get_parameter_rates(query, reply,
+                                          &out->supported_sample_rates[0]);
     if (replied) {
         str = str_parms_to_str(reply);
     } else {
@@ -2945,10 +3022,31 @@
     return status;
 }
 
-static char* in_get_parameters(const struct audio_stream *stream __unused,
-                               const char *keys __unused)
+static char* in_get_parameters(const struct audio_stream *stream,
+                               const char *keys)
 {
-    return strdup("");
+    struct stream_in *in = (struct stream_in *)stream;
+    struct str_parms *query = str_parms_create_str(keys);
+    char *str;
+    struct str_parms *reply = str_parms_create();
+    bool replied = false;
+
+    ALOGV("%s: enter: keys - %s", __func__, keys);
+    replied |= stream_get_parameter_channels(query, reply,
+                                             &in->supported_channel_masks[0]);
+    replied |= stream_get_parameter_formats(query, reply,
+                                            &in->supported_formats[0]);
+    replied |= stream_get_parameter_rates(query, reply,
+                                          &in->supported_sample_rates[0]);
+    if (replied) {
+        str = str_parms_to_str(reply);
+    } else {
+        str = strdup(keys);
+    }
+    str_parms_destroy(query);
+    str_parms_destroy(reply);
+    ALOGV("%s: exit: returns - %s", __func__, str);
+    return str;
 }
 
 static int in_set_gain(struct audio_stream_in *stream __unused, float gain __unused)
@@ -3334,7 +3432,10 @@
     struct audio_device *adev = (struct audio_device *)dev;
     struct stream_out *out;
     int i, ret;
-    const uint32_t direct_dev = (AUDIO_DEVICE_OUT_HDMI|AUDIO_DEVICE_OUT_USB_DEVICE);
+    bool is_hdmi = devices & AUDIO_DEVICE_OUT_AUX_DIGITAL;
+    bool is_usb_dev = audio_is_usb_out_device(devices) &&
+                      (devices != AUDIO_DEVICE_OUT_USB_ACCESSORY);
+    bool direct_dev = is_hdmi || is_usb_dev;
 
     ALOGV("%s: enter: sample_rate(%d) channel_mask(%#x) devices(%#x) flags(%#x)",
           __func__, config->sample_rate, config->channel_mask, devices, flags);
@@ -3356,18 +3457,27 @@
     /* Init use case and pcm_config */
     if (audio_is_linear_pcm(out->format) &&
         (out->flags == AUDIO_OUTPUT_FLAG_NONE ||
-         out->flags == AUDIO_OUTPUT_FLAG_DIRECT) &&
-        (out->devices & direct_dev)) {
-
-        bool hdmi = (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL);
-
+         out->flags == AUDIO_OUTPUT_FLAG_DIRECT) && direct_dev) {
         pthread_mutex_lock(&adev->lock);
-        if (out->devices & AUDIO_DEVICE_OUT_AUX_DIGITAL) {
+        if (is_hdmi) {
             ret = read_hdmi_channel_masks(out);
-        } else if (out->devices & AUDIO_DEVICE_OUT_USB_DEVICE) {
-            ret = read_usb_sup_formats(out) ||
-                  read_usb_sup_channel_masks(out) ||
-                  read_usb_sup_sample_rates(out);
+            if (config->sample_rate == 0)
+                config->sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
+            if (config->channel_mask == 0)
+                config->channel_mask = AUDIO_CHANNEL_OUT_5POINT1;
+            if (config->format == AUDIO_FORMAT_DEFAULT)
+                config->format = AUDIO_FORMAT_PCM_16_BIT;
+        } else if (is_usb_dev) {
+            ret = read_usb_sup_params_and_compare(true /*is_playback*/,
+                                                  &config->format,
+                                                  &out->supported_formats[0],
+                                                  MAX_SUPPORTED_FORMATS,
+                                                  &config->channel_mask,
+                                                  &out->supported_channel_masks[0],
+                                                  MAX_SUPPORTED_CHANNEL_MASKS,
+                                                  &config->sample_rate,
+                                                  &out->supported_sample_rates[0],
+                                                  MAX_SUPPORTED_SAMPLE_RATES);
             ALOGV("plugged dev USB ret %d", ret);
         } else {
             ret = -1;
@@ -3376,24 +3486,12 @@
         if (ret != 0)
             goto error_open;
 
-        if (config->sample_rate == 0) {
-            out->sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE;
-        } else {
-            out->sample_rate = config->sample_rate;
-        }
-        if (config->channel_mask == 0) {
-            out->channel_mask = AUDIO_CHANNEL_OUT_5POINT1;
-        } else {
-            out->channel_mask = config->channel_mask;
-        }
-        if (config->format == AUDIO_FORMAT_DEFAULT) {
-            out->format = AUDIO_FORMAT_PCM_16_BIT;
-        } else {
-            out->format = config->format;
-        }
+        out->channel_mask = config->channel_mask;
+        out->sample_rate = config->sample_rate;
+        out->format = config->format;
         out->usecase = USECASE_AUDIO_PLAYBACK_HIFI;
         // does this change?
-        out->config = hdmi ? pcm_config_hdmi_multi : pcm_config_hifi;
+        out->config = is_hdmi ? pcm_config_hdmi_multi : pcm_config_hifi;
         out->config.rate = config->sample_rate;
         out->config.channels = audio_channel_count_from_out_mask(out->channel_mask);
         out->config.period_size = HDMI_MULTI_PERIOD_BYTES / (out->config.channels * 2);
@@ -3901,6 +3999,8 @@
     int ret = 0, buffer_size, frame_size;
     int channel_count = audio_channel_count_from_in_mask(config->channel_mask);
     bool is_low_latency = false;
+    bool is_usb_dev = audio_is_usb_in_device(devices);
+    bool may_use_hifi_record = !(flags & (AUDIO_INPUT_FLAG_RAW|AUDIO_INPUT_FLAG_MMAP_NOIRQ));
 
     ALOGV("%s: enter", __func__);
     *stream_in = NULL;
@@ -3940,9 +4040,24 @@
     in->capture_handle = handle;
     in->flags = flags;
 
-    // restrict 24 bit capture for unprocessed source only
-    // for other sources if 24 bit requested reject 24 and set 16 bit capture only
-    if (config->format == AUDIO_FORMAT_DEFAULT) {
+    if (is_usb_dev && may_use_hifi_record) {
+        /* HiFi record selects an appropriate format, channel, rate combo
+           depending on sink capabilities*/
+        ret = read_usb_sup_params_and_compare(false /*is_playback*/,
+                                              &config->format,
+                                              &in->supported_formats[0],
+                                              MAX_SUPPORTED_FORMATS,
+                                              &config->channel_mask,
+                                              &in->supported_channel_masks[0],
+                                              MAX_SUPPORTED_CHANNEL_MASKS,
+                                              &config->sample_rate,
+                                              &in->supported_sample_rates[0],
+                                              MAX_SUPPORTED_SAMPLE_RATES);
+        if (ret != 0) {
+            ret = -EINVAL;
+            goto err_open;
+        }
+    } else if (config->format == AUDIO_FORMAT_DEFAULT) {
         config->format = AUDIO_FORMAT_PCM_16_BIT;
     } else if (config->format == AUDIO_FORMAT_PCM_FLOAT ||
                config->format == AUDIO_FORMAT_PCM_24_BIT_PACKED ||
@@ -3993,6 +4108,18 @@
         in->usecase = USECASE_AUDIO_RECORD_AFE_PROXY;
         in->config = pcm_config_afe_proxy_record;
         in->af_period_multiplier = 1;
+    } else if (is_usb_dev && may_use_hifi_record) {
+        in->usecase = USECASE_AUDIO_RECORD_HIFI;
+        in->config = pcm_config_audio_capture;
+        frame_size = audio_stream_in_frame_size(&in->stream);
+        buffer_size = get_input_buffer_size(config->sample_rate,
+                                            config->format,
+                                            channel_count,
+                                            false /*is_low_latency*/);
+        in->config.period_size = buffer_size / frame_size;
+        in->config.rate = config->sample_rate;
+        in->af_period_multiplier = 1;
+        in->config.format = pcm_format_from_audio_format(config->format);
     } else {
         in->usecase = USECASE_AUDIO_RECORD;
         if (config->sample_rate == LOW_LATENCY_CAPTURE_SAMPLE_RATE &&
@@ -4008,7 +4135,7 @@
                 buffer_size = get_input_buffer_size(config->sample_rate,
                                                     config->format,
                                                     channel_count,
-                                                   is_low_latency);
+                                                    is_low_latency);
                 in->config.period_size = buffer_size / frame_size;
                 in->config.rate = config->sample_rate;
                 in->af_period_multiplier = 1;
diff --git a/hal/audio_hw.h b/hal/audio_hw.h
index e410d64..b1e5e45 100644
--- a/hal/audio_hw.h
+++ b/hal/audio_hw.h
@@ -96,6 +96,7 @@
     USECASE_AUDIO_RECORD,
     USECASE_AUDIO_RECORD_LOW_LATENCY,
     USECASE_AUDIO_RECORD_MMAP,
+    USECASE_AUDIO_RECORD_HIFI,
 
     /* Voice extension usecases
      *
@@ -246,6 +247,14 @@
     audio_format_t format;
     card_status_t card_status;
     int capture_started;
+
+    struct stream_app_type_cfg app_type_cfg;
+
+    /* Array of supported channel mask configurations.
+       +1 so that the last entry is always 0 */
+    audio_channel_mask_t supported_channel_masks[MAX_SUPPORTED_CHANNEL_MASKS + 1];
+    audio_format_t supported_formats[MAX_SUPPORTED_FORMATS + 1];
+    uint32_t supported_sample_rates[MAX_SUPPORTED_SAMPLE_RATES + 1];
 };
 
 typedef enum usecase_type_t {
diff --git a/hal/msm8974/platform.c b/hal/msm8974/platform.c
index b9812b1..66719bb 100644
--- a/hal/msm8974/platform.c
+++ b/hal/msm8974/platform.c
@@ -165,6 +165,9 @@
     [USECASE_AUDIO_RECORD_MMAP] = {MMAP_RECORD_PCM_DEVICE,
             MMAP_RECORD_PCM_DEVICE},
 
+    [USECASE_AUDIO_RECORD_HIFI] = {MULTIMEDIA2_PCM_DEVICE,
+                                   MULTIMEDIA2_PCM_DEVICE},
+
     [USECASE_VOICE_CALL] = {VOICE_CALL_PCM_DEVICE,
                             VOICE_CALL_PCM_DEVICE},
     [USECASE_VOICE2_CALL] = {VOICE2_CALL_PCM_DEVICE, VOICE2_CALL_PCM_DEVICE},
@@ -553,6 +556,7 @@
     {TO_NAME_INDEX(USECASE_AUDIO_RECORD)},
     {TO_NAME_INDEX(USECASE_AUDIO_RECORD_LOW_LATENCY)},
     {TO_NAME_INDEX(USECASE_AUDIO_RECORD_MMAP)},
+    {TO_NAME_INDEX(USECASE_AUDIO_RECORD_HIFI)},
     {TO_NAME_INDEX(USECASE_VOICE_CALL)},
     {TO_NAME_INDEX(USECASE_VOICE2_CALL)},
     {TO_NAME_INDEX(USECASE_VOLTE_CALL)},
diff --git a/hal/platform_info.c b/hal/platform_info.c
index c63f244..cd2b5d4 100644
--- a/hal/platform_info.c
+++ b/hal/platform_info.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014-2017 The Android Open Source Project
+ * Copyright (C) 2014 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.