Add channel index record to AudioPolicyManager

Change-Id: Ic534bee084108a68c7818600df6e1c9dc9258a0d
diff --git a/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h b/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h
index f8c4d08..0b08430 100644
--- a/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h
+++ b/services/audiopolicy/common/managerdefinitions/include/ConfigParsingUtils.h
@@ -38,7 +38,8 @@
     uint32_t value;
 };
 
-#define STRING_TO_ENUM(string) { #string, string }
+// TODO: move to a separate file. Should be in sync with audio.h.
+#define STRING_TO_ENUM(string) { #string, (uint32_t)string } // uint32_t cast removes warning
 #define NAME_TO_ENUM(name, value) { name, value }
 #ifndef ARRAY_SIZE
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
@@ -204,6 +205,17 @@
     STRING_TO_ENUM(AUDIO_CHANNEL_IN_FRONT_BACK),
 };
 
+const StringToEnum sIndexChannelsNameToEnumTable[] = {
+    STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_1),
+    STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_2),
+    STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_3),
+    STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_4),
+    STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_5),
+    STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_6),
+    STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_7),
+    STRING_TO_ENUM(AUDIO_CHANNEL_INDEX_MASK_8),
+};
+
 const StringToEnum sGainModeNameToEnumTable[] = {
     STRING_TO_ENUM(AUDIO_GAIN_MODE_JOINT),
     STRING_TO_ENUM(AUDIO_GAIN_MODE_CHANNELS),
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
index f3978ec..647550c 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
@@ -235,8 +235,14 @@
                 (audio_channel_mask_t)ConfigParsingUtils::stringToEnum(sInChannelsNameToEnumTable,
                                                    ARRAY_SIZE(sInChannelsNameToEnumTable),
                                                    str);
+        if (channelMask == 0) { // if not found, check the channel index table
+            channelMask = (audio_channel_mask_t)
+                      ConfigParsingUtils::stringToEnum(sIndexChannelsNameToEnumTable,
+                              ARRAY_SIZE(sIndexChannelsNameToEnumTable),
+                              str);
+        }
         if (channelMask != 0) {
-            ALOGV("loadInChannels() adding channelMask %04x", channelMask);
+            ALOGV("loadInChannels() adding channelMask %#x", channelMask);
             mChannelMasks.add(channelMask);
         }
         str = strtok(NULL, "|");
@@ -441,30 +447,87 @@
     }
 
     const bool isRecordThread = mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SINK;
+    const bool isIndex = audio_channel_mask_get_representation(channelMask)
+            == AUDIO_CHANNEL_REPRESENTATION_INDEX;
+    int bestMatch = 0;
     for (size_t i = 0; i < mChannelMasks.size(); i ++) {
-        // FIXME Does not handle multi-channel automatic conversions yet
         audio_channel_mask_t supported = mChannelMasks[i];
         if (supported == channelMask) {
+            // Exact matches always taken.
             if (updatedChannelMask != NULL) {
                 *updatedChannelMask = channelMask;
             }
             return NO_ERROR;
         }
-        if (isRecordThread) {
-            // This uses hard-coded knowledge that AudioFlinger can silently down-mix and up-mix.
-            // FIXME Abstract this out to a table.
-            if (((supported == AUDIO_CHANNEL_IN_FRONT_BACK || supported == AUDIO_CHANNEL_IN_STEREO)
-                    && channelMask == AUDIO_CHANNEL_IN_MONO) ||
-                (supported == AUDIO_CHANNEL_IN_MONO && (channelMask == AUDIO_CHANNEL_IN_FRONT_BACK
-                    || channelMask == AUDIO_CHANNEL_IN_STEREO))) {
+
+        // AUDIO_CHANNEL_NONE (value: 0) is used for dynamic channel support
+        if (isRecordThread && supported != AUDIO_CHANNEL_NONE) {
+            // Approximate (best) match:
+            // The match score measures how well the supported channel mask matches the
+            // desired mask, where increasing-is-better.
+            //
+            // TODO: Some tweaks may be needed.
+            // Should be a static function of the data processing library.
+            //
+            // In priority:
+            // match score = 1000 if legacy channel conversion equivalent (always prefer this)
+            // OR
+            // match score += 100 if the channel mask representations match
+            // match score += number of channels matched.
+            //
+            // If there are no matched channels, the mask may still be accepted
+            // but the playback or record will be silent.
+            const bool isSupportedIndex = (audio_channel_mask_get_representation(supported)
+                    == AUDIO_CHANNEL_REPRESENTATION_INDEX);
+            int match;
+            if (isIndex && isSupportedIndex) {
+                // index equivalence
+                match = 100 + __builtin_popcount(
+                        audio_channel_mask_get_bits(channelMask)
+                            & audio_channel_mask_get_bits(supported));
+            } else if (isIndex && !isSupportedIndex) {
+                const uint32_t equivalentBits =
+                        (1 << audio_channel_count_from_in_mask(supported)) - 1 ;
+                match = __builtin_popcount(
+                        audio_channel_mask_get_bits(channelMask) & equivalentBits);
+            } else if (!isIndex && isSupportedIndex) {
+                const uint32_t equivalentBits =
+                        (1 << audio_channel_count_from_in_mask(channelMask)) - 1;
+                match = __builtin_popcount(
+                        equivalentBits & audio_channel_mask_get_bits(supported));
+            } else {
+                // positional equivalence
+                match = 100 + __builtin_popcount(
+                        audio_channel_mask_get_bits(channelMask)
+                            & audio_channel_mask_get_bits(supported));
+                switch (supported) {
+                case AUDIO_CHANNEL_IN_FRONT_BACK:
+                case AUDIO_CHANNEL_IN_STEREO:
+                    if (channelMask == AUDIO_CHANNEL_IN_MONO) {
+                        match = 1000;
+                    }
+                    break;
+                case AUDIO_CHANNEL_IN_MONO:
+                    if (channelMask == AUDIO_CHANNEL_IN_FRONT_BACK
+                            || channelMask == AUDIO_CHANNEL_IN_STEREO) {
+                        match = 1000;
+                    }
+                    break;
+                default:
+                    break;
+                }
+            }
+            if (match > bestMatch) {
+                bestMatch = match;
                 if (updatedChannelMask != NULL) {
                     *updatedChannelMask = supported;
+                } else {
+                    return NO_ERROR; // any match will do in this case.
                 }
-                return NO_ERROR;
             }
         }
     }
-    return BAD_VALUE;
+    return bestMatch > 0 ? NO_ERROR : BAD_VALUE;
 }
 
 status_t AudioPort::checkExactFormat(audio_format_t format) const