audio policy: improve input device connection logic

Do not systematically close all inputs when an input device is connected
or disconnected but only those affected by the change.

Also:
- refactor setDeviceConnectionStateInt() to align input and output
devices connection sequence
- remove unused "inputs" argument to checkInputsForDevice()

Bug: 126370328
Test: phone call, video call , camcorder, assistant, auto projected
Change-Id: I27734b32da97ad4790d6cd74eaaf866e414db211
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 6bd64d6..7011ef7 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -273,8 +273,6 @@
 
     // handle input devices
     if (audio_is_input_device(deviceType)) {
-        SortedVector <audio_io_handle_t> inputs;
-
         ssize_t index = mAvailableInputDevices.indexOf(device);
         switch (state)
         {
@@ -284,11 +282,18 @@
                 ALOGW("%s() device already connected: %s", __func__, device->toString().c_str());
                 return INVALID_OPERATION;
             }
+
+            if (mAvailableInputDevices.add(device) < 0) {
+                return NO_MEMORY;
+            }
+
             // Before checking intputs, broadcast connect event to allow HAL to retrieve dynamic
             // parameters on newly connected devices (instead of opening the inputs...)
             broadcastDeviceConnectionState(device, state);
 
-            if (checkInputsForDevice(device, state, inputs) != NO_ERROR) {
+            if (checkInputsForDevice(device, state) != NO_ERROR) {
+                mAvailableInputDevices.remove(device);
+
                 broadcastDeviceConnectionState(device, AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE);
 
                 mHwModules.cleanUpForDevice(device);
@@ -296,9 +301,6 @@
                 return INVALID_OPERATION;
             }
 
-            if (mAvailableInputDevices.add(device) < 0) {
-                return NO_MEMORY;
-            }
         } break;
 
         // handle input device disconnection
@@ -313,8 +315,9 @@
             // Set Disconnect to HALs
             broadcastDeviceConnectionState(device, state);
 
-            checkInputsForDevice(device, state, inputs);
             mAvailableInputDevices.remove(device);
+
+            checkInputsForDevice(device, state);
         } break;
 
         default:
@@ -325,7 +328,7 @@
         // Propagate device availability to Engine
         setEngineDeviceConnectionState(device, state);
 
-        closeAllInputs();
+        checkCloseInputs();
         // As the input device list can impact the output device selection, update
         // getDeviceForStrategy() cache
         updateDevicesAndOutputs();
@@ -2342,9 +2345,39 @@
     releaseInput(portId);
 }
 
-void AudioPolicyManager::closeAllInputs() {
-    while (mInputs.size() != 0) {
-        closeInput(mInputs.keyAt(0));
+void AudioPolicyManager::checkCloseInputs() {
+    // After connecting or disconnecting an input device, close input if:
+    // - it has no client (was just opened to check profile)  OR
+    // - none of its supported devices are connected anymore OR
+    // - one of its clients cannot be routed to one of its supported
+    // devices anymore. Otherwise update device selection
+    std::vector<audio_io_handle_t> inputsToClose;
+    for (size_t i = 0; i < mInputs.size(); i++) {
+        const sp<AudioInputDescriptor> input = mInputs.valueAt(i);
+        if (input->clientsList().size() == 0
+                || !mAvailableInputDevices.containsAtLeastOne(input->supportedDevices())) {
+            inputsToClose.push_back(mInputs.keyAt(i));
+        } else {
+            bool close = false;
+            for (const auto& client : input->clientsList()) {
+                sp<DeviceDescriptor> device =
+                    mEngine->getInputDeviceForAttributes(client->attributes());
+                if (!input->supportedDevices().contains(device)) {
+                    close = true;
+                    break;
+                }
+            }
+            if (close) {
+                inputsToClose.push_back(mInputs.keyAt(i));
+            } else {
+                setInputDevice(input->mIoHandle, getNewInputDevice(input));
+            }
+        }
+    }
+
+    for (const audio_io_handle_t handle : inputsToClose) {
+        ALOGV("%s closing input %d", __func__, handle);
+        closeInput(handle);
     }
 }
 
@@ -4684,8 +4717,7 @@
 }
 
 status_t AudioPolicyManager::checkInputsForDevice(const sp<DeviceDescriptor>& device,
-                                                  audio_policy_dev_state_t state,
-                                                  SortedVector<audio_io_handle_t>& inputs)
+                                                  audio_policy_dev_state_t state)
 {
     sp<AudioInputDescriptor> desc;
 
@@ -4695,16 +4727,7 @@
     }
 
     if (state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) {
-        // first list already open inputs that can be routed to this device
-        for (size_t input_index = 0; input_index < mInputs.size(); input_index++) {
-            desc = mInputs.valueAt(input_index);
-            if (desc->mProfile->supportsDeviceTypes(device->type())) {
-                ALOGV("checkInputsForDevice(): adding opened input %d", mInputs.keyAt(input_index));
-               inputs.add(mInputs.keyAt(input_index));
-            }
-        }
-
-        // then look for input profiles that can be routed to this device
+        // look for input profiles that can be routed to this device
         SortedVector< sp<IOProfile> > profiles;
         for (const auto& hwModule : mHwModules) {
             for (size_t profile_index = 0;
@@ -4720,8 +4743,9 @@
             }
         }
 
-        if (profiles.isEmpty() && inputs.isEmpty()) {
-            ALOGW("%s: No input available for device %s", __func__, device->toString().c_str());
+        if (profiles.isEmpty()) {
+            ALOGW("%s: No input profile available for device %s",
+                __func__, device->toString().c_str());
             return BAD_VALUE;
         }
 
@@ -4774,7 +4798,7 @@
                     input = AUDIO_IO_HANDLE_NONE;
                 }
 
-                if (input != 0) {
+                if (input != AUDIO_IO_HANDLE_NONE) {
                     addInput(input, desc);
                 }
             } // endif input != 0
@@ -4785,7 +4809,6 @@
                 profiles.removeAt(profile_index);
                 profile_index--;
             } else {
-                inputs.add(input);
                 if (audio_device_is_digital(device->type())) {
                     device->importAudioPort(profile);
                 }
@@ -4799,15 +4822,6 @@
         }
     } else {
         // Disconnect
-        // check if one opened input is not needed any more after disconnecting one device
-        for (size_t input_index = 0; input_index < mInputs.size(); input_index++) {
-            desc = mInputs.valueAt(input_index);
-            if (!mAvailableInputDevices.containsAtLeastOne(desc->supportedDevices())) {
-                ALOGV("checkInputsForDevice(): disconnecting adding input %d",
-                      mInputs.keyAt(input_index));
-                inputs.add(mInputs.keyAt(input_index));
-            }
-        }
         // Clear any profiles associated with the disconnected device.
         for (const auto& hwModule : mHwModules) {
             for (size_t profile_index = 0;