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

Commit 6e463ce6 authored by Phil Burk's avatar Phil Burk
Browse files

aaudio: steal exclusive streams

An app (B) that asks for an exclusive stream can steal
an exclusive stream from an earlier app (A).
App B will be given the MMAP resource as a SHARED stream.
The stream for app A will be disconnected and released
by the service.
If app A reopens a stream then it will get a SHARED
stream.

The order of the opening of the streams is controlled by using a
recursive_mutex in AAudioService::openStream().

Bug: 129846760
Test: media/libaaudio/tests/test_steal_exclusive.cpp
Test: also
Test: Launch AudioTroubleMaker. It should say "EXCLUSIVE".
Test: Press Home button.
Test: Siren sound from AudioTroubleMaker should continue.
Test: Launch OboeTester
Test: TEST OUTPUT, then Open, Start
Test: You should get an MMAP SHARED stream on Pixel.
Test: Go back to AudioTroubleMaker. It should say "SHARED".
Change-Id: I7f8339d8ed62546520a9b46ed398418b41ca2832
parent 4756a9f7
Loading
Loading
Loading
Loading
+18 −2
Original line number Diff line number Diff line
@@ -354,6 +354,12 @@ aaudio_result_t AudioStreamInternal::requestStart()
    drainTimestampsFromService();

    aaudio_result_t result = mServiceInterface.startStream(mServiceStreamHandle);
    if (result == AAUDIO_ERROR_INVALID_HANDLE) {
        ALOGD("%s() INVALID_HANDLE, stream was probably stolen", __func__);
        // Stealing was added in R. Coerce result to improve backward compatibility.
        result = AAUDIO_ERROR_DISCONNECTED;
        setState(AAUDIO_STREAM_STATE_DISCONNECTED);
    }

    startTime = AudioClock::getNanoseconds();
    mClockModel.start(startTime);
@@ -397,7 +403,12 @@ aaudio_result_t AudioStreamInternal::stopCallback()
    if (isDataCallbackSet()
            && (isActive() || getState() == AAUDIO_STREAM_STATE_DISCONNECTED)) {
        mCallbackEnabled.store(false);
        return joinThread(NULL); // may temporarily unlock mStreamLock
        aaudio_result_t result = joinThread(NULL); // may temporarily unlock mStreamLock
        if (result == AAUDIO_ERROR_INVALID_HANDLE) {
            ALOGD("%s() INVALID_HANDLE, stream was probably stolen", __func__);
            result = AAUDIO_OK;
        }
        return result;
    } else {
        return AAUDIO_OK;
    }
@@ -427,7 +438,12 @@ aaudio_result_t AudioStreamInternal::requestStop() {
    setState(AAUDIO_STREAM_STATE_STOPPING);
    mAtomicInternalTimestamp.clear();

    return mServiceInterface.stopStream(mServiceStreamHandle);
    result = mServiceInterface.stopStream(mServiceStreamHandle);
    if (result == AAUDIO_ERROR_INVALID_HANDLE) {
        ALOGD("%s() INVALID_HANDLE, stream was probably stolen", __func__);
        result = AAUDIO_OK;
    }
    return result;
}

aaudio_result_t AudioStreamInternal::registerThread() {
+21 −7
Original line number Diff line number Diff line
@@ -76,6 +76,7 @@ std::string AAudioEndpointManager::dump() const {
        result << "  ExclusiveFoundCount:   " << mExclusiveFoundCount << "\n";
        result << "  ExclusiveOpenCount:    " << mExclusiveOpenCount << "\n";
        result << "  ExclusiveCloseCount:   " << mExclusiveCloseCount << "\n";
        result << "  ExclusiveStolenCount:  " << mExclusiveStolenCount << "\n";
        result << "\n";

        if (isExclusiveLocked) {
@@ -142,7 +143,13 @@ sp<AAudioServiceEndpointShared> AAudioEndpointManager::findSharedEndpoint_l(
sp<AAudioServiceEndpoint> AAudioEndpointManager::openEndpoint(AAudioService &audioService,
                                        const aaudio::AAudioStreamRequest &request) {
    if (request.getConstantConfiguration().getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) {
        return openExclusiveEndpoint(audioService, request);
        sp<AAudioServiceEndpoint> endpointToSteal;
        sp<AAudioServiceEndpoint> foundEndpoint =
                openExclusiveEndpoint(audioService, request, endpointToSteal);
        if (endpointToSteal.get()) {
            endpointToSteal->releaseRegisteredStreams(); // free the MMAP resource
        }
        return foundEndpoint;
    } else {
        return openSharedEndpoint(audioService, request);
    }
@@ -150,7 +157,8 @@ sp<AAudioServiceEndpoint> AAudioEndpointManager::openEndpoint(AAudioService &aud

sp<AAudioServiceEndpoint> AAudioEndpointManager::openExclusiveEndpoint(
        AAudioService &aaudioService,
        const aaudio::AAudioStreamRequest &request) {
        const aaudio::AAudioStreamRequest &request,
        sp<AAudioServiceEndpoint> &endpointToSteal) {

    std::lock_guard<std::mutex> lock(mExclusiveLock);

@@ -161,18 +169,22 @@ sp<AAudioServiceEndpoint> AAudioEndpointManager::openExclusiveEndpoint(

    // If we find an existing one then this one cannot be exclusive.
    if (endpoint.get() != nullptr) {
        ALOGW("openExclusiveEndpoint() already in use");
        // Already open so do not allow a second stream.
        if (kStealingEnabled
                && !endpoint->isForSharing() // not currently SHARED
                && !request.isSharingModeMatchRequired()) { // app did not request a shared stream
            ALOGD("%s() endpoint in EXCLUSIVE use. Steal it!", __func__);
            mExclusiveStolenCount++;
            endpointToSteal = endpoint;
        }
        return nullptr;
    } else {
        sp<AAudioServiceEndpointMMAP> endpointMMap = new AAudioServiceEndpointMMAP(aaudioService);
        ALOGV("openExclusiveEndpoint(), no match so try to open MMAP %p for dev %d",
              endpointMMap.get(), configuration.getDeviceId());
        ALOGV("%s(), no match so try to open MMAP %p for dev %d",
              __func__, endpointMMap.get(), configuration.getDeviceId());
        endpoint = endpointMMap;

        aaudio_result_t result = endpoint->open(request);
        if (result != AAUDIO_OK) {
            ALOGV("openExclusiveEndpoint(), open failed");
            endpoint.clear();
        } else {
            mExclusiveStreams.push_back(endpointMMap);
@@ -183,7 +195,9 @@ sp<AAudioServiceEndpoint> AAudioEndpointManager::openExclusiveEndpoint(
    if (endpoint.get() != nullptr) {
        // Increment the reference count under this lock.
        endpoint->setOpenCount(endpoint->getOpenCount() + 1);
        endpoint->setForSharing(request.isSharingModeMatchRequired());
    }

    return endpoint;
}

+8 −1
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@

#include <map>
#include <mutex>
#include <sys/types.h>
#include <utils/Singleton.h>

#include "binding/AAudioServiceMessage.h"
@@ -62,7 +63,8 @@ public:

private:
    android::sp<AAudioServiceEndpoint> openExclusiveEndpoint(android::AAudioService &aaudioService,
                                                 const aaudio::AAudioStreamRequest &request);
                                                 const aaudio::AAudioStreamRequest &request,
                                                 sp<AAudioServiceEndpoint> &endpointToSteal);

    android::sp<AAudioServiceEndpoint> openSharedEndpoint(android::AAudioService &aaudioService,
                                              const aaudio::AAudioStreamRequest &request);
@@ -91,11 +93,16 @@ private:
    int32_t mExclusiveFoundCount  = 0; // number of times we FOUND an exclusive endpoint
    int32_t mExclusiveOpenCount   = 0; // number of times we OPENED an exclusive endpoint
    int32_t mExclusiveCloseCount  = 0; // number of times we CLOSED an exclusive endpoint
    int32_t mExclusiveStolenCount = 0; // number of times we STOLE an exclusive endpoint

    // Same as above but for SHARED endpoints.
    int32_t mSharedSearchCount    = 0;
    int32_t mSharedFoundCount     = 0;
    int32_t mSharedOpenCount      = 0;
    int32_t mSharedCloseCount     = 0;

    // For easily disabling the stealing of exclusive streams.
    static constexpr bool kStealingEnabled = true;
};
} /* namespace aaudio */

+15 −1
Original line number Diff line number Diff line
@@ -85,6 +85,17 @@ bool AAudioService::isCallerInService() {

aaudio_handle_t AAudioService::openStream(const aaudio::AAudioStreamRequest &request,
                                          aaudio::AAudioStreamConfiguration &configurationOutput) {
    // A lock in is used to order the opening of endpoints when an
    // EXCLUSIVE endpoint is stolen. We want the order to be:
    // 1) Thread A opens exclusive MMAP endpoint
    // 2) Thread B wants to open an exclusive MMAP endpoint so it steals the one from A
    //    under this lock.
    // 3) Thread B opens a shared MMAP endpoint.
    // 4) Thread A can then get the lock and also open a shared stream.
    // Without the lock. Thread A might sneak in and reallocate an exclusive stream
    // before B can open the shared stream.
    std::unique_lock<std::recursive_mutex> lock(mOpenLock);

    aaudio_result_t result = AAUDIO_OK;
    sp<AAudioServiceStreamBase> serviceStream;
    const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
@@ -139,7 +150,6 @@ aaudio_handle_t AAudioService::openStream(const aaudio::AAudioStreamRequest &req
        return result;
    } else {
        aaudio_handle_t handle = mStreamTracker.addStreamForHandle(serviceStream.get());
        ALOGV("openStream(): handle = 0x%08X", handle);
        serviceStream->setHandle(handle);
        pid_t pid = request.getProcessId();
        AAudioClientTracker::getInstance().registerClientStream(pid, serviceStream);
@@ -147,6 +157,7 @@ aaudio_handle_t AAudioService::openStream(const aaudio::AAudioStreamRequest &req
        // Log open in MediaMetrics after we have the handle because we need the handle to
        // create the metrics ID.
        serviceStream->logOpen(handle);
        ALOGV("%s(): return handle = 0x%08X", __func__, handle);
        return handle;
    }
}
@@ -180,7 +191,10 @@ aaudio_result_t AAudioService::closeStream(aaudio_handle_t streamHandle) {
        ALOGE("closeStream(0x%0x), illegal stream handle", streamHandle);
        return AAUDIO_ERROR_INVALID_HANDLE;
    }
    return closeStream(serviceStream);
}

aaudio_result_t AAudioService::closeStream(sp<AAudioServiceStreamBase> serviceStream) {
    pid_t pid = serviceStream->getOwnerProcessId();
    AAudioClientTracker::getInstance().unregisterClientStream(pid, serviceStream);

+18 −2
Original line number Diff line number Diff line
@@ -55,6 +55,10 @@ public:
                                       aaudio::AAudioStreamConfiguration &configurationOutput)
                                       override;

    /*
     * This is called from Binder. It checks for permissions
     * and converts the handle passed through Binder to a stream pointer.
     */
    aaudio_result_t closeStream(aaudio::aaudio_handle_t streamHandle) override;

    aaudio_result_t getStreamDescription(
@@ -84,8 +88,18 @@ public:
    aaudio_result_t stopClient(aaudio::aaudio_handle_t streamHandle,
                                       audio_port_handle_t clientHandle) override;

 // ===============================================================================
 // The following public methods are only called from the service and NOT by Binder.
 // ===============================================================================

    aaudio_result_t disconnectStreamByPortHandle(audio_port_handle_t portHandle);

    /*
     * This is only called from within the Service.
     * It bypasses the permission checks in closeStream(handle).
     */
    aaudio_result_t closeStream(sp<aaudio::AAudioServiceStreamBase> serviceStream);

private:

    /** @return true if the client is the audioserver
@@ -100,8 +114,6 @@ private:
    sp<aaudio::AAudioServiceStreamBase> convertHandleToServiceStream(
            aaudio::aaudio_handle_t streamHandle);



    bool releaseStream(const sp<aaudio::AAudioServiceStreamBase> &serviceStream);

    aaudio_result_t checkForPendingClose(const sp<aaudio::AAudioServiceStreamBase> &serviceStream,
@@ -111,6 +123,10 @@ private:

    aaudio::AAudioStreamTracker     mStreamTracker;

    // We use a lock to prevent thread A from reopening an exclusive stream
    // after thread B steals thread A's exclusive MMAP resource stream.
    std::recursive_mutex            mOpenLock;

    // TODO  Extract the priority constants from services/audioflinger/Threads.cpp
    // and share them with this code. Look for "kPriorityFastMixer".
    static constexpr int32_t        kRealTimeAudioPriorityClient = 2;
Loading