audio: Add check to IDevice.close for currently opened streams
IDevice.close must not proceed if there are streams
that are currently opened on this device.
Bug: 114451103
Test: atest VtsHalAudioV6_0TargetTest
Change-Id: I61d81bc0333098c341d5d551bf59331e49fcf682
diff --git a/audio/6.0/IDevice.hal b/audio/6.0/IDevice.hal
index 520e776..122c550 100644
--- a/audio/6.0/IDevice.hal
+++ b/audio/6.0/IDevice.hal
@@ -286,8 +286,12 @@
* all currently allocated resources. It is recommended to close
* the device on the client side as soon as it is becomes unused.
*
+ * Note that all streams must be closed by the client before
+ * attempting to close the device they belong to.
+ *
* @return retval OK in case the success.
- * INVALID_STATE if the device was already closed.
+ * INVALID_STATE if the device was already closed
+ * or there are streams currently opened.
*/
@exit
close() generates (Result retval);
diff --git a/audio/core/all-versions/default/Device.cpp b/audio/core/all-versions/default/Device.cpp
index 5ea4c8d..21dab00 100644
--- a/audio/core/all-versions/default/Device.cpp
+++ b/audio/core/all-versions/default/Device.cpp
@@ -53,10 +53,14 @@
void Device::closeInputStream(audio_stream_in_t* stream) {
mDevice->close_input_stream(mDevice, stream);
+ LOG_ALWAYS_FATAL_IF(mOpenedStreamsCount == 0, "mOpenedStreamsCount is already 0");
+ --mOpenedStreamsCount;
}
void Device::closeOutputStream(audio_stream_out_t* stream) {
mDevice->close_output_stream(mDevice, stream);
+ LOG_ALWAYS_FATAL_IF(mOpenedStreamsCount == 0, "mOpenedStreamsCount is already 0");
+ --mOpenedStreamsCount;
}
char* Device::halGetParameters(const char* keys) {
@@ -158,6 +162,7 @@
sp<IStreamOut> streamOut;
if (status == OK) {
streamOut = new StreamOut(this, halStream);
+ ++mOpenedStreamsCount;
}
HidlUtils::audioConfigFromHal(halConfig, suggestedConfig);
return {analyzeStatus("open_output_stream", status, {EINVAL} /*ignore*/), streamOut};
@@ -184,6 +189,7 @@
sp<IStreamIn> streamIn;
if (status == OK) {
streamIn = new StreamIn(this, halStream);
+ ++mOpenedStreamsCount;
}
HidlUtils::audioConfigFromHal(halConfig, suggestedConfig);
return {analyzeStatus("open_input_stream", status, {EINVAL} /*ignore*/), streamIn};
@@ -383,7 +389,7 @@
#endif
Result Device::doClose() {
- if (mIsClosed) return Result::INVALID_STATE;
+ if (mIsClosed || mOpenedStreamsCount != 0) return Result::INVALID_STATE;
mIsClosed = true;
return analyzeStatus("close", audio_hw_device_close(mDevice));
}
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 dc53a39..11ab607 100644
--- a/audio/core/all-versions/default/include/core/default/Device.h
+++ b/audio/core/all-versions/default/include/core/default/Device.h
@@ -130,6 +130,7 @@
private:
bool mIsClosed;
audio_hw_device_t* mDevice;
+ int mOpenedStreamsCount = 0;
virtual ~Device();
diff --git a/audio/core/all-versions/vts/functional/6.0/AudioPrimaryHidlHalTest.cpp b/audio/core/all-versions/vts/functional/6.0/AudioPrimaryHidlHalTest.cpp
index 7e931ff..22e60be 100644
--- a/audio/core/all-versions/vts/functional/6.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/6.0/AudioPrimaryHidlHalTest.cpp
@@ -145,3 +145,49 @@
}();
return parameters;
}
+
+TEST_P(AudioHidlDeviceTest, CloseDeviceWithOpenedOutputStreams) {
+ doc::test("Verify that a device can't be closed if there are streams opened");
+ DeviceAddress address{.device = AudioDevice::OUT_DEFAULT};
+ AudioConfig config{};
+ auto flags = hidl_bitfield<AudioOutputFlag>(AudioOutputFlag::NONE);
+ SourceMetadata initMetadata = {{{AudioUsage::MEDIA, AudioContentType::MUSIC, 1 /* gain */}}};
+ sp<IStreamOut> stream;
+ StreamHelper<IStreamOut> helper(stream);
+ AudioConfig suggestedConfig{};
+ ASSERT_NO_FATAL_FAILURE(helper.open(
+ [&](AudioIoHandle handle, AudioConfig config, auto cb) {
+ return getDevice()->openOutputStream(handle, address, config, flags, initMetadata,
+ cb);
+ },
+ config, &res, &suggestedConfig));
+ ASSERT_RESULT(Result::INVALID_STATE, getDevice()->close());
+ ASSERT_NO_FATAL_FAILURE(helper.close(true /*clear*/, &res));
+ ASSERT_OK(getDevice()->close());
+ ASSERT_TRUE(resetDevice());
+}
+
+TEST_P(AudioHidlDeviceTest, CloseDeviceWithOpenedInputStreams) {
+ doc::test("Verify that a device can't be closed if there are streams opened");
+ auto module = getCachedPolicyConfig().getModuleFromName(getDeviceName());
+ if (module->getInputProfiles().empty()) {
+ GTEST_SKIP() << "Device doesn't have input profiles";
+ }
+ DeviceAddress address{.device = AudioDevice::IN_DEFAULT};
+ AudioConfig config{};
+ auto flags = hidl_bitfield<AudioInputFlag>(AudioInputFlag::NONE);
+ SinkMetadata initMetadata = {{{.source = AudioSource::MIC, .gain = 1}}};
+ sp<IStreamIn> stream;
+ StreamHelper<IStreamIn> helper(stream);
+ AudioConfig suggestedConfig{};
+ ASSERT_NO_FATAL_FAILURE(helper.open(
+ [&](AudioIoHandle handle, AudioConfig config, auto cb) {
+ return getDevice()->openInputStream(handle, address, config, flags, initMetadata,
+ cb);
+ },
+ config, &res, &suggestedConfig));
+ ASSERT_RESULT(Result::INVALID_STATE, getDevice()->close());
+ ASSERT_NO_FATAL_FAILURE(helper.close(true /*clear*/, &res));
+ ASSERT_OK(getDevice()->close());
+ ASSERT_TRUE(resetDevice());
+}