|  | /* | 
|  | * Copyright (C) 2017 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 "AAudioEndpointManager" | 
|  | //#define LOG_NDEBUG 0 | 
|  | #include <utils/Log.h> | 
|  |  | 
|  | #include <assert.h> | 
|  | #include <functional> | 
|  | #include <map> | 
|  | #include <mutex> | 
|  | #include <sstream> | 
|  | #include <utility/AAudioUtilities.h> | 
|  | #include <media/AidlConversion.h> | 
|  |  | 
|  | #include "AAudioClientTracker.h" | 
|  | #include "AAudioEndpointManager.h" | 
|  | #include "AAudioServiceEndpointShared.h" | 
|  | #include "AAudioServiceEndpointMMAP.h" | 
|  | #include "AAudioServiceEndpointCapture.h" | 
|  | #include "AAudioServiceEndpointPlay.h" | 
|  |  | 
|  | using namespace android; | 
|  | using namespace aaudio; | 
|  |  | 
|  | ANDROID_SINGLETON_STATIC_INSTANCE(AAudioEndpointManager); | 
|  |  | 
|  | AAudioEndpointManager::AAudioEndpointManager() | 
|  | : Singleton<AAudioEndpointManager>() | 
|  | , mSharedStreams() | 
|  | , mExclusiveStreams() { | 
|  | } | 
|  |  | 
|  | std::string AAudioEndpointManager::dump() const NO_THREAD_SAFETY_ANALYSIS { | 
|  | std::stringstream result; | 
|  | int index = 0; | 
|  |  | 
|  | result << "AAudioEndpointManager:" << "\n"; | 
|  |  | 
|  | const bool isSharedLocked = AAudio_tryUntilTrue( | 
|  | [this]()->bool { return mSharedLock.try_lock(); } /* f */, | 
|  | 50 /* times */, | 
|  | 20 /* sleepMs */); | 
|  | if (!isSharedLocked) { | 
|  | result << "AAudioEndpointManager Shared may be deadlocked\n"; | 
|  | } | 
|  |  | 
|  | { | 
|  | const bool isExclusiveLocked = AAudio_tryUntilTrue( | 
|  | [this]() -> bool { return mExclusiveLock.try_lock(); } /* f */, | 
|  | 50 /* times */, | 
|  | 20 /* sleepMs */); | 
|  | if (!isExclusiveLocked) { | 
|  | result << "AAudioEndpointManager Exclusive may be deadlocked\n"; | 
|  | } | 
|  |  | 
|  | result << "Exclusive MMAP Endpoints: " << mExclusiveStreams.size() << "\n"; | 
|  | index = 0; | 
|  | for (const auto &stream : mExclusiveStreams) { | 
|  | result << "  #" << index++ << ":"; | 
|  | result << stream->dump() << "\n"; | 
|  | } | 
|  |  | 
|  | result << "  ExclusiveSearchCount:  " << mExclusiveSearchCount << "\n"; | 
|  | result << "  ExclusiveFoundCount:   " << mExclusiveFoundCount << "\n"; | 
|  | result << "  ExclusiveOpenCount:    " << mExclusiveOpenCount << "\n"; | 
|  | result << "  ExclusiveCloseCount:   " << mExclusiveCloseCount << "\n"; | 
|  | result << "  ExclusiveStolenCount:  " << mExclusiveStolenCount << "\n"; | 
|  | result << "\n"; | 
|  |  | 
|  | if (isExclusiveLocked) { | 
|  | mExclusiveLock.unlock(); | 
|  | } | 
|  | } | 
|  |  | 
|  | result << "Shared Endpoints: " << mSharedStreams.size() << "\n"; | 
|  | index = 0; | 
|  | for (const auto &stream : mSharedStreams) { | 
|  | result << "  #" << index++ << ":"; | 
|  | result << stream->dump() << "\n"; | 
|  | } | 
|  |  | 
|  | result << "  SharedSearchCount:     " << mSharedSearchCount << "\n"; | 
|  | result << "  SharedFoundCount:      " << mSharedFoundCount << "\n"; | 
|  | result << "  SharedOpenCount:       " << mSharedOpenCount << "\n"; | 
|  | result << "  SharedCloseCount:      " << mSharedCloseCount << "\n"; | 
|  | result << "\n"; | 
|  |  | 
|  | if (isSharedLocked) { | 
|  | mSharedLock.unlock(); | 
|  | } | 
|  | return result.str(); | 
|  | } | 
|  |  | 
|  |  | 
|  | // Try to find an existing endpoint. | 
|  | sp<AAudioServiceEndpoint> AAudioEndpointManager::findExclusiveEndpoint_l( | 
|  | const AAudioStreamConfiguration &configuration) { | 
|  | sp<AAudioServiceEndpoint> endpoint; | 
|  | mExclusiveSearchCount++; | 
|  | for (const auto& ep : mExclusiveStreams) { | 
|  | if (ep->matches(configuration)) { | 
|  | mExclusiveFoundCount++; | 
|  | endpoint = ep; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | ALOGV("findExclusiveEndpoint_l(), found %p for device = %d, sessionId = %d", | 
|  | endpoint.get(), configuration.getDeviceId(), configuration.getSessionId()); | 
|  | return endpoint; | 
|  | } | 
|  |  | 
|  | // Try to find an existing endpoint. | 
|  | sp<AAudioServiceEndpointShared> AAudioEndpointManager::findSharedEndpoint_l( | 
|  | const AAudioStreamConfiguration &configuration) { | 
|  | sp<AAudioServiceEndpointShared> endpoint; | 
|  | mSharedSearchCount++; | 
|  | for (const auto& ep  : mSharedStreams) { | 
|  | if (ep->matches(configuration)) { | 
|  | mSharedFoundCount++; | 
|  | endpoint = ep; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | ALOGV("findSharedEndpoint_l(), found %p for device = %d, sessionId = %d", | 
|  | endpoint.get(), configuration.getDeviceId(), configuration.getSessionId()); | 
|  | return endpoint; | 
|  | } | 
|  |  | 
|  | sp<AAudioServiceEndpoint> AAudioEndpointManager::openEndpoint(AAudioService &audioService, | 
|  | const aaudio::AAudioStreamRequest &request) { | 
|  | if (request.getConstantConfiguration().getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) { | 
|  | 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); | 
|  | } | 
|  | } | 
|  |  | 
|  | sp<AAudioServiceEndpoint> AAudioEndpointManager::openExclusiveEndpoint( | 
|  | AAudioService &aaudioService, | 
|  | const aaudio::AAudioStreamRequest &request, | 
|  | sp<AAudioServiceEndpoint> &endpointToSteal) { | 
|  |  | 
|  | const std::lock_guard<std::mutex> lock(mExclusiveLock); | 
|  |  | 
|  | const AAudioStreamConfiguration &configuration = request.getConstantConfiguration(); | 
|  |  | 
|  | // Try to find an existing endpoint. | 
|  | sp<AAudioServiceEndpoint> endpoint = findExclusiveEndpoint_l(configuration); | 
|  |  | 
|  | // If we find an existing one then this one cannot be exclusive. | 
|  | if (endpoint.get() != nullptr) { | 
|  | 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++; | 
|  | // Prevent this process from getting another EXCLUSIVE stream. | 
|  | // This will prevent two clients from colliding after a DISCONNECTION | 
|  | // when they both try to open an exclusive stream at the same time. | 
|  | // That can result in a stream getting disconnected between the OPEN | 
|  | // and START calls. This will help preserve app compatibility. | 
|  | // An app can avoid having this happen by closing their streams when | 
|  | // the app is paused. | 
|  | const pid_t pid = VALUE_OR_FATAL( | 
|  | aidl2legacy_int32_t_pid_t(request.getAttributionSource().pid)); | 
|  | AAudioClientTracker::getInstance().setExclusiveEnabled(pid, false); | 
|  | endpointToSteal = endpoint; // return it to caller | 
|  | } | 
|  | return nullptr; | 
|  | } else { | 
|  | const sp<AAudioServiceEndpointMMAP> endpointMMap = | 
|  | new AAudioServiceEndpointMMAP(aaudioService); | 
|  | ALOGV("%s(), no match so try to open MMAP %p for dev %d", | 
|  | __func__, endpointMMap.get(), configuration.getDeviceId()); | 
|  | endpoint = endpointMMap; | 
|  |  | 
|  | const aaudio_result_t result = endpoint->open(request); | 
|  | if (result != AAUDIO_OK) { | 
|  | endpoint.clear(); | 
|  | } else { | 
|  | mExclusiveStreams.push_back(endpointMMap); | 
|  | mExclusiveOpenCount++; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (endpoint.get() != nullptr) { | 
|  | // Increment the reference count under this lock. | 
|  | endpoint->setOpenCount(endpoint->getOpenCount() + 1); | 
|  | endpoint->setForSharing(request.isSharingModeMatchRequired()); | 
|  | } | 
|  |  | 
|  | return endpoint; | 
|  | } | 
|  |  | 
|  | sp<AAudioServiceEndpoint> AAudioEndpointManager::openSharedEndpoint( | 
|  | AAudioService &aaudioService, | 
|  | const aaudio::AAudioStreamRequest &request) { | 
|  |  | 
|  | const std::lock_guard<std::mutex> lock(mSharedLock); | 
|  |  | 
|  | const AAudioStreamConfiguration &configuration = request.getConstantConfiguration(); | 
|  | const aaudio_direction_t direction = configuration.getDirection(); | 
|  |  | 
|  | // Try to find an existing endpoint. | 
|  | sp<AAudioServiceEndpointShared> endpoint = findSharedEndpoint_l(configuration); | 
|  |  | 
|  | // If we can't find an existing one then open a new one. | 
|  | if (endpoint.get() == nullptr) { | 
|  | // we must call openStream with audioserver identity | 
|  | const int64_t token = IPCThreadState::self()->clearCallingIdentity(); | 
|  | switch (direction) { | 
|  | case AAUDIO_DIRECTION_INPUT: | 
|  | endpoint = new AAudioServiceEndpointCapture(aaudioService); | 
|  | break; | 
|  | case AAUDIO_DIRECTION_OUTPUT: | 
|  | endpoint = new AAudioServiceEndpointPlay(aaudioService); | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (endpoint.get() != nullptr) { | 
|  | const aaudio_result_t result = endpoint->open(request); | 
|  | if (result != AAUDIO_OK) { | 
|  | endpoint.clear(); | 
|  | } else { | 
|  | mSharedStreams.push_back(endpoint); | 
|  | mSharedOpenCount++; | 
|  | } | 
|  | } | 
|  | ALOGV("%s(), created endpoint %p, requested device = %d, dir = %d", | 
|  | __func__, endpoint.get(), configuration.getDeviceId(), (int)direction); | 
|  | IPCThreadState::self()->restoreCallingIdentity(token); | 
|  | } | 
|  |  | 
|  | if (endpoint.get() != nullptr) { | 
|  | // Increment the reference count under this lock. | 
|  | endpoint->setOpenCount(endpoint->getOpenCount() + 1); | 
|  | } | 
|  | return endpoint; | 
|  | } | 
|  |  | 
|  | void AAudioEndpointManager::closeEndpoint(const sp<AAudioServiceEndpoint>& serviceEndpoint) { | 
|  | if (serviceEndpoint->getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) { | 
|  | return closeExclusiveEndpoint(serviceEndpoint); | 
|  | } else { | 
|  | return closeSharedEndpoint(serviceEndpoint); | 
|  | } | 
|  | } | 
|  |  | 
|  | void AAudioEndpointManager::closeExclusiveEndpoint( | 
|  | const sp<AAudioServiceEndpoint>& serviceEndpoint) { | 
|  | if (serviceEndpoint.get() == nullptr) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Decrement the reference count under this lock. | 
|  | const std::lock_guard<std::mutex> lock(mExclusiveLock); | 
|  | const int32_t newRefCount = serviceEndpoint->getOpenCount() - 1; | 
|  | serviceEndpoint->setOpenCount(newRefCount); | 
|  |  | 
|  | // If no longer in use then actually close it. | 
|  | if (newRefCount <= 0) { | 
|  | mExclusiveStreams.erase( | 
|  | std::remove(mExclusiveStreams.begin(), mExclusiveStreams.end(), serviceEndpoint), | 
|  | mExclusiveStreams.end()); | 
|  |  | 
|  | serviceEndpoint->close(); | 
|  | mExclusiveCloseCount++; | 
|  | ALOGV("%s() %p for device %d", | 
|  | __func__, serviceEndpoint.get(), serviceEndpoint->getDeviceId()); | 
|  | } | 
|  | } | 
|  |  | 
|  | void AAudioEndpointManager::closeSharedEndpoint(const sp<AAudioServiceEndpoint>& serviceEndpoint) { | 
|  | if (serviceEndpoint.get() == nullptr) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Decrement the reference count under this lock. | 
|  | const std::lock_guard<std::mutex> lock(mSharedLock); | 
|  | const int32_t newRefCount = serviceEndpoint->getOpenCount() - 1; | 
|  | serviceEndpoint->setOpenCount(newRefCount); | 
|  |  | 
|  | // If no longer in use then actually close it. | 
|  | if (newRefCount <= 0) { | 
|  | mSharedStreams.erase( | 
|  | std::remove(mSharedStreams.begin(), mSharedStreams.end(), serviceEndpoint), | 
|  | mSharedStreams.end()); | 
|  |  | 
|  | serviceEndpoint->close(); | 
|  |  | 
|  | mSharedCloseCount++; | 
|  | ALOGV("%s(%p) closed for device %d", | 
|  | __func__, serviceEndpoint.get(), serviceEndpoint->getDeviceId()); | 
|  | } | 
|  | } |