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

Commit 026c2a54 authored by Mikhail Naganov's avatar Mikhail Naganov
Browse files

libaudiohal@aidl: Support clip transition reporting

If the AIDL HAL is at least V4 or it advertises clip
transition reporting support via parameter
'aosp.clipTransitionSupport' then expect two calls
of 'onDrainReady' callback for the "early notify" drain.

Add tests to validate this behavior on the client side.
Tests are also gated by the reporting support.

Bug: 370117758
Bug: 373872271
Bug: 384431822
Test: atest audiotrack_tests
Test: atest CoreAudioHalAidlTest
Change-Id: Iaf459298c46d60e3f8878dbf1f4505c1e8d9a9b0
parent 6efc6c01
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -258,6 +258,14 @@ status_t AudioPlayback::onProcess(bool testSeek) {
        return INVALID_OPERATION;
}

void AudioPlayback::pause() {
    mTrack->pause();
}

void AudioPlayback::resume() {
    mTrack->start();
}

void AudioPlayback::stop() {
    {
        std::lock_guard lock(mMutex);
+2 −0
Original line number Diff line number Diff line
@@ -124,6 +124,8 @@ class AudioPlayback : public AudioTrack::IAudioTrackCallback {
    status_t waitForConsumption(bool testSeek = false) EXCLUDES(mMutex);
    status_t fillBuffer();
    status_t onProcess(bool testSeek = false);
    void pause();
    void resume();
    void stop() EXCLUDES(mMutex);
    bool waitForStreamEnd();

+79 −13
Original line number Diff line number Diff line
@@ -180,7 +180,29 @@ TEST(AudioTrackTest, TestAudioCbNotifier) {
    ap->stop();
}

TEST(AudioTrackTest, OffloadCompletion) {
class AudioTrackOffloadTest : public ::testing::Test {
  protected:
    void TearDown() override {
        if (!IsSkipped()) {
            // Let the offload AF stream to exit to avoid interfering with other tests.
            std::this_thread::sleep_for(std::chrono::milliseconds(750));
        }
    }
    bool halSupportsClipTransition() const;
    void testPlayback(bool testDrainPause, sp<AudioPlayback>* outPlayback = nullptr);
};

bool AudioTrackOffloadTest::halSupportsClipTransition() const {
    // TODO: Check for the HAL type (HIDL/AIDL) and version. HIDL and AIDL V4 should also
    //       support this.
    AudioParameter param;
    param.addKey(String8(AudioParameter::keyClipTransitionSupport));
    String8 values = AudioSystem::getParameters(AUDIO_IO_HANDLE_NONE, param.keysToString());
    LOG(DEBUG) << __func__ << ": values \"" << values << "\"";
    return !values.empty();
}

void AudioTrackOffloadTest::testPlayback(bool testDrainPause, sp<AudioPlayback>* outPlayback) {
    audio_offload_info_t info = AUDIO_INFO_INITIALIZER;
    info.sample_rate = 48000;
    info.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
@@ -189,14 +211,6 @@ TEST(AudioTrackTest, OffloadCompletion) {
    info.bit_rate = 236256;
    info.duration_us = 120 * 1000000;  // 120 sec to ensure the offloading choice

    audio_config_base_t config = {/* .sample_rate = */ info.sample_rate,
                                  /* .channel_mask = */ info.channel_mask,
                                  /* .format = */ AUDIO_FORMAT_PCM_16_BIT};
    audio_attributes_t attributes = AUDIO_ATTRIBUTES_INITIALIZER;
    attributes.content_type = AUDIO_CONTENT_TYPE_MUSIC;
    attributes.usage = AUDIO_USAGE_MEDIA;
    attributes.flags = AUDIO_FLAG_NONE;

    if (AUDIO_OFFLOAD_NOT_SUPPORTED == AudioSystem::getOffloadSupport(info)) {
        GTEST_SKIP() << "offload playback is not supported for "
                     << audio_format_to_string(info.format);
@@ -207,13 +221,65 @@ TEST(AudioTrackTest, OffloadCompletion) {
    ASSERT_EQ(OK, ap->loadResource("/data/local/tmp/sine960hz_48000_3s.ape"))
            << "unable to open the media file";
    ASSERT_EQ(OK, ap->create()) << "track creation failed";
    EXPECT_EQ(OK, ap->start()) << "audio track start failed";
    ASSERT_EQ(OK, ap->start()) << "audio track start failed";
    LOG(INFO) << __func__ << ": Started track";
    EXPECT_EQ(OK, ap->onProcess());
    LOG(INFO) << __func__ << ": onProcess done";
    if (!outPlayback) {
        ap->stop();
    LOG(INFO) << __func__ << ": Stopped track, waiting for stream end";
        LOG(INFO) << __func__ << ": Stopped track";
    }
    if (testDrainPause) {
        // Wait for draining to start, no event for this.
        std::this_thread::sleep_for(std::chrono::milliseconds(200));
        LOG(INFO) << __func__ << ": Pausing drain";
        ap->pause();
        LOG(INFO) << __func__ << ": Resuming drain";
        ap->resume();
    }
    if (!outPlayback) {
        LOG(INFO) << __func__ << ": Waiting for stream end";
        EXPECT_TRUE(ap->waitForStreamEnd()) << "Did not receive onStreamEnd";
    } else {
        *outPlayback = std::move(ap);
    }
}

TEST_F(AudioTrackOffloadTest, Completion) {
    testPlayback(false /*testDrainPause*/);
}

TEST_F(AudioTrackOffloadTest, DrainPause) {
    if (!halSupportsClipTransition()) {
        // TODO: In theory, this should also work w/o having the proper clip transition
        //       support, but as a fact it was not. Need to figure out why.
        GTEST_SKIP() << "Proper indication of clip transition is not supported";
    }
    testPlayback(true /*testDrainPause*/);
}

// Similar to AudioTrackOffloadTest.testMultipleAudioTrackOffloadPreemption
TEST_F(AudioTrackOffloadTest, ClipPreemption) {
    if (!halSupportsClipTransition()) {
        GTEST_SKIP() << "Proper indication of clip transition is not supported";
    }
    sp<AudioPlayback> trackOne, trackTwo;
    {
        SCOPED_TRACE("track 1");
        LOG(INFO) << __func__ << ": Creating and starting track 1";
        ASSERT_NO_FATAL_FAILURE(testPlayback(false /*testDrainPause*/, &trackOne));
    }
    {
        SCOPED_TRACE("track 2");
        // Wait for track 1 to start playing, no event for this.
        std::this_thread::sleep_for(std::chrono::milliseconds(300));
        LOG(INFO) << __func__ << ": Creating and starting track 2";
        ASSERT_NO_FATAL_FAILURE(testPlayback(false /*testDrainPause*/, &trackTwo));
        std::this_thread::sleep_for(std::chrono::milliseconds(200));
        trackTwo->stop();
    }
    LOG(INFO) << __func__ << ": Waiting for stream end on track 2";
    EXPECT_TRUE(trackTwo->waitForStreamEnd()) << "Did not receive onStreamEnd on track 2";
}

class AudioTrackCreateTest
+18 −2
Original line number Diff line number Diff line
@@ -94,6 +94,10 @@ namespace android {

namespace {

static constexpr int32_t kAidlVersion1 = 1;
static constexpr int32_t kAidlVersion2 = 2;
static constexpr int32_t kAidlVersion3 = 3;

// Note: these converters are for types defined in different AIDL files. Although these
// AIDL files are copies of each other, however formally these are different types
// thus we don't use a conversion via a parcelable.
@@ -175,6 +179,17 @@ status_t DeviceHalAidl::initCheck() {
    TIME_CHECK();
    RETURN_IF_MODULE_NOT_INIT(NO_INIT);
    std::lock_guard l(mLock);
    int32_t aidlVersion = 0;
    RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->getInterfaceVersion(&aidlVersion)));
    if (aidlVersion > kAidlVersion3) {
        mHasClipTransitionSupport = true;
    } else {
        AudioParameter parameterKeys;
        parameterKeys.addKey(String8(AudioParameter::keyClipTransitionSupport));
        String8 values;
        auto status = parseAndGetVendorParameters(mVendorExt, mModule, parameterKeys, &values);
        mHasClipTransitionSupport = status == OK && !values.empty();
    }
    return mMapper.initialize();
}

@@ -545,7 +560,7 @@ status_t DeviceHalAidl::openOutputStream(
        std::lock_guard l(mLock);
        RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->openOutputStream(args, &ret)));
    }
    StreamContextAidl context(ret.desc, isOffload, aidlHandle);
    StreamContextAidl context(ret.desc, isOffload, aidlHandle, mHasClipTransitionSupport);
    if (!context.isValid()) {
        AUGMENT_LOG(E, "Failed to created a valid stream context from the descriptor: %s",
                    ret.desc.toString().c_str());
@@ -627,7 +642,8 @@ status_t DeviceHalAidl::openInputStream(
        std::lock_guard l(mLock);
        RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->openInputStream(args, &ret)));
    }
    StreamContextAidl context(ret.desc, false /*isAsynchronous*/, aidlHandle);
    StreamContextAidl context(
            ret.desc, false /*isAsynchronous*/, aidlHandle, mHasClipTransitionSupport);
    if (!context.isValid()) {
        AUGMENT_LOG(E, "Failed to created a valid stream context from the descriptor: %s",
                    ret.desc.toString().c_str());
+1 −0
Original line number Diff line number Diff line
@@ -265,6 +265,7 @@ class DeviceHalAidl : public DeviceHalInterface, public ConversionHelperAidl,
    Hal2AidlMapper mMapper GUARDED_BY(mLock);
    LockedAccessor<Hal2AidlMapper> mMapperAccessor;
    Microphones mMicrophones GUARDED_BY(mLock);
    bool mHasClipTransitionSupport = false;
};

} // namespace android
Loading