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

Commit 0be9192f authored by Mikhail Naganov's avatar Mikhail Naganov
Browse files

libaudiohal@aidl: Fix handling of transient patch updates

There are cases when the framework opens a stream for one
device, and then issues a "create patch" command with
AUDIO_PATCH_HANDLE_NONE, and a different device. In that
case the mapper must match the patch using the mix port
handle, and then send a patch update command to the HAL.

Bug: 341326679
Test: atest CoreAudioHalAidlTest
Change-Id: Ic2ed15343f494f346de70af0f6d22fd59a3a81d7
parent 1ba5fd29
Loading
Loading
Loading
Loading
+15 −2
Original line number Diff line number Diff line
@@ -136,8 +136,8 @@ status_t Hal2AidlMapper::createOrUpdatePatch(
    // 'sinks' will not be updated because 'setAudioPatch' only needs IDs. Here we log
    // the source arguments, where only the audio configuration and device specifications
    // are relevant.
    ALOGD("%s: [disregard IDs] sources: %s, sinks: %s",
            __func__, ::android::internal::ToString(sources).c_str(),
    ALOGD("%s: patch ID: %d, [disregard IDs] sources: %s, sinks: %s",
            __func__, *patchId, ::android::internal::ToString(sources).c_str(),
            ::android::internal::ToString(sinks).c_str());
    auto fillPortConfigs = [&](
            const std::vector<AudioPortConfig>& configs,
@@ -209,11 +209,24 @@ status_t Hal2AidlMapper::createOrUpdatePatch(
        // that there can only be one patch for an I/O thread.
        PatchMatch match = sourceIsDevice && sinkIsDevice ?
                MATCH_BOTH : (sourceIsDevice ? MATCH_SINKS : MATCH_SOURCES);
        auto requestedPatch = patch;
        RETURN_STATUS_IF_ERROR(findOrCreatePatch(patch, match,
                                                 &patch, &created));
        // No cleanup of the patch is needed, it is managed by the framework.
        *patchId = patch.id;
        if (!created) {
            requestedPatch.id = patch.id;
            if (patch != requestedPatch) {
                ALOGI("%s: Updating transient patch. Current: %s, new: %s",
                        __func__, patch.toString().c_str(), requestedPatch.toString().c_str());
                // Since matching may be done by mix port only, update the patch if the device port
                // config has changed.
                patch = requestedPatch;
                RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
                                mModule->setAudioPatch(patch, &patch)));
                existingPatchIt = mPatches.find(patch.id);
                existingPatchIt->second = patch;
            }
            // The framework might have "created" a patch which already existed due to
            // stream creation. Need to release the ownership from the stream.
            for (auto& s : mStreams) {
+73 −0
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ using ::aidl::android::hardware::audio::core::AudioRoute;
using ::aidl::android::hardware::audio::core::VendorParameter;
using ::aidl::android::media::audio::common::AudioChannelLayout;
using ::aidl::android::media::audio::common::AudioConfig;
using ::aidl::android::media::audio::common::AudioDevice;
using ::aidl::android::media::audio::common::AudioDeviceDescription;
using ::aidl::android::media::audio::common::AudioDeviceType;
using ::aidl::android::media::audio::common::AudioFormatDescription;
@@ -160,6 +161,24 @@ Configuration getTestConfiguration() {
            createProfile(PcmType::INT_16_BIT, {AudioChannelLayout::LAYOUT_STEREO}, {48000})};
    Configuration c;

    AudioPort micInDevice =
            createPort(c.nextPortId++, "Built-In Mic", 0, true,
                       createPortDeviceExt(AudioDeviceType::IN_MICROPHONE,
                                           1 << AudioPortDeviceExt::FLAG_INDEX_DEFAULT_DEVICE));
    micInDevice.profiles = standardPcmAudioProfiles;
    c.ports.push_back(micInDevice);

    AudioPort micInBackDevice =
            createPort(c.nextPortId++, "Built-In Back Mic", 0, true,
                       createPortDeviceExt(AudioDeviceType::IN_MICROPHONE_BACK, 0));
    micInDevice.profiles = standardPcmAudioProfiles;
    c.ports.push_back(micInBackDevice);

    AudioPort primaryInMix =
            createPort(c.nextPortId++, "primary input", 0, true, createPortMixExt(0, 1));
    primaryInMix.profiles = standardPcmAudioProfiles;
    c.ports.push_back(primaryInMix);

    AudioPort btOutDevice =
            createPort(c.nextPortId++, "BT A2DP Out", 0, false,
                       createPortDeviceExt(AudioDeviceType::OUT_DEVICE, 0,
@@ -172,6 +191,7 @@ Configuration getTestConfiguration() {
    btOutMix.profiles = standardPcmAudioProfiles;
    c.ports.push_back(btOutMix);

    c.routes.push_back(createRoute({micInDevice, micInBackDevice}, primaryInMix));
    c.routes.push_back(createRoute({btOutMix}, btOutDevice));

    return c;
@@ -184,6 +204,11 @@ class ModuleMock : public ::aidl::android::hardware::audio::core::BnModule,
    explicit ModuleMock(const Configuration& config) : mConfig(config) {}
    bool isScreenTurnedOn() const { return mIsScreenTurnedOn; }
    ScreenRotation getScreenRotation() const { return mScreenRotation; }
    std::vector<AudioPatch> getPatches() {
        std::vector<AudioPatch> result;
        getAudioPatches(&result);
        return result;
    }

  private:
    ndk::ScopedAStatus setModuleDebug(
@@ -1141,3 +1166,51 @@ TEST_F(Hal2AidlMapperTest, DisconnectConnectCreateFwkPatchDisconnectReleaseClose
    EXPECT_EQ(0, mMapper->findFwkPatch(mPatch.id));
    EXPECT_EQ(0, mMapper->findFwkPatch(newPatchId));
}

TEST_F(Hal2AidlMapperTest, ChangeTransientPatchDevice) {
    std::mutex mutex;  // Only needed for cleanups.
    auto mapperAccessor = std::make_unique<LockedAccessor<Hal2AidlMapper>>(*mMapper, mutex);
    Hal2AidlMapper::Cleanups cleanups(*mapperAccessor);
    AudioConfig config;
    config.base.channelMask = AudioChannelLayout::make<AudioChannelLayout::layoutMask>(
            AudioChannelLayout::LAYOUT_STEREO);
    config.base.format =
            AudioFormatDescription{.type = AudioFormatType::PCM, .pcm = PcmType::INT_16_BIT};
    config.base.sampleRate = 48000;
    AudioDevice defaultDevice;
    defaultDevice.type.type = AudioDeviceType::IN_DEFAULT;
    AudioPortConfig mixPortConfig;
    AudioPatch transientPatch;
    ASSERT_EQ(OK, mMapper->prepareToOpenStream(43 /*ioHandle*/, defaultDevice,
                                               AudioIoFlags::make<AudioIoFlags::input>(0),
                                               AudioSource::DEFAULT, &cleanups, &config,
                                               &mixPortConfig, &transientPatch));
    cleanups.disarmAll();
    ASSERT_NE(0, transientPatch.id);
    ASSERT_NE(0, mixPortConfig.id);
    sp<StreamHalInterface> stream = sp<StreamHalMock>::make();
    mMapper->addStream(stream, mixPortConfig.id, transientPatch.id);

    AudioPatch patch{};
    int32_t patchId;
    AudioPortConfig backMicPortConfig;
    backMicPortConfig.channelMask = config.base.channelMask;
    backMicPortConfig.format = config.base.format;
    backMicPortConfig.sampleRate = aidl::android::media::audio::common::Int{config.base.sampleRate};
    backMicPortConfig.flags = AudioIoFlags::make<AudioIoFlags::input>(0);
    backMicPortConfig.ext = createPortDeviceExt(AudioDeviceType::IN_MICROPHONE_BACK, 0);
    ASSERT_EQ(OK, mMapper->createOrUpdatePatch({backMicPortConfig}, {mixPortConfig}, &patchId,
                                               &cleanups));
    cleanups.disarmAll();
    ASSERT_EQ(android::OK,
              mMapper->findPortConfig(backMicPortConfig.ext.get<AudioPortExt::device>().device,
                                      &backMicPortConfig));
    EXPECT_NE(0, backMicPortConfig.id);

    EXPECT_EQ(transientPatch.id, patchId);
    auto patches = mModule->getPatches();
    auto patchIt = findById(patches, patchId);
    ASSERT_NE(patchIt, patches.end());
    EXPECT_EQ(std::vector<int32_t>{backMicPortConfig.id}, patchIt->sourcePortConfigIds);
    EXPECT_EQ(std::vector<int32_t>{mixPortConfig.id}, patchIt->sinkPortConfigIds);
}