Add setMasterMono and getMasterMono

Bug: 15283594
Bug: 22700363
Change-Id: I32dc1fcecf285967a61bd508af3bb299595db57d
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 11dabae..3040833 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -842,15 +842,15 @@
         goto non_direct_output;
     }
 
-    // Do not allow offloading if one non offloadable effect is enabled. This prevents from
-    // creating an offloaded track and tearing it down immediately after start when audioflinger
-    // detects there is an active non offloadable effect.
+    // Do not allow offloading if one non offloadable effect is enabled or MasterMono is enabled.
+    // This prevents creating an offloaded track and tearing it down immediately after start
+    // when audioflinger detects there is an active non offloadable effect.
     // FIXME: We should check the audio session here but we do not have it in this context.
     // This may prevent offloading in rare situations where effects are left active by apps
     // in the background.
 
     if (((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) ||
-            !mEffects.isNonOffloadableEffectEnabled()) {
+            !(mEffects.isNonOffloadableEffectEnabled() || mMasterMono)) {
         profile = getProfileForDirectOutput(device,
                                            samplingRate,
                                            format,
@@ -2088,6 +2088,8 @@
     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");
+    result.append(buffer);
 
     write(fd, result.string(), result.size());
 
@@ -2115,6 +2117,10 @@
      offloadInfo.stream_type, offloadInfo.bit_rate, offloadInfo.duration_us,
      offloadInfo.has_video);
 
+    if (mMasterMono) {
+        return false; // no offloading if mono is set.
+    }
+
     // Check if offload has been disabled
     char propValue[PROPERTY_VALUE_MAX];
     if (property_get("audio.offload.disable", propValue, "0")) {
@@ -2878,6 +2884,44 @@
     return status;
 }
 
+status_t AudioPolicyManager::setMasterMono(bool mono)
+{
+    if (mMasterMono == mono) {
+        return NO_ERROR;
+    }
+    mMasterMono = mono;
+    // if enabling mono we close all offloaded devices, which will invalidate the
+    // corresponding AudioTrack. The AudioTrack client/MediaPlayer is responsible
+    // for recreating the new AudioTrack as non-offloaded PCM.
+    //
+    // If disabling mono, we leave all tracks as is: we don't know which clients
+    // and tracks are able to be recreated as offloaded. The next "song" should
+    // play back offloaded.
+    if (mMasterMono) {
+        Vector<audio_io_handle_t> offloaded;
+        for (size_t i = 0; i < mOutputs.size(); ++i) {
+            sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
+            if (desc->mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
+                offloaded.push(desc->mIoHandle);
+            }
+        }
+        for (size_t i = 0; i < offloaded.size(); ++i) {
+            closeOutput(offloaded[i]);
+        }
+    }
+    // update master mono for all remaining outputs
+    for (size_t i = 0; i < mOutputs.size(); ++i) {
+        updateMono(mOutputs.keyAt(i));
+    }
+    return NO_ERROR;
+}
+
+status_t AudioPolicyManager::getMasterMono(bool *mono)
+{
+    *mono = mMasterMono;
+    return NO_ERROR;
+}
+
 status_t AudioPolicyManager::disconnectAudioSource(const sp<AudioSourceDescriptor>& sourceDesc)
 {
     ALOGV("%s handle %d", __FUNCTION__, sourceDesc->getHandle());
@@ -2944,7 +2988,8 @@
     mBeaconMuteRefCount(0),
     mBeaconPlayingRefCount(0),
     mBeaconMuted(false),
-    mTtsOutputAvailable(false)
+    mTtsOutputAvailable(false),
+    mMasterMono(false)
 {
     audio_policy::EngineInstance *engineInstance = audio_policy::EngineInstance::getInstance();
     if (!engineInstance) {
@@ -3368,6 +3413,7 @@
 {
     outputDesc->setIoHandle(output);
     mOutputs.add(output, outputDesc);
+    updateMono(output); // update mono status when adding to output list
     nextAudioPortGeneration();
 }
 
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 59163ca..37faac2 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -24,6 +24,7 @@
 #include <utils/Errors.h>
 #include <utils/KeyedVector.h>
 #include <utils/SortedVector.h>
+#include <media/AudioParameter.h>
 #include <media/AudioPolicy.h>
 #include "AudioPolicyInterface.h"
 
@@ -231,6 +232,9 @@
                                           uid_t uid);
         virtual status_t stopAudioSource(audio_io_handle_t handle);
 
+        virtual status_t setMasterMono(bool mono);
+        virtual status_t getMasterMono(bool *mono);
+
         // Audio policy configuration file parsing (audio_policy.conf)
         // TODO candidates to be moved to ConfigParsingUtils
                 void defaultAudioPolicyConfig(void);
@@ -565,6 +569,7 @@
         bool mBeaconMuted;              // has STREAM_TTS been muted
         bool mTtsOutputAvailable;       // true if a dedicated output for TTS stream is available
 
+        bool mMasterMono;               // true if we wish to force all outputs to mono
         AudioPolicyMixCollection mPolicyMixes; // list of registered mixes
 
 #ifdef AUDIO_POLICY_TEST
@@ -644,6 +649,11 @@
                                                           audio_policy_dev_state_t state,
                                                           const char *device_address,
                                                           const char *device_name);
+        void updateMono(audio_io_handle_t output) {
+            AudioParameter param;
+            param.addInt(String8(AUDIO_PARAMETER_MONO_OUTPUT), (int)mMasterMono);
+            mpClientInterface->setParameters(output, param.toString());
+        }
 };
 
 };