audio: Add IDevice.setConnectedState_7_1 method

This is an updated version of IDevice.setConnectedState which
accepts a full AudioPort so that additional data like extra
audio descriptors can be passed to the audio HAL.

Bug: 211601178
Test: atest VtsHalAudioV7_1TargetTest
Change-Id: Id746caa32122dabfb83feb5b515bed7717bcb67c
diff --git a/audio/7.1/IDevice.hal b/audio/7.1/IDevice.hal
index 373d17f..e0b1e92 100644
--- a/audio/7.1/IDevice.hal
+++ b/audio/7.1/IDevice.hal
@@ -85,4 +85,16 @@
                     Result retval,
                     IStreamIn inStream,
                     AudioConfig suggestedConfig);
+
+    /**
+     * Notifies the device module about the connection state of an input/output
+     * device attached to it. The devicePort identifies the device and may also
+     * provide extra information such as raw audio descriptors.
+     *
+     * @param devicePort audio device port.
+     * @param connected whether the device is connected.
+     * @return retval operation completion status.
+     */
+    setConnectedState_7_1(AudioPort devicePort, bool connected)
+            generates (Result retval);
 };
diff --git a/audio/core/all-versions/default/Device.cpp b/audio/core/all-versions/default/Device.cpp
index ac5a3ba..ca8c03d 100644
--- a/audio/core/all-versions/default/Device.cpp
+++ b/audio/core/all-versions/default/Device.cpp
@@ -616,6 +616,21 @@
 
 #endif
 
+#if MAJOR_VERSION == 7 && MINOR_VERSION == 1
+Return<Result> Device::setConnectedState_7_1(const AudioPort& devicePort, bool connected) {
+    if (version() >= AUDIO_DEVICE_API_VERSION_3_2 &&
+        mDevice->set_device_connected_state_v7 != nullptr) {
+        audio_port_v7 halPort;
+        if (status_t status = HidlUtils::audioPortToHal(devicePort, &halPort); status != NO_ERROR) {
+            return analyzeStatus("audioPortToHal", status);
+        }
+        return analyzeStatus("set_device_connected_state_v7",
+                             mDevice->set_device_connected_state_v7(mDevice, &halPort, connected));
+    }
+    return Result::NOT_SUPPORTED;
+}
+#endif
+
 }  // namespace implementation
 }  // namespace CPP_VERSION
 }  // namespace audio
diff --git a/audio/core/all-versions/default/include/core/default/Device.h b/audio/core/all-versions/default/include/core/default/Device.h
index 0aeb6b3..8cde3e0 100644
--- a/audio/core/all-versions/default/include/core/default/Device.h
+++ b/audio/core/all-versions/default/include/core/default/Device.h
@@ -163,6 +163,9 @@
                                   const hidl_vec<AudioPortConfig>& sinks,
                                   createAudioPatch_cb _hidl_cb) override;
 #endif
+#if MAJOR_VERSION == 7 && MINOR_VERSION == 1
+    Return<Result> setConnectedState_7_1(const AudioPort& devicePort, bool connected) override;
+#endif
     Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& options) override;
 
     // Utility methods for extending interfaces.
diff --git a/audio/core/all-versions/vts/functional/7.1/AudioPrimaryHidlHalTest.cpp b/audio/core/all-versions/vts/functional/7.1/AudioPrimaryHidlHalTest.cpp
index b750f56..d82d4ad 100644
--- a/audio/core/all-versions/vts/functional/7.1/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/7.1/AudioPrimaryHidlHalTest.cpp
@@ -16,3 +16,34 @@
 
 // pull in all the <= 7.0 tests
 #include "7.0/AudioPrimaryHidlHalTest.cpp"
+
+TEST_P(AudioHidlDeviceTest, SetConnectedState_7_1) {
+    doc::test("Check that the HAL can be notified of device connection and disconnection");
+    using AD = xsd::AudioDevice;
+    for (auto deviceType : {AD::AUDIO_DEVICE_OUT_HDMI, AD::AUDIO_DEVICE_OUT_WIRED_HEADPHONE,
+                            AD::AUDIO_DEVICE_IN_USB_HEADSET}) {
+        SCOPED_TRACE("device=" + toString(deviceType));
+        for (bool state : {true, false}) {
+            SCOPED_TRACE("state=" + ::testing::PrintToString(state));
+            DeviceAddress address = {};
+            address.deviceType = toString(deviceType);
+            if (deviceType == AD::AUDIO_DEVICE_IN_USB_HEADSET) {
+                address.address.alsa({0, 0});
+            }
+            AudioPort devicePort;
+            devicePort.ext.device(address);
+            auto ret = getDevice()->setConnectedState_7_1(devicePort, state);
+            ASSERT_TRUE(ret.isOk());
+            if (ret == Result::NOT_SUPPORTED) {
+                doc::partialTest("setConnectedState_7_1 is not supported");
+                break;  // other deviceType might be supported
+            }
+            ASSERT_OK(ret);
+        }
+    }
+
+    // Because there is no way of knowing if the devices were connected before
+    // calling setConnectedState, there is no way to restore the HAL to its
+    // initial state. To workaround this, destroy the HAL at the end of this test.
+    ASSERT_TRUE(resetDevice());
+}