audiopolicy: fix dynamic device & explicit routing management

While a playback is ongoing using explicit routing
and the device is disconnected, setOutputDevices() will bail out
if the new device is incompatible with the io profile. So the device
will not be reset in the output.
Moreover, if another device of the same type/address is connected,
this device will have a new unique ID.
If a track wants to use it as explicit, as the first playback/capture is still
on going, the function findPreferredDevice will return the previous device id.

This CL enforces:
-setOutputDevices() to check if new device is not compatible AND previous devices
are still available
- When a device is disconnected, go over all clients to clear any preferred device
referring to the device being removed.
- Sanitize preferred device received by get<Output|Input>ForAttr() according to
connected devices.

Test: AudioPolicyTests --gtest_filter=DynamicAddressOutputDevice/DeviceConnectionTest.DeviceConnectionState/*
Test: audio smoke tests
Test: CTS tests for AudioTrack, AudioRecord, AudioRouting

Change-Id: Ib3a2d54d902def0fcfc2a623c25bb92d65b1ab98
Signed-off-by: Francois Gaffie <francois.gaffie@renault.com>
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
index d4cfd1e..803cfac 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioInputDescriptor.h
@@ -143,6 +143,16 @@
 
     void trackEffectEnabled(const sp<EffectDescriptor> &effect, bool enabled);
 
+    /**
+    * @brief clearSessionRoutesForDevice: when a device is disconnected, and if this device has
+    * been chosen as the preferred device by any client, the policy manager shall
+    * prevent from using this device any more by clearing all the session routes involving this
+    * device.
+    * In other words, the preferred device port id of these clients will be resetted to NONE.
+    * @param disconnectedDevice device to be disconnected
+    */
+    void clearSessionRoutesForDevice(const sp<DeviceDescriptor> &disconnectedDevice);
+
     void dump(String8 *dst) const;
 };
 
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
index 6132bb4..c84636e 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioOutputDescriptor.h
@@ -380,6 +380,16 @@
                                       uint32_t inPastMs = 0, nsecs_t sysTime = 0) const;
 
     /**
+     * @brief clearSessionRoutesForDevice: when a device is disconnected, and if this device has
+     * been chosen as the preferred device by any client, the policy manager shall
+     * prevent from using this device any more by clearing all the session routes involving this
+     * device.
+     * In other words, the preferred device port id of these clients will be resetted to NONE.
+     * @param disconnectedDevice device to be disconnected
+     */
+    void clearSessionRoutesForDevice(const sp<DeviceDescriptor> &disconnectedDevice);
+
+    /**
      * returns the A2DP output handle if it is open or 0 otherwise
      */
     audio_io_handle_t getA2dpOutput() const;
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
index c880e67..1fa1123 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioInputDescriptor.cpp
@@ -511,6 +511,19 @@
     }
 }
 
+void AudioInputCollection::clearSessionRoutesForDevice(
+    const sp<DeviceDescriptor> &disconnectedDevice)
+{
+    for (size_t i = 0; i < size(); i++) {
+        sp<AudioInputDescriptor> inputDesc = valueAt(i);
+        for (const auto& client : inputDesc->getClientIterable()) {
+            if (client->preferredDeviceId() == disconnectedDevice->getId()) {
+                client->setPreferredDeviceId(AUDIO_PORT_HANDLE_NONE);
+            }
+        }
+    }
+}
+
 void AudioInputCollection::dump(String8 *dst) const
 {
     dst->append("\nInputs dump:\n");
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index 1dfd88a..77e7add 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -802,6 +802,19 @@
     return 0;
 }
 
+void SwAudioOutputCollection::clearSessionRoutesForDevice(
+        const sp<DeviceDescriptor> &disconnectedDevice)
+{
+    for (size_t i = 0; i < size(); i++) {
+        sp<AudioOutputDescriptor> outputDesc = valueAt(i);
+        for (const auto& client : outputDesc->getClientIterable()) {
+            if (client->preferredDeviceId() == disconnectedDevice->getId()) {
+                client->setPreferredDeviceId(AUDIO_PORT_HANDLE_NONE);
+            }
+        }
+    }
+}
+
 void SwAudioOutputCollection::dump(String8 *dst) const
 {
     dst->append("\nOutputs dump:\n");
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index ccec93f..2854a98 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -193,6 +193,8 @@
             // remove device from available output devices
             mAvailableOutputDevices.remove(device);
 
+            mOutputs.clearSessionRoutesForDevice(device);
+
             checkOutputsForDevice(device, state, outputs);
 
             // Reset active device codec
@@ -929,8 +931,6 @@
     const sp<DeviceDescriptor> requestedDevice =
         mAvailableOutputDevices.getDeviceFromId(requestedPortId);
 
-
-
     status_t status = getAudioAttributes(resultAttr, attr, *stream);
     if (status != NO_ERROR) {
         return status;
@@ -1045,6 +1045,14 @@
     audio_attributes_t resultAttr;
     bool isRequestedDeviceForExclusiveUse = false;
     std::vector<sp<SwAudioOutputDescriptor>> secondaryOutputDescs;
+    const sp<DeviceDescriptor> requestedDevice =
+      mAvailableOutputDevices.getDeviceFromId(requestedPortId);
+
+    // Prevent from storing invalid requested device id in clients
+    const audio_port_handle_t sanitizedRequestedPortId =
+      requestedDevice != nullptr ? requestedPortId : AUDIO_PORT_HANDLE_NONE;
+    *selectedDeviceId = sanitizedRequestedPortId;
+
     status_t status = getOutputForAttrInt(&resultAttr, output, session, attr, stream, uid,
             config, flags, selectedDeviceId, &isRequestedDeviceForExclusiveUse,
             &secondaryOutputDescs);
@@ -1064,15 +1072,15 @@
 
     sp<TrackClientDescriptor> clientDesc =
         new TrackClientDescriptor(*portId, uid, session, resultAttr, clientConfig,
-                                  requestedPortId, *stream,
+                                  sanitizedRequestedPortId, *stream,
                                   mEngine->getProductStrategyForAttributes(resultAttr),
                                   *flags, isRequestedDeviceForExclusiveUse,
                                   std::move(weakSecondaryOutputDescs));
     sp<SwAudioOutputDescriptor> outputDesc = mOutputs.valueFor(*output);
     outputDesc->addClient(clientDesc);
 
-    ALOGV("%s() returns output %d selectedDeviceId %d for port ID %d", __func__,
-          *output, *selectedDeviceId, *portId);
+    ALOGV("%s() returns output %d requestedPortId %d selectedDeviceId %d for port ID %d", __func__,
+          *output, requestedPortId, *selectedDeviceId, *portId);
 
     return NO_ERROR;
 }
@@ -1953,6 +1961,8 @@
         if (explicitRoutingDevice != nullptr) {
             device = explicitRoutingDevice;
         } else {
+            // Prevent from storing invalid requested device id in clients
+            requestedDeviceId = AUDIO_PORT_HANDLE_NONE;
             device = mEngine->getInputDeviceForAttributes(attributes, &policyMix);
         }
         if (device == nullptr) {
@@ -5295,16 +5305,17 @@
 
     // filter devices according to output selected
     DeviceVector filteredDevices = outputDesc->filterSupportedDevices(devices);
+    DeviceVector prevDevices = outputDesc->devices();
 
     // no need to proceed if new device is not AUDIO_DEVICE_NONE and not supported by current
-    // output profile
-    if (!devices.isEmpty() && filteredDevices.isEmpty()) {
+    // output profile or if new device is not supported AND previous device(s) is(are) still
+    // available (otherwise reset device must be done on the output)
+    if (!devices.isEmpty() && filteredDevices.isEmpty() &&
+            !mAvailableOutputDevices.filter(prevDevices).empty()) {
         ALOGV("%s: unsupported device %s for output", __func__, devices.toString().c_str());
         return 0;
     }
 
-    DeviceVector prevDevices = outputDesc->devices();
-
     ALOGV("setOutputDevices() prevDevice %s", prevDevices.toString().c_str());
 
     if (!filteredDevices.isEmpty()) {
@@ -5819,6 +5830,8 @@
         }
     }
 
+    mInputs.clearSessionRoutesForDevice(deviceDesc);
+
     mHwModules.cleanUpForDevice(deviceDesc);
 }