audio policy: volume control reorganization

Output volume and routing control by AudioOutputDescriptor
is reorganized to prepare hardware source volume and routing
control.
AudioOutputDescriptor contains all volume, device and activity
state common to software (audio flinger mixers) and
hardware sources (tuners, A2DP, HDMI).
A new class SwAudioOutputDescriptor is derived from
AudioOutputDescriptor and is specific to software sources.

Low level routing and volume control methods receive an
AudioOutputDescriptor parameter instead of an IO handle.

mPrimaryOutput is now an AudioOutputDescriptor.

Change-Id: Ie90943ee3102cdb8adf89fdd2addd2c279b1e5bf
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index ffa689a..b5ff9a9 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -157,7 +157,7 @@
         // outputs must be closed after checkOutputForAllStrategies() is executed
         if (!outputs.isEmpty()) {
             for (size_t i = 0; i < outputs.size(); i++) {
-                sp<AudioOutputDescriptor> desc = mOutputs.valueFor(outputs[i]);
+                sp<SwAudioOutputDescriptor> desc = mOutputs.valueFor(outputs[i]);
                 // close unused outputs after device disconnection or direct outputs that have been
                 // opened by checkOutputsForDevice() to query dynamic parameters
                 if ((state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) ||
@@ -176,18 +176,17 @@
             updateCallRouting(newDevice);
         }
         for (size_t i = 0; i < mOutputs.size(); i++) {
-            audio_io_handle_t output = mOutputs.keyAt(i);
-            if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (output != mPrimaryOutput)) {
-                audio_devices_t newDevice = getNewOutputDevice(mOutputs.keyAt(i),
-                                                               true /*fromCache*/);
+            sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
+            if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (desc != mPrimaryOutput)) {
+                audio_devices_t newDevice = getNewOutputDevice(desc, true /*fromCache*/);
                 // do not force device change on duplicated output because if device is 0, it will
                 // also force a device 0 for the two outputs it is duplicated to which may override
                 // a valid device selection on those outputs.
-                bool force = !mOutputs.valueAt(i)->isDuplicated()
+                bool force = !desc->isDuplicated()
                         && (!device_distinguishes_on_address(device)
                                 // always force when disconnecting (a non-duplicated device)
                                 || (state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE));
-                setOutputDevice(output, newDevice, force, 0);
+                setOutputDevice(desc, newDevice, force, 0);
             }
         }
 
@@ -349,7 +348,7 @@
                                                 AUDIO_OUTPUT_FLAG_NONE,
                                                 AUDIO_FORMAT_INVALID);
         if (output != AUDIO_IO_HANDLE_NONE) {
-            sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
+            sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
             ALOG_ASSERT(!outputDesc->isDuplicated(),
                         "updateCallRouting() RX device output is duplicated");
             outputDesc->toAudioPortConfig(&patch.sources[1]);
@@ -450,13 +449,13 @@
     checkOutputForAllStrategies();
     updateDevicesAndOutputs();
 
-    sp<AudioOutputDescriptor> hwOutputDesc = mOutputs.valueFor(mPrimaryOutput);
+    sp<SwAudioOutputDescriptor> hwOutputDesc = mPrimaryOutput;
 
     int delayMs = 0;
     if (isStateInCall(state)) {
         nsecs_t sysTime = systemTime();
         for (size_t i = 0; i < mOutputs.size(); i++) {
-            sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
+            sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
             // mute media and sonification strategies and delay device switch by the largest
             // latency of any output where either strategy is active.
             // This avoid sending the ring tone or music tail into the earpiece or headset.
@@ -466,14 +465,14 @@
                  isStrategyActive(desc, STRATEGY_SONIFICATION,
                                   SONIFICATION_HEADSET_MUSIC_DELAY,
                                   sysTime)) &&
-                    (delayMs < (int)desc->mLatency*2)) {
-                delayMs = desc->mLatency*2;
+                    (delayMs < (int)desc->latency()*2)) {
+                delayMs = desc->latency()*2;
             }
-            setStrategyMute(STRATEGY_MEDIA, true, mOutputs.keyAt(i));
-            setStrategyMute(STRATEGY_MEDIA, false, mOutputs.keyAt(i), MUTE_TIME_MS,
+            setStrategyMute(STRATEGY_MEDIA, true, desc);
+            setStrategyMute(STRATEGY_MEDIA, false, desc, MUTE_TIME_MS,
                 getDeviceForStrategy(STRATEGY_MEDIA, true /*fromCache*/));
-            setStrategyMute(STRATEGY_SONIFICATION, true, mOutputs.keyAt(i));
-            setStrategyMute(STRATEGY_SONIFICATION, false, mOutputs.keyAt(i), MUTE_TIME_MS,
+            setStrategyMute(STRATEGY_SONIFICATION, true, desc);
+            setStrategyMute(STRATEGY_SONIFICATION, false, desc, MUTE_TIME_MS,
                 getDeviceForStrategy(STRATEGY_SONIFICATION, true /*fromCache*/));
         }
     }
@@ -549,13 +548,13 @@
         updateCallRouting(newDevice);
     }
     for (size_t i = 0; i < mOutputs.size(); i++) {
-        audio_io_handle_t output = mOutputs.keyAt(i);
-        audio_devices_t newDevice = getNewOutputDevice(output, true /*fromCache*/);
-        if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (output != mPrimaryOutput)) {
-            setOutputDevice(output, newDevice, (newDevice != AUDIO_DEVICE_NONE));
+        sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueAt(i);
+        audio_devices_t newDevice = getNewOutputDevice(outputDesc, true /*fromCache*/);
+        if ((mEngine->getPhoneState() != AUDIO_MODE_IN_CALL) || (outputDesc != mPrimaryOutput)) {
+            setOutputDevice(outputDesc, newDevice, (newDevice != AUDIO_DEVICE_NONE));
         }
         if (forceVolumeReeval && (newDevice != AUDIO_DEVICE_NONE)) {
-            applyStreamVolumes(output, newDevice, 0, true);
+            applyStreamVolumes(outputDesc, newDevice, 0, true);
         }
     }
 
@@ -642,7 +641,7 @@
         }
         stream_type_to_audio_attributes(*stream, &attributes);
     }
-    sp<AudioOutputDescriptor> desc;
+    sp<SwAudioOutputDescriptor> desc;
     if (mPolicyMixes.getOutputForAttr(attributes, desc) == NO_ERROR) {
         ALOG_ASSERT(desc != 0, "Invalid desc returned by getOutputForAttr");
         if (!audio_is_linear_pcm(format)) {
@@ -713,7 +712,8 @@
 
         if (mTestOutputs[mCurOutput] == 0) {
             ALOGV("getOutput() opening test output");
-            sp<AudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(NULL);
+            sp<AudioOutputDescriptor> outputDesc = new SwAudioOutputDescriptor(NULL,
+                                                                               mpClientInterface);
             outputDesc->mDevice = mTestDevice;
             outputDesc->mLatency = mTestLatencyMs;
             outputDesc->mFlags =
@@ -789,10 +789,10 @@
     }
 
     if (profile != 0) {
-        sp<AudioOutputDescriptor> outputDesc = NULL;
+        sp<SwAudioOutputDescriptor> outputDesc = NULL;
 
         for (size_t i = 0; i < mOutputs.size(); i++) {
-            sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
+            sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
             if (!desc->isDuplicated() && (profile == desc->mProfile)) {
                 outputDesc = desc;
                 // reuse direct output if currently open and configured with same parameters
@@ -809,7 +809,7 @@
         if (outputDesc != NULL) {
             closeOutput(outputDesc->mIoHandle);
         }
-        outputDesc = new AudioOutputDescriptor(profile);
+        outputDesc = new SwAudioOutputDescriptor(profile, mpClientInterface);
         outputDesc->mDevice = device;
         outputDesc->mLatency = 0;
         outputDesc->mFlags =(audio_output_flags_t) (outputDesc->mFlags | flags);
@@ -915,7 +915,7 @@
     audio_io_handle_t outputPrimary = 0;
 
     for (size_t i = 0; i < outputs.size(); i++) {
-        sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(outputs[i]);
+        sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(outputs[i]);
         if (!outputDesc->isDuplicated()) {
             // if a valid format is specified, skip output if not compatible
             if (format != AUDIO_FORMAT_INVALID) {
@@ -962,8 +962,51 @@
         return BAD_VALUE;
     }
 
+    sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueAt(index);
+
+    audio_devices_t newDevice;
+    if (outputDesc->mPolicyMix != NULL) {
+        newDevice = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
+    } else {
+        newDevice = AUDIO_DEVICE_NONE;
+    }
+
+    uint32_t delayMs = 0;
+
+    // Routing?
+    mOutputRoutes.incRouteActivity(session);
+
+    status_t status = startSource(outputDesc, stream, newDevice, &delayMs);
+
+    if (status != NO_ERROR) {
+        mOutputRoutes.decRouteActivity(session);
+    }
+    // Automatically enable the remote submix input when output is started on a re routing mix
+    // of type MIX_TYPE_RECORDERS
+    if (audio_is_remote_submix_device(newDevice) && outputDesc->mPolicyMix != NULL &&
+            outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) {
+            setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
+                    AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
+                    outputDesc->mPolicyMix->mRegistrationId,
+                    "remote-submix");
+    }
+
+    if (delayMs != 0) {
+        usleep(delayMs * 1000);
+    }
+
+    return status;
+}
+
+status_t AudioPolicyManager::startSource(sp<AudioOutputDescriptor> outputDesc,
+                                             audio_stream_type_t stream,
+                                             audio_devices_t device,
+                                             uint32_t *delayMs)
+{
     // cannot start playback of STREAM_TTS if any other output is being used
     uint32_t beaconMuteLatency = 0;
+
+    *delayMs = 0;
     if (stream == AUDIO_STREAM_TTS) {
         ALOGV("\t found BEACON stream");
         if (mOutputs.isAnyOutputActive(AUDIO_STREAM_TTS /*streamToIgnore*/)) {
@@ -976,22 +1019,15 @@
         beaconMuteLatency = handleEventForBeacon(STARTING_OUTPUT);
     }
 
-    sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(index);
     // increment usage count for this stream on the requested output:
     // NOTE that the usage count is the same for duplicated output and hardware output which is
     // necessary for a correct control of hardware output routing by startOutput() and stopOutput()
     outputDesc->changeRefCount(stream, 1);
 
-    // Routing?
-    mOutputRoutes.incRouteActivity(session);
-
     if (outputDesc->mRefCount[stream] == 1) {
         // starting an output being rerouted?
-        audio_devices_t newDevice;
-        if (outputDesc->mPolicyMix != NULL) {
-            newDevice = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
-        } else {
-            newDevice = getNewOutputDevice(output, false /*fromCache*/);
+        if (device == AUDIO_DEVICE_NONE) {
+            device = getNewOutputDevice(outputDesc, false /*fromCache*/);
         }
         routing_strategy strategy = getStrategy(stream);
         bool shouldWait = (strategy == STRATEGY_SONIFICATION) ||
@@ -1007,7 +1043,7 @@
                 // In this case, the audio HAL must receive the new device selection so that it can
                 // change the device currently selected by the other active output.
                 if (outputDesc->sharesHwModuleWith(desc) &&
-                    desc->device() != newDevice) {
+                    desc->device() != device) {
                     force = true;
                 }
                 // wait for audio on other active outputs to be presented when starting
@@ -1019,7 +1055,7 @@
                 }
             }
         }
-        uint32_t muteWaitMs = setOutputDevice(output, newDevice, force);
+        uint32_t muteWaitMs = setOutputDevice(outputDesc, device, force);
 
         // handle special case for sonification while in call
         if (isInCall()) {
@@ -1028,32 +1064,18 @@
 
         // apply volume rules for current stream and device if necessary
         checkAndSetVolume(stream,
-                          mStreams[stream].getVolumeIndex(newDevice),
-                          output,
-                          newDevice);
+                          mStreams.valueFor(stream).getVolumeIndex(device),
+                          outputDesc,
+                          device);
 
         // update the outputs if starting an output with a stream that can affect notification
         // routing
         handleNotificationRoutingForStream(stream);
 
-        // Automatically enable the remote submix input when output is started on a re routing mix
-        // of type MIX_TYPE_RECORDERS
-        if (audio_is_remote_submix_device(newDevice) && outputDesc->mPolicyMix != NULL &&
-                outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) {
-                setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
-                        AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
-                        outputDesc->mPolicyMix->mRegistrationId,
-                        "remote-submix");
-        }
-
         // force reevaluating accessibility routing when ringtone or alarm starts
         if (strategy == STRATEGY_SONIFICATION) {
             mpClientInterface->invalidateStream(AUDIO_STREAM_ACCESSIBILITY);
         }
-
-        if (waitMs > muteWaitMs) {
-            usleep((waitMs - muteWaitMs) * 2 * 1000);
-        }
     }
     return NO_ERROR;
 }
@@ -1070,8 +1092,32 @@
         return BAD_VALUE;
     }
 
-    sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(index);
+    sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueAt(index);
 
+    if (outputDesc->mRefCount[stream] == 1) {
+        // Automatically disable the remote submix input when output is stopped on a
+        // re routing mix of type MIX_TYPE_RECORDERS
+        if (audio_is_remote_submix_device(outputDesc->mDevice) &&
+                outputDesc->mPolicyMix != NULL &&
+                outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) {
+            setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
+                    AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+                    outputDesc->mPolicyMix->mRegistrationId,
+                    "remote-submix");
+        }
+    }
+
+    // Routing?
+    if (outputDesc->mRefCount[stream] > 0) {
+        mOutputRoutes.decRouteActivity(session);
+    }
+
+    return stopSource(outputDesc, stream);
+}
+
+status_t AudioPolicyManager::stopSource(sp<AudioOutputDescriptor> outputDesc,
+                                            audio_stream_type_t stream)
+{
     // always handle stream stop, check which stream type is stopping
     handleEventForBeacon(stream == AUDIO_STREAM_TTS ? STOPPING_BEACON : STOPPING_OUTPUT);
 
@@ -1084,44 +1130,30 @@
         // decrement usage count of this stream on the output
         outputDesc->changeRefCount(stream, -1);
 
-        // Routing?
-        mOutputRoutes.decRouteActivity(session);
-
         // store time at which the stream was stopped - see isStreamActive()
         if (outputDesc->mRefCount[stream] == 0) {
-            // Automatically disable the remote submix input when output is stopped on a
-            // re routing mix of type MIX_TYPE_RECORDERS
-            if (audio_is_remote_submix_device(outputDesc->mDevice) &&
-                    outputDesc->mPolicyMix != NULL &&
-                    outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) {
-                setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
-                        AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
-                        outputDesc->mPolicyMix->mRegistrationId,
-                        "remote-submix");
-            }
-
             outputDesc->mStopTime[stream] = systemTime();
-            audio_devices_t newDevice = getNewOutputDevice(output, false /*fromCache*/);
+            audio_devices_t newDevice = getNewOutputDevice(outputDesc, false /*fromCache*/);
             // delay the device switch by twice the latency because stopOutput() is executed when
             // the track stop() command is received and at that time the audio track buffer can
             // still contain data that needs to be drained. The latency only covers the audio HAL
             // and kernel buffers. Also the latency does not always include additional delay in the
             // audio path (audio DSP, CODEC ...)
-            setOutputDevice(output, newDevice, false, outputDesc->mLatency*2);
+            setOutputDevice(outputDesc, newDevice, false, outputDesc->latency()*2);
 
             // force restoring the device selection on other active outputs if it differs from the
             // one being selected for this output
             for (size_t i = 0; i < mOutputs.size(); i++) {
                 audio_io_handle_t curOutput = mOutputs.keyAt(i);
                 sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
-                if (curOutput != output &&
+                if (desc != outputDesc &&
                         desc->isActive() &&
                         outputDesc->sharesHwModuleWith(desc) &&
                         (newDevice != desc->device())) {
-                    setOutputDevice(curOutput,
-                                    getNewOutputDevice(curOutput, false /*fromCache*/),
+                    setOutputDevice(desc,
+                                    getNewOutputDevice(desc, false /*fromCache*/),
                                     true,
-                                    outputDesc->mLatency*2);
+                                    outputDesc->latency()*2);
                 }
             }
             // update the outputs if stopping one with a stream that can affect notification routing
@@ -1129,7 +1161,7 @@
         }
         return NO_ERROR;
     } else {
-        ALOGW("stopOutput() refcount is already 0 for output %d", output);
+        ALOGW("stopOutput() refcount is already 0");
         return INVALID_OPERATION;
     }
 }
@@ -1161,7 +1193,7 @@
     // Routing
     mOutputRoutes.removeRoute(session);
 
-    sp<AudioOutputDescriptor> desc = mOutputs.valueAt(index);
+    sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(index);
     if (desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) {
         if (desc->mDirectOpenCount <= 0) {
             ALOGW("releaseOutput() invalid open count %d for output %d",
@@ -1173,8 +1205,9 @@
             // If effects where present on the output, audioflinger moved them to the primary
             // output by default: move them back to the appropriate output.
             audio_io_handle_t dstOutput = getOutputForEffect();
-            if (dstOutput != mPrimaryOutput) {
-                mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, mPrimaryOutput, dstOutput);
+            if (dstOutput != mPrimaryOutput->mIoHandle) {
+                mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX,
+                                               mPrimaryOutput->mIoHandle, dstOutput);
             }
             mpClientInterface->onAudioPortListUpdate();
         }
@@ -1528,8 +1561,8 @@
                                                   audio_devices_t device)
 {
 
-    if ((index < mStreams[stream].getVolumeIndexMin()) ||
-            (index > mStreams[stream].getVolumeIndexMax())) {
+    if ((index < mStreams.valueFor(stream).getVolumeIndexMin()) ||
+            (index > mStreams.valueFor(stream).getVolumeIndexMax())) {
         return BAD_VALUE;
     }
     if (!audio_is_output_device(device)) {
@@ -1537,7 +1570,7 @@
     }
 
     // Force max volume if stream cannot be muted
-    if (!mStreams.canBeMuted(stream)) index = mStreams[stream].getVolumeIndexMax();
+    if (!mStreams.canBeMuted(stream)) index = mStreams.valueFor(stream).getVolumeIndexMax();
 
     ALOGV("setStreamVolumeIndex() stream %d, device %04x, index %d",
           stream, device, index);
@@ -1566,16 +1599,17 @@
     }
     status_t status = NO_ERROR;
     for (size_t i = 0; i < mOutputs.size(); i++) {
-        audio_devices_t curDevice = Volume::getDeviceForVolume(mOutputs.valueAt(i)->device());
+        sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
+        audio_devices_t curDevice = Volume::getDeviceForVolume(desc->device());
         if ((device == AUDIO_DEVICE_OUT_DEFAULT) || ((curDevice & strategyDevice) != 0)) {
-            status_t volStatus = checkAndSetVolume(stream, index, mOutputs.keyAt(i), curDevice);
+            status_t volStatus = checkAndSetVolume(stream, index, desc, curDevice);
             if (volStatus != NO_ERROR) {
                 status = volStatus;
             }
         }
         if ((device == AUDIO_DEVICE_OUT_DEFAULT) || ((curDevice & accessibilityDevice) != 0)) {
             status_t volStatus = checkAndSetVolume(AUDIO_STREAM_ACCESSIBILITY,
-                                                   index, mOutputs.keyAt(i), curDevice);
+                                                   index, desc, curDevice);
         }
     }
     return status;
@@ -1598,7 +1632,7 @@
     }
     device = Volume::getDeviceForVolume(device);
 
-    *index =  mStreams[stream].getVolumeIndex(device);
+    *index =  mStreams.valueFor(stream).getVolumeIndex(device);
     ALOGV("getStreamVolumeIndex() stream %d device %08x index %d", stream, device, *index);
     return NO_ERROR;
 }
@@ -1622,7 +1656,7 @@
     audio_io_handle_t outputDeepBuffer = 0;
 
     for (size_t i = 0; i < outputs.size(); i++) {
-        sp<AudioOutputDescriptor> desc = mOutputs.valueFor(outputs[i]);
+        sp<SwAudioOutputDescriptor> desc = mOutputs.valueFor(outputs[i]);
         ALOGV("selectOutputForEffects outputs[%zu] flags %x", i, desc->mFlags);
         if ((desc->mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) {
             outputOffloaded = outputs[i];
@@ -1676,6 +1710,16 @@
     return mEffects.registerEffect(desc, io, strategy, session, id);
 }
 
+bool AudioPolicyManager::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const
+{
+    return mOutputs.isStreamActive(stream, inPastMs);
+}
+
+bool AudioPolicyManager::isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs) const
+{
+    return mOutputs.isStreamActiveRemotely(stream, inPastMs);
+}
+
 bool AudioPolicyManager::isSourceActive(audio_source_t source) const
 {
     for (size_t i = 0; i < mInputs.size(); i++) {
@@ -1826,7 +1870,7 @@
     snprintf(buffer, SIZE, "\nAudioPolicyManager Dump: %p\n", this);
     result.append(buffer);
 
-    snprintf(buffer, SIZE, " Primary Output: %d\n", mPrimaryOutput);
+    snprintf(buffer, SIZE, " Primary Output: %d\n", mPrimaryOutput->mIoHandle);
     result.append(buffer);
     snprintf(buffer, SIZE, " Phone state: %d\n", mEngine->getPhoneState());
     result.append(buffer);
@@ -2044,7 +2088,7 @@
     }
 
     if (patch->sources[0].type == AUDIO_PORT_TYPE_MIX) {
-        sp<AudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(patch->sources[0].id);
+        sp<SwAudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(patch->sources[0].id);
         if (outputDesc == NULL) {
             ALOGV("createAudioPatch() output not found for id %d", patch->sources[0].id);
             return BAD_VALUE;
@@ -2092,7 +2136,7 @@
         // TODO: reconfigure output format and channels here
         ALOGV("createAudioPatch() setting device %08x on output %d",
               devices.types(), outputDesc->mIoHandle);
-        setOutputDevice(outputDesc->mIoHandle, devices.types(), true, 0, handle);
+        setOutputDevice(outputDesc, devices.types(), true, 0, handle);
         index = mAudioPatches.indexOfKey(*handle);
         if (index >= 0) {
             if (patchDesc != 0 && patchDesc != mAudioPatches.valueAt(index)) {
@@ -2270,14 +2314,14 @@
     struct audio_patch *patch = &patchDesc->mPatch;
     patchDesc->mUid = mUidCached;
     if (patch->sources[0].type == AUDIO_PORT_TYPE_MIX) {
-        sp<AudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(patch->sources[0].id);
+        sp<SwAudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(patch->sources[0].id);
         if (outputDesc == NULL) {
             ALOGV("releaseAudioPatch() output not found for id %d", patch->sources[0].id);
             return BAD_VALUE;
         }
 
-        setOutputDevice(outputDesc->mIoHandle,
-                        getNewOutputDevice(outputDesc->mIoHandle, true /*fromCache*/),
+        setOutputDevice(outputDesc,
+                        getNewOutputDevice(outputDesc, true /*fromCache*/),
                        true,
                        0,
                        NULL);
@@ -2336,7 +2380,7 @@
     sp<AudioPortConfig> audioPortConfig;
     if (config->type == AUDIO_PORT_TYPE_MIX) {
         if (config->role == AUDIO_PORT_ROLE_SOURCE) {
-            sp<AudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(config->id);
+            sp<SwAudioOutputDescriptor> outputDesc = mOutputs.getOutputFromId(config->id);
             if (outputDesc == NULL) {
                 return BAD_VALUE;
             }
@@ -2418,7 +2462,6 @@
 #ifdef AUDIO_POLICY_TEST
     Thread(false),
 #endif //AUDIO_POLICY_TEST
-    mPrimaryOutput((audio_io_handle_t)0),
     mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f),
     mA2dpSuspended(false),
     mSpeakerDrcEnabled(false),
@@ -2502,7 +2545,8 @@
             if ((profileType & outputDeviceTypes) == 0) {
                 continue;
             }
-            sp<AudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(outProfile);
+            sp<SwAudioOutputDescriptor> outputDesc = new SwAudioOutputDescriptor(outProfile,
+                                                                                 mpClientInterface);
 
             outputDesc->mDevice = profileType;
             audio_config_t config = AUDIO_CONFIG_INITIALIZER;
@@ -2538,10 +2582,10 @@
                 }
                 if (mPrimaryOutput == 0 &&
                         outProfile->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY) {
-                    mPrimaryOutput = output;
+                    mPrimaryOutput = outputDesc;
                 }
                 addOutput(output, outputDesc);
-                setOutputDevice(output,
+                setOutputDevice(outputDesc,
                                 outputDesc->mDevice,
                                 true);
             }
@@ -2648,7 +2692,7 @@
     if (mPrimaryOutput != 0) {
         AudioParameter outputCmd = AudioParameter();
         outputCmd.addInt(String8("set_id"), 0);
-        mpClientInterface->setParameters(mPrimaryOutput, outputCmd.toString());
+        mpClientInterface->setParameters(mPrimaryOutput->mIoHandle, outputCmd.toString());
 
         mTestDevice = AUDIO_DEVICE_OUT_SPEAKER;
         mTestSamplingRate = 44100;
@@ -2788,20 +2832,21 @@
             if (param.get(String8("test_cmd_policy_reopen"), value) == NO_ERROR) {
                 param.remove(String8("test_cmd_policy_reopen"));
 
-                sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(mPrimaryOutput);
-                mpClientInterface->closeOutput(mPrimaryOutput);
+                mpClientInterface->closeOutput(mpClientInterface->closeOutput(mPrimaryOutput););
 
-                audio_module_handle_t moduleHandle = outputDesc->getModuleHandle();
+                audio_module_handle_t moduleHandle = mPrimaryOutput->getModuleHandle();
 
-                removeOutput(mPrimaryOutput);
-                sp<AudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(NULL);
+                removeOutput(mPrimaryOutput->mIoHandle);
+                sp<SwAudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(NULL,
+                                                                               mpClientInterface);
                 outputDesc->mDevice = AUDIO_DEVICE_OUT_SPEAKER;
                 audio_config_t config = AUDIO_CONFIG_INITIALIZER;
                 config.sample_rate = outputDesc->mSamplingRate;
                 config.channel_mask = outputDesc->mChannelMask;
                 config.format = outputDesc->mFormat;
+                audio_io_handle_t handle;
                 status_t status = mpClientInterface->openOutput(moduleHandle,
-                                                                &mPrimaryOutput,
+                                                                &handle,
                                                                 &config,
                                                                 &outputDesc->mDevice,
                                                                 String8(""),
@@ -2815,10 +2860,11 @@
                     outputDesc->mSamplingRate = config.sample_rate;
                     outputDesc->mChannelMask = config.channel_mask;
                     outputDesc->mFormat = config.format;
+                    mPrimaryOutput = outputDesc;
                     AudioParameter outputCmd = AudioParameter();
                     outputCmd.addInt(String8("set_id"), 0);
-                    mpClientInterface->setParameters(mPrimaryOutput, outputCmd.toString());
-                    addOutput(mPrimaryOutput, outputDesc);
+                    mpClientInterface->setParameters(handle, outputCmd.toString());
+                    addOutput(handle, outputDesc);
                 }
             }
 
@@ -2850,7 +2896,7 @@
 
 // ---
 
-void AudioPolicyManager::addOutput(audio_io_handle_t output, sp<AudioOutputDescriptor> outputDesc)
+void AudioPolicyManager::addOutput(audio_io_handle_t output, sp<SwAudioOutputDescriptor> outputDesc)
 {
     outputDesc->setIoHandle(output);
     mOutputs.add(output, outputDesc);
@@ -2869,7 +2915,7 @@
     nextAudioPortGeneration();
 }
 
-void AudioPolicyManager::findIoHandlesByAddress(sp<AudioOutputDescriptor> desc /*in*/,
+void AudioPolicyManager::findIoHandlesByAddress(sp<SwAudioOutputDescriptor> desc /*in*/,
         const audio_devices_t device /*in*/,
         const String8 address /*in*/,
         SortedVector<audio_io_handle_t>& outputs /*out*/) {
@@ -2888,7 +2934,7 @@
                                                    const String8 address)
 {
     audio_devices_t device = devDesc->type();
-    sp<AudioOutputDescriptor> desc;
+    sp<SwAudioOutputDescriptor> desc;
     // erase all current sample rates, formats and channel masks
     devDesc->clearCapabilities();
 
@@ -2896,7 +2942,7 @@
         // first list already open outputs that can be routed to this device
         for (size_t i = 0; i < mOutputs.size(); i++) {
             desc = mOutputs.valueAt(i);
-            if (!desc->isDuplicated() && (desc->mProfile->mSupportedDevices.types() & device)) {
+            if (!desc->isDuplicated() && (desc->supportedDevices() & device)) {
                 if (!device_distinguishes_on_address(device)) {
                     ALOGV("checkOutputsForDevice(): adding opened output %d", mOutputs.keyAt(i));
                     outputs.add(mOutputs.keyAt(i));
@@ -2955,7 +3001,7 @@
 
             ALOGV("opening output for device %08x with params %s profile %p",
                                                       device, address.string(), profile.get());
-            desc = new AudioOutputDescriptor(profile);
+            desc = new SwAudioOutputDescriptor(profile, mpClientInterface);
             desc->mDevice = device;
             audio_config_t config = AUDIO_CONFIG_INITIALIZER;
             config.sample_rate = desc->mSamplingRate;
@@ -3068,28 +3114,29 @@
                         audio_io_handle_t duplicatedOutput = AUDIO_IO_HANDLE_NONE;
 
                         // set initial stream volume for device
-                        applyStreamVolumes(output, device, 0, true);
+                        applyStreamVolumes(desc, device, 0, true);
 
                         //TODO: configure audio effect output stage here
 
                         // open a duplicating output thread for the new output and the primary output
-                        duplicatedOutput = mpClientInterface->openDuplicateOutput(output,
-                                                                                  mPrimaryOutput);
+                        duplicatedOutput =
+                                mpClientInterface->openDuplicateOutput(output,
+                                                                       mPrimaryOutput->mIoHandle);
                         if (duplicatedOutput != AUDIO_IO_HANDLE_NONE) {
                             // add duplicated output descriptor
-                            sp<AudioOutputDescriptor> dupOutputDesc =
-                                    new AudioOutputDescriptor(NULL);
-                            dupOutputDesc->mOutput1 = mOutputs.valueFor(mPrimaryOutput);
-                            dupOutputDesc->mOutput2 = mOutputs.valueFor(output);
+                            sp<SwAudioOutputDescriptor> dupOutputDesc =
+                                    new SwAudioOutputDescriptor(NULL, mpClientInterface);
+                            dupOutputDesc->mOutput1 = mPrimaryOutput;
+                            dupOutputDesc->mOutput2 = desc;
                             dupOutputDesc->mSamplingRate = desc->mSamplingRate;
                             dupOutputDesc->mFormat = desc->mFormat;
                             dupOutputDesc->mChannelMask = desc->mChannelMask;
                             dupOutputDesc->mLatency = desc->mLatency;
                             addOutput(duplicatedOutput, dupOutputDesc);
-                            applyStreamVolumes(duplicatedOutput, device, 0, true);
+                            applyStreamVolumes(dupOutputDesc, device, 0, true);
                         } else {
                             ALOGW("checkOutputsForDevice() could not open dup output for %d and %d",
-                                    mPrimaryOutput, output);
+                                    mPrimaryOutput->mIoHandle, output);
                             mpClientInterface->closeOutput(output);
                             removeOutput(output);
                             nextAudioPortGeneration();
@@ -3111,7 +3158,7 @@
                 if (device_distinguishes_on_address(device)) {
                     ALOGV("checkOutputsForDevice(): setOutputDevice(dev=0x%x, addr=%s)",
                             device, address.string());
-                    setOutputDevice(output, device, true/*force*/, 0/*delay*/,
+                    setOutputDevice(desc, device, true/*force*/, 0/*delay*/,
                             NULL/*patch handle*/, address.string());
                 }
                 ALOGV("checkOutputsForDevice(): adding output %d", output);
@@ -3129,10 +3176,9 @@
             if (!desc->isDuplicated()) {
                 // exact match on device
                 if (device_distinguishes_on_address(device) &&
-                        (desc->mProfile->mSupportedDevices.types() == device)) {
+                        (desc->supportedDevices() == device)) {
                     findIoHandlesByAddress(desc, device, address, outputs);
-                } else if (!(desc->mProfile->mSupportedDevices.types()
-                        & mAvailableOutputDevices.types())) {
+                } else if (!(desc->supportedDevices() & mAvailableOutputDevices.types())) {
                     ALOGV("checkOutputsForDevice(): disconnecting adding output %d",
                             mOutputs.keyAt(i));
                     outputs.add(mOutputs.keyAt(i));
@@ -3367,7 +3413,7 @@
 {
     ALOGV("closeOutput(%d)", output);
 
-    sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
+    sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
     if (outputDesc == NULL) {
         ALOGW("closeOutput() unknown output %d", output);
         return;
@@ -3376,7 +3422,7 @@
 
     // look for duplicated outputs connected to the output being removed.
     for (size_t i = 0; i < mOutputs.size(); i++) {
-        sp<AudioOutputDescriptor> dupOutputDesc = mOutputs.valueAt(i);
+        sp<SwAudioOutputDescriptor> dupOutputDesc = mOutputs.valueAt(i);
         if (dupOutputDesc->isDuplicated() &&
                 (dupOutputDesc->mOutput1 == outputDesc ||
                 dupOutputDesc->mOutput2 == outputDesc)) {
@@ -3445,8 +3491,9 @@
     mInputs.removeItem(input);
 }
 
-SortedVector<audio_io_handle_t> AudioPolicyManager::getOutputsForDevice(audio_devices_t device,
-                                                                        AudioOutputCollection openOutputs)
+SortedVector<audio_io_handle_t> AudioPolicyManager::getOutputsForDevice(
+                                                                audio_devices_t device,
+                                                                SwAudioOutputCollection openOutputs)
 {
     SortedVector<audio_io_handle_t> outputs;
 
@@ -3487,14 +3534,14 @@
     // associated with policies in the "before" and "after" output vectors
     ALOGVV("checkOutputForStrategy(): policy related outputs");
     for (size_t i = 0 ; i < mPreviousOutputs.size() ; i++) {
-        const sp<AudioOutputDescriptor> desc = mPreviousOutputs.valueAt(i);
+        const sp<SwAudioOutputDescriptor> desc = mPreviousOutputs.valueAt(i);
         if (desc != 0 && desc->mPolicyMix != NULL) {
             srcOutputs.add(desc->mIoHandle);
             ALOGVV(" previous outputs: adding %d", desc->mIoHandle);
         }
     }
     for (size_t i = 0 ; i < mOutputs.size() ; i++) {
-        const sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
+        const sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
         if (desc != 0 && desc->mPolicyMix != NULL) {
             dstOutputs.add(desc->mIoHandle);
             ALOGVV(" new outputs: adding %d", desc->mIoHandle);
@@ -3506,10 +3553,10 @@
               strategy, srcOutputs[0], dstOutputs[0]);
         // mute strategy while moving tracks from one output to another
         for (size_t i = 0; i < srcOutputs.size(); i++) {
-            sp<AudioOutputDescriptor> desc = mOutputs.valueFor(srcOutputs[i]);
+            sp<SwAudioOutputDescriptor> desc = mOutputs.valueFor(srcOutputs[i]);
             if (isStrategyActive(desc, strategy)) {
-                setStrategyMute(strategy, true, srcOutputs[i]);
-                setStrategyMute(strategy, false, srcOutputs[i], MUTE_TIME_MS, newDevice);
+                setStrategyMute(strategy, true, desc);
+                setStrategyMute(strategy, false, desc, MUTE_TIME_MS, newDevice);
             }
         }
 
@@ -3606,12 +3653,11 @@
     }
 }
 
-audio_devices_t AudioPolicyManager::getNewOutputDevice(audio_io_handle_t output, bool fromCache)
+audio_devices_t AudioPolicyManager::getNewOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
+                                                       bool fromCache)
 {
     audio_devices_t device = AUDIO_DEVICE_NONE;
 
-    sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
-
     ssize_t index = mAudioPatches.indexOfKey(outputDesc->mPatchHandle);
     if (index >= 0) {
         sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
@@ -3789,9 +3835,9 @@
         ALOGV("\t muting %d", mute);
         uint32_t maxLatency = 0;
         for (size_t i = 0; i < mOutputs.size(); i++) {
-            sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i);
+            sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
             setStreamMute(AUDIO_STREAM_TTS, mute/*on*/,
-                    desc->mIoHandle,
+                    desc,
                     0 /*delay*/, AUDIO_DEVICE_NONE);
             const uint32_t latency = desc->latency() * 2;
             if (latency > maxLatency) {
@@ -3855,7 +3901,7 @@
 
     for (size_t i = 0; i < NUM_STRATEGIES; i++) {
         audio_devices_t curDevice = getDeviceForStrategy((routing_strategy)i, false /*fromCache*/);
-        curDevice = curDevice & outputDesc->mProfile->mSupportedDevices.types();
+        curDevice = curDevice & outputDesc->supportedDevices();
         bool mute = shouldMute && (curDevice & device) && (curDevice != device);
         bool doMute = false;
 
@@ -3874,10 +3920,9 @@
                         == AUDIO_DEVICE_NONE) {
                     continue;
                 }
-                audio_io_handle_t curOutput = mOutputs.keyAt(j);
-                ALOGVV("checkDeviceMuteStrategies() %s strategy %d (curDevice %04x) on output %d",
-                      mute ? "muting" : "unmuting", i, curDevice, curOutput);
-                setStrategyMute((routing_strategy)i, mute, curOutput, mute ? 0 : delayMs);
+                ALOGVV("checkDeviceMuteStrategies() %s strategy %d (curDevice %04x)",
+                      mute ? "muting" : "unmuting", i, curDevice);
+                setStrategyMute((routing_strategy)i, mute, desc, mute ? 0 : delayMs);
                 if (isStrategyActive(desc, (routing_strategy)i)) {
                     if (mute) {
                         // FIXME: should not need to double latency if volume could be applied
@@ -3902,9 +3947,9 @@
         }
         for (size_t i = 0; i < NUM_STRATEGIES; i++) {
             if (isStrategyActive(outputDesc, (routing_strategy)i)) {
-                setStrategyMute((routing_strategy)i, true, outputDesc->mIoHandle);
+                setStrategyMute((routing_strategy)i, true, outputDesc);
                 // do tempMute unmute after twice the mute wait time
-                setStrategyMute((routing_strategy)i, false, outputDesc->mIoHandle,
+                setStrategyMute((routing_strategy)i, false, outputDesc,
                                 muteWaitMs *2, device);
             }
         }
@@ -3919,32 +3964,31 @@
     return 0;
 }
 
-uint32_t AudioPolicyManager::setOutputDevice(audio_io_handle_t output,
+uint32_t AudioPolicyManager::setOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
                                              audio_devices_t device,
                                              bool force,
                                              int delayMs,
                                              audio_patch_handle_t *patchHandle,
                                              const char* address)
 {
-    ALOGV("setOutputDevice() output %d device %04x delayMs %d", output, device, delayMs);
-    sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
+    ALOGV("setOutputDevice() device %04x delayMs %d", device, delayMs);
     AudioParameter param;
     uint32_t muteWaitMs;
 
     if (outputDesc->isDuplicated()) {
-        muteWaitMs = setOutputDevice(outputDesc->mOutput1->mIoHandle, device, force, delayMs);
-        muteWaitMs += setOutputDevice(outputDesc->mOutput2->mIoHandle, device, force, delayMs);
+        muteWaitMs = setOutputDevice(outputDesc->subOutput1(), device, force, delayMs);
+        muteWaitMs += setOutputDevice(outputDesc->subOutput2(), device, force, delayMs);
         return muteWaitMs;
     }
     // no need to proceed if new device is not AUDIO_DEVICE_NONE and not supported by current
     // output profile
-    if (device != AUDIO_DEVICE_NONE &&
-        (device & outputDesc->mProfile->mSupportedDevices.types()) == 0) {
+    if ((device != AUDIO_DEVICE_NONE) &&
+            ((device & outputDesc->supportedDevices()) == 0)) {
         return 0;
     }
 
     // filter devices according to output selected
-    device = (audio_devices_t)(device & outputDesc->mProfile->mSupportedDevices.types());
+    device = (audio_devices_t)(device & outputDesc->supportedDevices());
 
     audio_devices_t prevDevice = outputDesc->mDevice;
 
@@ -3964,8 +4008,7 @@
     if ((device == AUDIO_DEVICE_NONE || device == prevDevice) &&
         !force &&
         outputDesc->mPatchHandle != 0) {
-        ALOGV("setOutputDevice() setting same device 0x%04x or null device for output %d",
-              device, output);
+        ALOGV("setOutputDevice() setting same device 0x%04x or null device", device);
         return muteWaitMs;
     }
 
@@ -3973,7 +4016,7 @@
 
     // do the routing
     if (device == AUDIO_DEVICE_NONE) {
-        resetOutputDevice(output, delayMs, NULL);
+        resetOutputDevice(outputDesc, delayMs, NULL);
     } else {
         DeviceVector deviceList = (address == NULL) ?
                 mAvailableOutputDevices.getDevicesFromType(device)
@@ -4040,16 +4083,15 @@
     }
 
     // update stream volumes according to new device
-    applyStreamVolumes(output, device, delayMs);
+    applyStreamVolumes(outputDesc, device, delayMs);
 
     return muteWaitMs;
 }
 
-status_t AudioPolicyManager::resetOutputDevice(audio_io_handle_t output,
+status_t AudioPolicyManager::resetOutputDevice(const sp<AudioOutputDescriptor>& outputDesc,
                                                int delayMs,
                                                audio_patch_handle_t *patchHandle)
 {
-    sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
     ssize_t index;
     if (patchHandle) {
         index = mAudioPatches.indexOfKey(*patchHandle);
@@ -4206,16 +4248,11 @@
 }
 
 float AudioPolicyManager::computeVolume(audio_stream_type_t stream,
-                                        int index,
-                                        audio_io_handle_t output,
-                                        audio_devices_t device)
+                                            int index,
+                                            audio_devices_t device)
 {
     float volume = 1.0;
-    sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
 
-    if (device == AUDIO_DEVICE_NONE) {
-        device = outputDesc->device();
-    }
     volume = mEngine->volIndexToAmpl(Volume::getDeviceCategory(device), stream, index);
 
     // if a headset is connected, apply the following rules to ring tones and notifications
@@ -4242,8 +4279,7 @@
                 mLimitRingtoneVolume) {
             audio_devices_t musicDevice = getDeviceForStrategy(STRATEGY_MEDIA, true /*fromCache*/);
             float musicVol = computeVolume(AUDIO_STREAM_MUSIC,
-                               mStreams[AUDIO_STREAM_MUSIC].getVolumeIndex(musicDevice),
-                               output,
+                               mStreams.valueFor(AUDIO_STREAM_MUSIC).getVolumeIndex(musicDevice),
                                musicDevice);
             float minVol = (musicVol > SONIFICATION_HEADSET_VOLUME_MIN) ?
                                 musicVol : SONIFICATION_HEADSET_VOLUME_MIN;
@@ -4258,17 +4294,16 @@
 }
 
 status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream,
-                                               int index,
-                                               audio_io_handle_t output,
-                                               audio_devices_t device,
-                                               int delayMs,
-                                               bool force)
+                                                   int index,
+                                                   const sp<AudioOutputDescriptor>& outputDesc,
+                                                   audio_devices_t device,
+                                                   int delayMs,
+                                                   bool force)
 {
-
     // do not change actual stream volume if the stream is muted
-    if (mOutputs.valueFor(output)->mMuteCount[stream] != 0) {
+    if (outputDesc->mMuteCount[stream] != 0) {
         ALOGVV("checkAndSetVolume() stream %d muted count %d",
-              stream, mOutputs.valueFor(output)->mMuteCount[stream]);
+              stream, outputDesc->mMuteCount[stream]);
         return NO_ERROR;
     }
     audio_policy_forced_cfg_t forceUseForComm =
@@ -4281,45 +4316,28 @@
         return INVALID_OPERATION;
     }
 
-    float volume = computeVolume(stream, index, output, device);
-    // unit gain if rerouting to external policy
-    if (device == AUDIO_DEVICE_OUT_REMOTE_SUBMIX) {
-        ssize_t index = mOutputs.indexOfKey(output);
-        if (index >= 0) {
-            sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(index);
-            if (outputDesc->mPolicyMix != NULL) {
-                ALOGV("max gain when rerouting for output=%d", output);
-                volume = 1.0f;
-            }
-        }
+    if (device == AUDIO_DEVICE_NONE) {
+        device = outputDesc->device();
+    }
 
+    float volume = computeVolume(stream, index, device);
+    if (outputDesc->isFixedVolume(device)) {
+        volume = 1.0f;
     }
-    // We actually change the volume if:
-    // - the float value returned by computeVolume() changed
-    // - the force flag is set
-    if (volume != mOutputs.valueFor(output)->mCurVolume[stream] ||
-            force) {
-        mOutputs.valueFor(output)->mCurVolume[stream] = volume;
-        ALOGVV("checkAndSetVolume() for output %d stream %d, volume %f, delay %d", output, stream, volume, delayMs);
-        // Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is
-        // enabled
-        if (stream == AUDIO_STREAM_BLUETOOTH_SCO) {
-            mpClientInterface->setStreamVolume(AUDIO_STREAM_VOICE_CALL, volume, output, delayMs);
-        }
-        mpClientInterface->setStreamVolume(stream, volume, output, delayMs);
-    }
+
+    outputDesc->setVolume(volume, stream, device, delayMs, force);
 
     if (stream == AUDIO_STREAM_VOICE_CALL ||
         stream == AUDIO_STREAM_BLUETOOTH_SCO) {
         float voiceVolume;
         // Force voice volume to max for bluetooth SCO as volume is managed by the headset
         if (stream == AUDIO_STREAM_VOICE_CALL) {
-            voiceVolume = (float)index/(float)mStreams[stream].getVolumeIndexMax();
+            voiceVolume = (float)index/(float)mStreams.valueFor(stream).getVolumeIndexMax();
         } else {
             voiceVolume = 1.0;
         }
 
-        if (voiceVolume != mLastVoiceVolume && output == mPrimaryOutput) {
+        if (voiceVolume != mLastVoiceVolume && outputDesc == mPrimaryOutput) {
             mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
             mLastVoiceVolume = voiceVolume;
         }
@@ -4328,20 +4346,20 @@
     return NO_ERROR;
 }
 
-void AudioPolicyManager::applyStreamVolumes(audio_io_handle_t output,
-                                            audio_devices_t device,
-                                            int delayMs,
-                                            bool force)
+void AudioPolicyManager::applyStreamVolumes(const sp<AudioOutputDescriptor>& outputDesc,
+                                                audio_devices_t device,
+                                                int delayMs,
+                                                bool force)
 {
-    ALOGVV("applyStreamVolumes() for output %d and device %x", output, device);
+    ALOGVV("applyStreamVolumes() for device %08x", device);
 
     for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) {
         if (stream == AUDIO_STREAM_PATCH) {
             continue;
         }
         checkAndSetVolume((audio_stream_type_t)stream,
-                          mStreams[stream].getVolumeIndex(device),
-                          output,
+                          mStreams.valueFor((audio_stream_type_t)stream).getVolumeIndex(device),
+                          outputDesc,
                           device,
                           delayMs,
                           force);
@@ -4349,10 +4367,10 @@
 }
 
 void AudioPolicyManager::setStrategyMute(routing_strategy strategy,
-                                         bool on,
-                                         audio_io_handle_t output,
-                                         int delayMs,
-                                         audio_devices_t device)
+                                             bool on,
+                                             const sp<AudioOutputDescriptor>& outputDesc,
+                                             int delayMs,
+                                             audio_devices_t device)
 {
     ALOGVV("setStrategyMute() strategy %d, mute %d, output %d", strategy, on, output);
     for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) {
@@ -4360,32 +4378,31 @@
             continue;
         }
         if (getStrategy((audio_stream_type_t)stream) == strategy) {
-            setStreamMute((audio_stream_type_t)stream, on, output, delayMs, device);
+            setStreamMute((audio_stream_type_t)stream, on, outputDesc, delayMs, device);
         }
     }
 }
 
 void AudioPolicyManager::setStreamMute(audio_stream_type_t stream,
-                                       bool on,
-                                       audio_io_handle_t output,
-                                       int delayMs,
-                                       audio_devices_t device)
+                                           bool on,
+                                           const sp<AudioOutputDescriptor>& outputDesc,
+                                           int delayMs,
+                                           audio_devices_t device)
 {
-    const StreamDescriptor &streamDesc = mStreams[stream];
-    sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output);
+    const StreamDescriptor& streamDesc = mStreams.valueFor(stream);
     if (device == AUDIO_DEVICE_NONE) {
         device = outputDesc->device();
     }
 
-    ALOGVV("setStreamMute() stream %d, mute %d, output %d, mMuteCount %d device %04x",
-          stream, on, output, outputDesc->mMuteCount[stream], device);
+    ALOGVV("setStreamMute() stream %d, mute %d, mMuteCount %d device %04x",
+          stream, on, outputDesc->mMuteCount[stream], device);
 
     if (on) {
         if (outputDesc->mMuteCount[stream] == 0) {
             if (streamDesc.canBeMuted() &&
                     ((stream != AUDIO_STREAM_ENFORCED_AUDIBLE) ||
                      (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) {
-                checkAndSetVolume(stream, 0, output, device, delayMs);
+                checkAndSetVolume(stream, 0, outputDesc, device, delayMs);
             }
         }
         // increment mMuteCount after calling checkAndSetVolume() so that volume change is not ignored
@@ -4398,7 +4415,7 @@
         if (--outputDesc->mMuteCount[stream] == 0) {
             checkAndSetVolume(stream,
                               streamDesc.getVolumeIndex(device),
-                              output,
+                              outputDesc,
                               device,
                               delayMs);
         }
@@ -4417,7 +4434,7 @@
     const routing_strategy stream_strategy = getStrategy(stream);
     if ((stream_strategy == STRATEGY_SONIFICATION) ||
             ((stream_strategy == STRATEGY_SONIFICATION_RESPECTFUL))) {
-        sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(mPrimaryOutput);
+        sp<SwAudioOutputDescriptor> outputDesc = mPrimaryOutput;
         ALOGV("handleIncallSonification() stream %d starting %d device %x stateChange %d",
                 stream, starting, outputDesc->mDevice, stateChange);
         if (outputDesc->mRefCount[stream]) {