AudioPolicyManager: force encoded surround setting

Bug: 26373761
Change-Id: I0f7a486bf1aa7a27f15cf220e2c75ef23f8f8536
Signed-off-by: Phil Burk <philburk@google.com>
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 8ef6b1fc..9cf59de 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "APM::AudioPolicyManager"
+#define LOG_TAG "AudioPolicyManager"
 //#define LOG_NDEBUG 0
 
 //#define VERY_VERBOSE_LOGGING
@@ -567,6 +567,7 @@
 {
     ALOGV("setForceUse() usage %d, config %d, mPhoneState %d", usage, config, mEngine->getPhoneState());
 
+    audio_policy_forced_cfg_t originalConfig = mEngine->getForceUse(usage);
     if (mEngine->setForceUse(usage, config) != NO_ERROR) {
         ALOGW("setForceUse() could not set force cfg %d for usage %d", config, usage);
         return;
@@ -579,6 +580,33 @@
     checkA2dpSuspend();
     checkOutputForAllStrategies();
     updateDevicesAndOutputs();
+
+    // Did surround forced use change?
+    if ((usage == AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND)
+            && (originalConfig != config)) {
+        const char *device_address  = "";
+        // Is it currently connected? If so then cycle the connection
+        // so that the supported surround formats will be reloaded.
+        //
+        // FIXME As S/PDIF is not a removable device we have to handle this differently.
+        // Probably by updating the device descriptor directly and manually
+        // tearing down active playback on S/PDIF
+        if (getDeviceConnectionState(AUDIO_DEVICE_OUT_HDMI, device_address) ==
+                                             AUDIO_POLICY_DEVICE_STATE_AVAILABLE) {
+            // Disconnect and reconnect output devices so that the surround
+            // encodings can be updated.
+            const char *device_name = "";
+            // disconnect
+            setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_HDMI,
+                        AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+                        device_address, device_name);
+            // reconnect
+            setDeviceConnectionStateInt(AUDIO_DEVICE_OUT_HDMI,
+                        AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+                        device_address, device_name);
+        }
+    }
+
     if (mEngine->getPhoneState() == AUDIO_MODE_IN_CALL && hasPrimaryOutput()) {
         audio_devices_t newDevice = getNewOutputDevice(mPrimaryOutput, true /*fromCache*/);
         updateCallRouting(newDevice);
@@ -2115,6 +2143,9 @@
     snprintf(buffer, SIZE, " Force use for hdmi system audio %d\n",
             mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO));
     result.append(buffer);
+    snprintf(buffer, SIZE, " Force use for encoded surround output %d\n",
+            mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND));
+    result.append(buffer);
     snprintf(buffer, SIZE, " TTS output %s\n", mTtsOutputAvailable ? "available" : "not available");
     result.append(buffer);
     snprintf(buffer, SIZE, " Master mono: %s\n", mMasterMono ? "on" : "off");
@@ -5144,6 +5175,84 @@
     }
 }
 
+// Modify the list of surround sound formats supported.
+void AudioPolicyManager::filterSurroundFormats(FormatVector &formats) {
+
+    audio_policy_forced_cfg_t forceUse = mEngine->getForceUse(
+            AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND);
+    ALOGI("%s: forced use = %d", __FUNCTION__, forceUse);
+
+    // Analyze original support for various formats.
+    bool supportsRawSurround = false;
+    bool supportsIEC61937 = false;
+    for (size_t formatIndex = 0; formatIndex < formats.size(); formatIndex++) {
+        audio_format_t format = formats[formatIndex];
+        ALOGI("%s: original formats: #%x", __FUNCTION__, format);
+        switch (format) {
+            case AUDIO_FORMAT_AC3:
+            case AUDIO_FORMAT_E_AC3:
+            case AUDIO_FORMAT_DTS:
+            case AUDIO_FORMAT_DTS_HD:
+                supportsRawSurround = true;
+                break;
+            case AUDIO_FORMAT_IEC61937:
+                supportsIEC61937 = true;
+                break;
+            default:
+                break;
+        }
+    }
+    ALOGI("%s: supportsRawSurround = %d, supportsIEC61937 = %d",
+            __FUNCTION__, supportsRawSurround, supportsIEC61937);
+
+    // Modify formats based on surround preferences.
+    // If NEVER, remove support for surround formats.
+    if ((forceUse == AUDIO_POLICY_FORCE_ENCODED_SURROUND_NEVER)
+            && (supportsRawSurround || supportsIEC61937)) {
+        // Remove surround sound related formats.
+        for (size_t formatIndex = 0; formatIndex < formats.size(); ) {
+            audio_format_t format = formats[formatIndex];
+            switch(format) {
+                case AUDIO_FORMAT_AC3:
+                case AUDIO_FORMAT_E_AC3:
+                case AUDIO_FORMAT_DTS:
+                case AUDIO_FORMAT_DTS_HD:
+                case AUDIO_FORMAT_IEC61937:
+                    ALOGI("%s: remove #%x", __FUNCTION__, format);
+                    formats.removeAt(formatIndex);
+                    break;
+                default:
+                    formatIndex++; // keep it
+                    break;
+            }
+        }
+        supportsRawSurround = false;
+        supportsIEC61937 = false;
+    }
+    // If ALWAYS, add support for raw surround formats if all are missing.
+    // This assumes that if any of these formats are reported by the HAL
+    // then the report is valid and should not be modified.
+    if ((forceUse == AUDIO_POLICY_FORCE_ENCODED_SURROUND_ALWAYS)
+            && !supportsRawSurround) {
+        formats.add(AUDIO_FORMAT_AC3);
+        formats.add(AUDIO_FORMAT_E_AC3);
+        formats.add(AUDIO_FORMAT_DTS);
+        formats.add(AUDIO_FORMAT_DTS_HD);
+        supportsRawSurround = true;
+    }
+    // Add support for IEC61937 if raw surround supported.
+    // The HAL could do this but add it here, just in case.
+    if (supportsRawSurround && !supportsIEC61937) {
+        formats.add(AUDIO_FORMAT_IEC61937);
+        // supportsIEC61937 = true;
+    }
+    // Just for debugging.
+    for (size_t formatIndex = 0; formatIndex < formats.size(); formatIndex++) {
+        audio_format_t format = formats[formatIndex];
+        ALOGI("%s: final formats: #%x", __FUNCTION__, format);
+    }
+}
+
 void AudioPolicyManager::updateAudioProfiles(audio_io_handle_t ioHandle,
                                              AudioProfileVector &profiles)
 {
@@ -5153,14 +5262,16 @@
     if (profiles.hasDynamicFormat()) {
         reply = mpClientInterface->getParameters(ioHandle,
                                                  String8(AUDIO_PARAMETER_STREAM_SUP_FORMATS));
-        ALOGV("%s: supported formats %s", __FUNCTION__, reply.string());
+        ALOGI("%s: supported formats %s", __FUNCTION__, reply.string());
         AudioParameter repliedParameters(reply);
         if (repliedParameters.get(
                 String8(AUDIO_PARAMETER_STREAM_SUP_FORMATS), reply) != NO_ERROR) {
             ALOGE("%s: failed to retrieve format, bailing out", __FUNCTION__);
             return;
         }
-        profiles.setFormats(formatsFromString(reply.string()));
+        FormatVector formats = formatsFromString(reply.string());
+        filterSurroundFormats(formats);
+        profiles.setFormats(formats);
     }
     const FormatVector &supportedFormats = profiles.getSupportedFormats();