Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ed261bbf authored by Mikhail Naganov's avatar Mikhail Naganov
Browse files

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
parent 422afc13
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -286,8 +286,12 @@ interface IDevice {
     * 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);
+7 −1
Original line number Diff line number Diff line
@@ -53,10 +53,14 @@ Result Device::analyzeStatus(const char* funcName, int status,

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 @@ std::tuple<Result, sp<IStreamOut>> Device::openOutputStreamImpl(int32_t ioHandle
    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 @@ std::tuple<Result, sp<IStreamIn>> Device::openInputStreamImpl(
    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 @@ Return<Result> Device::setConnectedState(const DeviceAddress& address, bool conn
#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));
}
+1 −0
Original line number Diff line number Diff line
@@ -130,6 +130,7 @@ struct Device : public IDevice, public ParametersUtil {
  private:
    bool mIsClosed;
    audio_hw_device_t* mDevice;
    int mOpenedStreamsCount = 0;

    virtual ~Device();

+46 −0
Original line number Diff line number Diff line
@@ -145,3 +145,49 @@ const std::vector<DeviceConfigParameter>& getInputDeviceConfigParameters() {
    }();
    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());
}