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

Commit 4abc4cb8 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes Ib6e8474b,I3693949e into main

* changes:
  Stop command thread only for the first close request.
  Add test for requesting close stream multiple times simultaneously.
parents 8fcf2fa7 e40864ed
Loading
Loading
Loading
Loading
+0 −6
Original line number Diff line number Diff line
@@ -49,12 +49,6 @@ enum {
};
typedef int32_t aaudio_policy_t;

// Internal error codes. Only used by the framework.
enum {
    AAUDIO_INTERNAL_ERROR_BASE = -1000,
    AAUDIO_ERROR_STANDBY,
};

/**
 * Control whether AAudioStreamBuilder_openStream() will use the new MMAP data path
 * or the older "Legacy" data path.
+8 −0
Original line number Diff line number Diff line
@@ -22,6 +22,14 @@

namespace aaudio {

// Internal error codes. Only used by the framework.
enum {
    AAUDIO_INTERNAL_ERROR_BASE = -1000,
    AAUDIO_ERROR_STANDBY,
    AAUDIO_ERROR_ALREADY_CLOSED,

};

aaudio_policy_t AudioGlobal_getMMapPolicy();
aaudio_result_t AudioGlobal_setMMapPolicy(aaudio_policy_t policy);

+27 −0
Original line number Diff line number Diff line
@@ -248,3 +248,30 @@ cc_binary {
    srcs: ["test_idle_disconnected_shared_stream.cpp"],
    shared_libs: ["libaaudio"],
}

cc_test {
    name: "test_multiple_close_simultaneously",
    defaults: [
        "latest_android_media_audio_common_types_cpp_shared",
        "libaaudio_tests_defaults",
    ],
    srcs: ["test_multiple_close_simultaneously.cpp"],
    shared_libs: [
        "aaudio-aidl-cpp",
        "framework-permission-aidl-cpp",
        "libaaudio",
        "libbinder",
        "liblog",
        "libutils",
    ],
    // This test will run 1 minute to ensure there is no crash happen.
    // In that case, set the timeout as 2 minutes to allow the test to complete.
    test_options: {
        test_runner_options: [
            {
                name: "native-test-timeout",
                value: "2m",
            }
        ],
    },
}
+153 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "test_multiple_close_simultaneously"

#include <chrono>
#include <condition_variable>
#include <shared_mutex>
#include <string>
#include <thread>

#include <gtest/gtest.h>

#include <binder/IBinder.h>
#include <binder/IServiceManager.h>
#include <utils/Log.h>

#include <aaudio/AAudio.h>
#include <aaudio/IAAudioService.h>
#include <aaudio/StreamRequest.h>
#include <aaudio/StreamParameters.h>

using namespace android;
using namespace aaudio;

#define AAUDIO_SERVICE_NAME "media.aaudio"

static constexpr int THREAD_NUM = 2;
static constexpr auto TEST_DURATION = std::chrono::minutes(1);

static std::string sError;
static bool sTestPassed = true;

struct Signal {
    std::atomic_int value{0};
    std::shared_mutex lock;
    std::condition_variable_any cv;
};

class AAudioServiceDeathRecipient : public IBinder::DeathRecipient {
public:
    void binderDied(const wp<IBinder>& who __unused) override {
        sError = "AAudioService is dead";
        ALOGE("%s", sError.c_str());
        sTestPassed = false;
    }
};

sp<IAAudioService> getAAudioService(const sp<IBinder::DeathRecipient>& recipient) {
    auto sm = defaultServiceManager();
    if (sm == nullptr) {
        sError = "Cannot get service manager";
        ALOGE("%s", sError.c_str());
        return nullptr;
    }
    sp<IBinder> binder = sm->waitForService(String16(AAUDIO_SERVICE_NAME));
    if (binder == nullptr) {
        sError = "Cannot get aaudio service";
        ALOGE("%s", sError.c_str());
        return nullptr;
    }
    if (binder->linkToDeath(recipient) != NO_ERROR) {
        sError = "Cannot link to binder death";
        ALOGE("%s", sError.c_str());
        return nullptr;
    }
    return interface_cast<IAAudioService>(binder);
}

void openAndMultipleClose(const sp<IAAudioService>& aaudioService) {
    auto start = std::chrono::system_clock::now();
    bool hasFailedOpening = false;
    while (sTestPassed && std::chrono::system_clock::now() - start < TEST_DURATION) {
        StreamRequest inRequest;
        StreamParameters outParams;
        int32_t handle = 0;
        inRequest.attributionSource.uid = getuid();
        inRequest.attributionSource.pid = getpid();
        inRequest.attributionSource.token = sp<BBinder>::make();
        auto status = aaudioService->openStream(inRequest, &outParams, &handle);
        if (!status.isOk()) {
            sError = "Cannot open stream, it can be caused by service death";
            ALOGE("%s", sError.c_str());
            sTestPassed = false;
            break;
        }
        if (handle <= 0) {
            sError = "Cannot get stream handle after open, returned handle"
                    + std::to_string(handle);
            ALOGE("%s", sError.c_str());
            sTestPassed = false;
            break;
        }
        hasFailedOpening = false;

        Signal isReady;
        Signal startWork;
        Signal isCompleted;
        std::unique_lock readyLock(isReady.lock);
        std::unique_lock completedLock(isCompleted.lock);
        for (int i = 0; i < THREAD_NUM; ++i) {
            std::thread closeStream([aaudioService, handle, &isReady, &startWork, &isCompleted] {
                isReady.value++;
                isReady.cv.notify_one();
                {
                    std::shared_lock<std::shared_mutex> _l(startWork.lock);
                    startWork.cv.wait(_l, [&startWork] { return startWork.value.load() == 1; });
                }
                int32_t result;
                aaudioService->closeStream(handle, &result);
                isCompleted.value++;
                isCompleted.cv.notify_one();
            });
            closeStream.detach();
        }
        isReady.cv.wait(readyLock, [&isReady] { return isReady.value == THREAD_NUM; });
        {
            std::unique_lock startWorkLock(startWork.lock);
            startWork.value.store(1);
        }
        startWork.cv.notify_all();
        isCompleted.cv.wait_for(completedLock,
                                std::chrono::milliseconds(1000),
                                [&isCompleted] { return isCompleted.value == THREAD_NUM; });
        if (isCompleted.value != THREAD_NUM) {
            sError = "Close is not completed within 1 second";
            ALOGE("%s", sError.c_str());
            sTestPassed = false;
            break;
        }
    }
}

TEST(test_multiple_close_simultaneously, open_multiple_close) {
    const auto recipient = sp<AAudioServiceDeathRecipient>::make();
    auto aaudioService = getAAudioService(recipient);
    ASSERT_NE(nullptr, aaudioService) << sError;
    openAndMultipleClose(aaudioService);
    ASSERT_TRUE(sTestPassed) << sError;
}
+18 −13
Original line number Diff line number Diff line
@@ -75,11 +75,7 @@ AAudioServiceStreamBase::~AAudioServiceStreamBase() {
                        this, getState());

    // Stop the command thread before destroying.
    if (mThreadEnabled) {
        mThreadEnabled = false;
        mCommandQueue.stopWaiting();
        mCommandThread.stop();
    }
    stopCommandThread();
}

std::string AAudioServiceStreamBase::dumpHeader() {
@@ -194,26 +190,27 @@ aaudio_result_t AAudioServiceStreamBase::open(const aaudio::AAudioStreamRequest

error:
    closeAndClear();
    mThreadEnabled = false;
    mCommandQueue.stopWaiting();
    mCommandThread.stop();
    stopCommandThread();
    return result;
}

aaudio_result_t AAudioServiceStreamBase::close() {
    aaudio_result_t result = sendCommand(CLOSE, nullptr, true /*waitForReply*/, TIMEOUT_NANOS);
    if (result == AAUDIO_ERROR_ALREADY_CLOSED) {
        // AAUDIO_ERROR_ALREADY_CLOSED is not a really error but just indicate the stream has
        // already been closed. In that case, there is no need to close the stream once more.
        ALOGD("The stream(%d) is already closed", mHandle);
        return AAUDIO_OK;
    }

    // Stop the command thread as the stream is closed.
    mThreadEnabled = false;
    mCommandQueue.stopWaiting();
    mCommandThread.stop();
    stopCommandThread();

    return result;
}

aaudio_result_t AAudioServiceStreamBase::close_l() {
    if (getState() == AAUDIO_STREAM_STATE_CLOSED) {
        return AAUDIO_OK;
        return AAUDIO_ERROR_ALREADY_CLOSED;
    }

    // This will stop the stream, just in case it was not already stopped.
@@ -766,3 +763,11 @@ aaudio_result_t AAudioServiceStreamBase::closeAndClear() {
        .record();
    return result;
}

void AAudioServiceStreamBase::stopCommandThread() {
    bool threadEnabled = true;
    if (mThreadEnabled.compare_exchange_strong(threadEnabled, false)) {
        mCommandQueue.stopWaiting();
        mCommandThread.stop();
    }
}
Loading