audio policy: select output with best sample format match

Add rules allowing selection of the output stream with the sample
format offering the best match to the client track sample format
when more than one output is compatible with a request.

Bug: 27210844

Change-Id: I31981940be5d1c06d713d52187cfb0a7bf355773
diff --git a/services/audiopolicy/common/include/policy.h b/services/audiopolicy/common/include/policy.h
index e3ee4e5..18acb23 100755
--- a/services/audiopolicy/common/include/policy.h
+++ b/services/audiopolicy/common/include/policy.h
@@ -126,3 +126,18 @@
     }
     return 0;
 }
+
+/* Indicates if audio formats are equivalent when considering a match between
+ * audio HAL supported formats and client requested formats
+ */
+static inline bool audio_formats_match(audio_format_t format1,
+                                       audio_format_t format2)
+{
+    if (audio_is_linear_pcm(format1) &&
+            (audio_bytes_per_sample(format1) > 2) &&
+            audio_is_linear_pcm(format2) &&
+            (audio_bytes_per_sample(format2) > 2)) {
+        return true;
+    }
+    return format1 == format2;
+}
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
index 0da3aea..e19a8ee 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPort.h
@@ -110,6 +110,12 @@
 
     static int compareFormats(audio_format_t format1, audio_format_t format2);
 
+    // Used to select an audio HAL output stream with a sample format providing the
+    // less degradation for a given AudioTrack sample format.
+    static bool isBetterFormatMatch(audio_format_t newFormat,
+                                        audio_format_t currentFormat,
+                                        audio_format_t targetFormat);
+
     audio_module_handle_t getModuleHandle() const;
     uint32_t getModuleVersion() const;
     const char *getModuleName() const;
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
index 191439e..bda59ad 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPort.cpp
@@ -268,6 +268,32 @@
     return index1 - index2;
 }
 
+bool AudioPort::isBetterFormatMatch(audio_format_t newFormat,
+                                    audio_format_t currentFormat,
+                                    audio_format_t targetFormat)
+{
+    if (newFormat == currentFormat) {
+        return false;
+    }
+    if (currentFormat == AUDIO_FORMAT_INVALID) {
+        return true;
+    }
+    if (newFormat == targetFormat) {
+        return true;
+    }
+    int currentDiffBytes = (int)audio_bytes_per_sample(targetFormat) -
+            audio_bytes_per_sample(currentFormat);
+    int newDiffBytes = (int)audio_bytes_per_sample(targetFormat) -
+            audio_bytes_per_sample(newFormat);
+
+    if (abs(newDiffBytes) < abs(currentDiffBytes)) {
+        return true;
+    } else if (abs(newDiffBytes) == abs(currentDiffBytes)) {
+        return (newDiffBytes >= 0);
+    }
+    return false;
+}
+
 void AudioPort::pickAudioProfile(uint32_t &samplingRate,
                                  audio_channel_mask_t &channelMask,
                                  audio_format_t &format) const
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp
index c599665..961072e 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioProfile.cpp
@@ -31,7 +31,7 @@
 status_t AudioProfile::checkExact(uint32_t samplingRate, audio_channel_mask_t channelMask,
                                   audio_format_t format) const
 {
-    if (format == mFormat &&
+    if (audio_formats_match(format, mFormat) &&
             (mChannelMasks.isEmpty() || supportsChannels(channelMask)) &&
             (mSamplingRates.isEmpty() || supportsRate(samplingRate))) {
         return NO_ERROR;
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 8bb49fa..6b9cb83 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -876,7 +876,7 @@
                 outputDesc = desc;
                 // reuse direct output if currently open and configured with same parameters
                 if ((samplingRate == outputDesc->mSamplingRate) &&
-                        (format == outputDesc->mFormat) &&
+                        audio_formats_match(format, outputDesc->mFormat) &&
                         (channelMask == outputDesc->mChannelMask)) {
                     outputDesc->mDirectOpenCount++;
                     ALOGV("getOutput() reusing direct output %d", mOutputs.keyAt(i));
@@ -927,7 +927,7 @@
         // only accept an output with the requested parameters
         if (status != NO_ERROR ||
             (samplingRate != 0 && samplingRate != config.sample_rate) ||
-            (format != AUDIO_FORMAT_DEFAULT && format != config.format) ||
+            (format != AUDIO_FORMAT_DEFAULT && !audio_formats_match(format, config.format)) ||
             (channelMask != 0 && channelMask != config.channel_mask)) {
             ALOGV("getOutput() failed opening direct output: output %d samplingRate %d %d,"
                     "format %d %d, channelMask %04x %04x", output, samplingRate,
@@ -992,8 +992,9 @@
     // devices (the list was previously build by getOutputsForDevice()).
     // The priority is as follows:
     // 1: the output with the highest number of requested policy flags
-    // 2: the primary output
-    // 3: the first output in the list
+    // 2: the output with the bit depth the closest to the requested one
+    // 3: the primary output
+    // 4: the first output in the list
 
     if (outputs.size() == 0) {
         return 0;
@@ -1003,8 +1004,11 @@
     }
 
     int maxCommonFlags = 0;
-    audio_io_handle_t outputFlags = 0;
-    audio_io_handle_t outputPrimary = 0;
+    audio_io_handle_t outputForFlags = 0;
+    audio_io_handle_t outputForPrimary = 0;
+    audio_io_handle_t outputForFormat = 0;
+    audio_format_t bestFormat = AUDIO_FORMAT_INVALID;
+    audio_format_t bestFormatForFlags = AUDIO_FORMAT_INVALID;
 
     for (size_t i = 0; i < outputs.size(); i++) {
         sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(outputs[i]);
@@ -1012,31 +1016,48 @@
             // if a valid format is specified, skip output if not compatible
             if (format != AUDIO_FORMAT_INVALID) {
                 if (outputDesc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) {
-                    if (format != outputDesc->mFormat) {
+                    if (!audio_formats_match(format, outputDesc->mFormat)) {
                         continue;
                     }
                 } else if (!audio_is_linear_pcm(format)) {
                     continue;
                 }
+                if (AudioPort::isBetterFormatMatch(
+                        outputDesc->mFormat, bestFormat, format)) {
+                    outputForFormat = outputs[i];
+                    bestFormat = outputDesc->mFormat;
+                }
             }
 
             int commonFlags = popcount(outputDesc->mProfile->getFlags() & flags);
-            if (commonFlags > maxCommonFlags) {
-                outputFlags = outputs[i];
-                maxCommonFlags = commonFlags;
+            if (commonFlags >= maxCommonFlags) {
+                if (commonFlags == maxCommonFlags) {
+                    if (AudioPort::isBetterFormatMatch(
+                            outputDesc->mFormat, bestFormatForFlags, format)) {
+                        outputForFlags = outputs[i];
+                        bestFormatForFlags = outputDesc->mFormat;
+                    }
+                } else {
+                    outputForFlags = outputs[i];
+                    maxCommonFlags = commonFlags;
+                    bestFormatForFlags = outputDesc->mFormat;
+                }
                 ALOGV("selectOutput() commonFlags for output %d, %04x", outputs[i], commonFlags);
             }
             if (outputDesc->mProfile->getFlags() & AUDIO_OUTPUT_FLAG_PRIMARY) {
-                outputPrimary = outputs[i];
+                outputForPrimary = outputs[i];
             }
         }
     }
 
-    if (outputFlags != 0) {
-        return outputFlags;
+    if (outputForFlags != 0) {
+        return outputForFlags;
     }
-    if (outputPrimary != 0) {
-        return outputPrimary;
+    if (outputForFormat != 0) {
+        return outputForFormat;
+    }
+    if (outputForPrimary != 0) {
+        return outputForPrimary;
     }
 
     return outputs[0];
@@ -1509,7 +1530,7 @@
             // and current input properties are not exactly as requested.
             if ((desc->mSamplingRate != samplingRate ||
                     desc->mChannelMask != channelMask ||
-                    desc->mFormat != format) &&
+                    !audio_formats_match(desc->mFormat, format)) &&
                     (source_priority(desc->getHighestPrioritySource(false /*activeOnly*/)) <
                      source_priority(inputSource))) {
                 ALOGV("%s: ", __FUNCTION__);
@@ -1544,7 +1565,7 @@
     // only accept input with the exact requested set of parameters
     if (status != NO_ERROR || input == AUDIO_IO_HANDLE_NONE ||
         (profileSamplingRate != config.sample_rate) ||
-        (profileFormat != config.format) ||
+        !audio_formats_match(profileFormat, config.format) ||
         (profileChannelMask != config.channel_mask)) {
         ALOGW("getInputForAttr() failed opening input: samplingRate %d"
               ", format %d, channelMask %x",