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

Commit c71b11be authored by Eric Laurent's avatar Eric Laurent
Browse files

Audio policy: fix input preemption logic

Fix the input preemption logic in AudioPolicyManager::getInputForDevice
when the maximum number of opened input stream is reached:
- avoid preemption loops between two AudioRecord clients
- avoid cases were no preemption occurs but an attempt is made to open a
  new input anyway.

The new logic is:
1) reuse an input if the new client has the same session as an existing
   client on this input
2) try to close/preempt an input if possible and then reuse an input
3) the candidate inputs for preemption are inputs with strictly lower
   priority use cases than the new client, or same priority that were not opened
   thanks to preemption or have been active since, ordered by use case priority
   and favoring idle inputs as second criteria
4) if no candidates for preemption are found, reuse preferably inputs
   with the same use case as the new client and favoring active inputs
   as second criteria.

Bug: 338446410
Test: atest audiopolicy_tests:AudioPolicyManagerInputPreemptionTest
Flag: com.android.media.audioserver.fix_input_sharing_logic
Change-Id: I82a953edcf1b3ea76a44ae0d02c943daaacfea64
parent 311daa69
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -41,7 +41,8 @@ class AudioInputDescriptor: public AudioPortConfig,
{
public:
    AudioInputDescriptor(const sp<IOProfile>& profile,
                         AudioPolicyClientInterface *clientInterface);
                         AudioPolicyClientInterface *clientInterface,
                         bool isPreemptor);

    virtual ~AudioInputDescriptor() = default;

@@ -127,6 +128,8 @@ public:
    // active use case
    void checkSuspendEffects();

    bool isPreemptor() const { return mIsPreemptor; }

 private:

    void updateClientRecordingConfiguration(int event, const sp<RecordClientDescriptor>& client);
@@ -145,6 +148,7 @@ public:
    int32_t mGlobalActiveCount = 0;  // non-client-specific activity ref count
    EffectDescriptorCollection mEnabledEffects;
    audio_input_flags_t& mFlags = AudioPortConfig::mFlags.input;
    bool mIsPreemptor; // true if this input was opened after preemting another one
};

class AudioInputCollection :
+6 −2
Original line number Diff line number Diff line
@@ -30,9 +30,10 @@
namespace android {

AudioInputDescriptor::AudioInputDescriptor(const sp<IOProfile>& profile,
                                           AudioPolicyClientInterface *clientInterface)
                                           AudioPolicyClientInterface *clientInterface,
                                           bool isPreemptor)
    : mProfile(profile)
    ,  mClientInterface(clientInterface)
    ,  mClientInterface(clientInterface), mIsPreemptor(isPreemptor)
{
    if (profile != NULL) {
        profile->pickAudioProfile(mSamplingRate, mChannelMask, mFormat);
@@ -275,6 +276,9 @@ void AudioInputDescriptor::stop()
                            "%s invalid profile active count %u",
                            __func__, mProfile->curActiveCount);
        mProfile->curActiveCount--;
        // allow preemption again now that at least one client was able to
        // capture on this input
        mIsPreemptor = false;
    }
}

+100 −28
Original line number Diff line number Diff line
@@ -3082,7 +3082,77 @@ audio_io_handle_t AudioPolicyManager::getInputForDevice(const sp<DeviceDescripto
        }
    }

    bool isPreemptor = false;
    if (!profile->canOpenNewIo()) {
        if (com::android::media::audioserver::fix_input_sharing_logic()) {
            //  First pick best candidate for preemption (there may not be any):
            //  - Preempt and input if:
            //     - It has only strictly lower priority use cases than the new client
            //     - It has equal priority use cases than the new client, was not
            //     opened thanks to preemption or has been active since opened.
            //  - Order the preemption candidates by inactive first and priority second
            sp<AudioInputDescriptor> closeCandidate;
            int leastCloseRank = INT_MAX;
            static const int sCloseActive = 0x100;

            for (size_t i = 0; i < mInputs.size(); i++) {
                sp<AudioInputDescriptor> desc = mInputs.valueAt(i);
                if (desc->mProfile != profile) {
                    continue;
                }
                sp<RecordClientDescriptor> topPrioClient = desc->getHighestPriorityClient();
                if (topPrioClient == nullptr) {
                    continue;
                }
                int topPrio = source_priority(topPrioClient->source());
                if (topPrio < source_priority(attributes.source)
                      || (topPrio == source_priority(attributes.source)
                          && !desc->isPreemptor())) {
                    int closeRank = (desc->isActive() ? sCloseActive : 0) + topPrio;
                    if (closeRank < leastCloseRank) {
                        leastCloseRank = closeRank;
                        closeCandidate = desc;
                    }
                }
            }

            if (closeCandidate != nullptr) {
                closeInput(closeCandidate->mIoHandle);
                // Mark the new input as being issued from a preemption
                // so that is will not be preempted later
                isPreemptor = true;
            } else {
                // Then pick the best reusable input (There is always one)
                // The order of preference is:
                // 1) active inputs with same use case as the new client
                // 2) inactive inputs with same use case
                // 3) active inputs with different use cases
                // 4) inactive inputs with different use cases
                sp<AudioInputDescriptor> reuseCandidate;
                int leastReuseRank = INT_MAX;
                static const int sReuseDifferentUseCase = 0x100;

                for (size_t i = 0; i < mInputs.size(); i++) {
                    sp<AudioInputDescriptor> desc = mInputs.valueAt(i);
                    if (desc->mProfile != profile) {
                        continue;
                    }
                    int reuseRank = sReuseDifferentUseCase;
                    for (const auto& client: desc->getClientIterable()) {
                        if (client->source() == attributes.source) {
                            reuseRank = 0;
                            break;
                        }
                    }
                    reuseRank += desc->isActive() ? 0 : 1;
                    if (reuseRank < leastReuseRank) {
                        leastReuseRank = reuseRank;
                        reuseCandidate = desc;
                    }
                }
                return reuseCandidate->mIoHandle;
            }
        } else { // fix_input_sharing_logic()
            for (size_t i = 0; i < mInputs.size(); ) {
                 sp<AudioInputDescriptor> desc = mInputs.valueAt(i);
                 if (desc->mProfile != profile) {
@@ -3117,8 +3187,10 @@ audio_io_handle_t AudioPolicyManager::getInputForDevice(const sp<DeviceDescripto
                }
            }
        }
    }

    sp<AudioInputDescriptor> inputDesc = new AudioInputDescriptor(profile, mpClientInterface);
    sp<AudioInputDescriptor> inputDesc = new AudioInputDescriptor(
            profile, mpClientInterface, isPreemptor);

    audio_config_t lConfig = AUDIO_CONFIG_INITIALIZER;
    lConfig.sample_rate = profileSamplingRate;
@@ -6621,8 +6693,8 @@ void AudioPolicyManager::onNewAudioModulesAvailableInt(DeviceVector *newDevices)
                    __func__, inProfile->getTagName().c_str());
                continue;
            }
            sp<AudioInputDescriptor> inputDesc =
                    new AudioInputDescriptor(inProfile, mpClientInterface);
            sp<AudioInputDescriptor> inputDesc = new AudioInputDescriptor(
                    inProfile, mpClientInterface, false /*isPreemptor*/);

            audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
            status_t status = inputDesc->open(nullptr,
@@ -6932,7 +7004,7 @@ status_t AudioPolicyManager::checkInputsForDevice(const sp<DeviceDescriptor>& de
                continue;
            }

            desc = new AudioInputDescriptor(profile, mpClientInterface);
            desc = new AudioInputDescriptor(profile, mpClientInterface, false  /*isPreemptor*/);
            audio_io_handle_t input = AUDIO_IO_HANDLE_NONE;
            status = desc->open(nullptr, device, AUDIO_SOURCE_MIC, AUDIO_INPUT_FLAG_NONE, &input);

+16 −0
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ public:
        *input = mNextIoHandle++;
        mOpenedInputs.insert(*input);
        ALOGD("%s: opened input %d", __func__, *input);
        mOpenInputCallsCount++;
        return NO_ERROR;
    }

@@ -86,6 +87,7 @@ public:
            return BAD_VALUE;
        }
        ALOGD("%s: closed input %d", __func__, input);
        mCloseInputCallsCount++;
        return NO_ERROR;
    }

@@ -260,6 +262,18 @@ public:
        auto it = mTracksInternalMute.find(portId);
        return it == mTracksInternalMute.end() ? false : it->second;
    }
    void resetInputApiCallsCounters() {
        mOpenInputCallsCount = 0;
        mCloseInputCallsCount = 0;
    }

    size_t getCloseInputCallsCount() const {
        return mCloseInputCallsCount;
    }

    size_t getOpenInputCallsCount() const {
        return mOpenInputCallsCount;
    }

private:
    audio_module_handle_t mNextModuleHandle = AUDIO_MODULE_HANDLE_NONE + 1;
@@ -275,6 +289,8 @@ private:
    std::set<audio_channel_mask_t> mSupportedChannelMasks;
    std::map<audio_port_handle_t, bool> mTracksInternalMute;
    std::set<audio_io_handle_t> mOpenedInputs;
    size_t mOpenInputCallsCount = 0;
    size_t mCloseInputCallsCount = 0;
};

} // namespace android
+89 −0
Original line number Diff line number Diff line
@@ -3830,3 +3830,92 @@ INSTANTIATE_TEST_CASE_P(
        testing::Values(AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE,
                        AUDIO_USAGE_ALARM)
);

class AudioPolicyManagerInputPreemptionTest : public AudioPolicyManagerTestWithConfigurationFile {
};

TEST_F_WITH_FLAGS(
        AudioPolicyManagerInputPreemptionTest,
        SameSessionReusesInput,
        REQUIRES_FLAGS_ENABLED(
                ACONFIG_FLAG(com::android::media::audioserver, fix_input_sharing_logic))
) {
    mClient->resetInputApiCallsCounters();

    audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
    attr.source = AUDIO_SOURCE_MIC;
    audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
    audio_io_handle_t input1 = AUDIO_PORT_HANDLE_NONE;
    ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input1, TEST_SESSION_ID, 1, &selectedDeviceId,
                                            AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
                                            48000));

    EXPECT_EQ(1, mClient->getOpenInputCallsCount());

    audio_io_handle_t input2 = AUDIO_PORT_HANDLE_NONE;
    ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input2, TEST_SESSION_ID, 1, &selectedDeviceId,
                                        AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
                                        48000));

    EXPECT_EQ(1, mClient->getOpenInputCallsCount());
    EXPECT_EQ(0, mClient->getCloseInputCallsCount());
    EXPECT_EQ(input1, input2);
}

TEST_F_WITH_FLAGS(
        AudioPolicyManagerInputPreemptionTest,
        LesserPriorityReusesInput,
        REQUIRES_FLAGS_ENABLED(
                ACONFIG_FLAG(com::android::media::audioserver, fix_input_sharing_logic))
) {
    mClient->resetInputApiCallsCounters();

    audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
    attr.source = AUDIO_SOURCE_MIC;
    audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
    audio_io_handle_t input1 = AUDIO_PORT_HANDLE_NONE;
    ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input1, TEST_SESSION_ID, 1, &selectedDeviceId,
                                            AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
                                            48000));

    EXPECT_EQ(1, mClient->getOpenInputCallsCount());

    audio_io_handle_t input2 = AUDIO_PORT_HANDLE_NONE;
    attr.source = AUDIO_SOURCE_VOICE_RECOGNITION;
    ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input2, OTHER_SESSION_ID, 1, &selectedDeviceId,
                                        AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
                                        48000));

    EXPECT_EQ(1, mClient->getOpenInputCallsCount());
    EXPECT_EQ(0, mClient->getCloseInputCallsCount());
    EXPECT_EQ(input1, input2);
}

TEST_F_WITH_FLAGS(
        AudioPolicyManagerInputPreemptionTest,
        HigherPriorityPreemptsInput,
        REQUIRES_FLAGS_ENABLED(
                ACONFIG_FLAG(com::android::media::audioserver, fix_input_sharing_logic))
) {
    mClient->resetInputApiCallsCounters();

    audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
    attr.source = AUDIO_SOURCE_MIC;
    audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
    audio_io_handle_t input1 = AUDIO_PORT_HANDLE_NONE;
    ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input1, TEST_SESSION_ID, 1, &selectedDeviceId,
                                            AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
                                            48000));

    EXPECT_EQ(1, mClient->getOpenInputCallsCount());

    audio_io_handle_t input2 = AUDIO_PORT_HANDLE_NONE;
    attr.source = AUDIO_SOURCE_CAMCORDER;
    ASSERT_NO_FATAL_FAILURE(getInputForAttr(attr, &input2, OTHER_SESSION_ID, 1, &selectedDeviceId,
                                        AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO,
                                        48000));

    EXPECT_EQ(2, mClient->getOpenInputCallsCount());
    EXPECT_EQ(1, mClient->getCloseInputCallsCount());
    EXPECT_NE(input1, input2);
}
Loading