/* * 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 #include #include #include #include #include #include #include #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() , 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 AAudioEndpointManager::findExclusiveEndpoint_l( const AAudioStreamConfiguration &configuration) { sp 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 AAudioEndpointManager::findSharedEndpoint_l( const AAudioStreamConfiguration &configuration) { sp 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 AAudioEndpointManager::openEndpoint(AAudioService &audioService, const aaudio::AAudioStreamRequest &request) { if (request.getConstantConfiguration().getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) { sp endpointToSteal; sp foundEndpoint = openExclusiveEndpoint(audioService, request, endpointToSteal); if (endpointToSteal.get()) { endpointToSteal->releaseRegisteredStreams(); // free the MMAP resource } return foundEndpoint; } else { return openSharedEndpoint(audioService, request); } } sp AAudioEndpointManager::openExclusiveEndpoint( AAudioService &aaudioService, const aaudio::AAudioStreamRequest &request, sp &endpointToSteal) { std::lock_guard lock(mExclusiveLock); const AAudioStreamConfiguration &configuration = request.getConstantConfiguration(); // Try to find an existing endpoint. sp 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. 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 { sp endpointMMap = new AAudioServiceEndpointMMAP(aaudioService); 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) { 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 AAudioEndpointManager::openSharedEndpoint( AAudioService &aaudioService, const aaudio::AAudioStreamRequest &request) { std::lock_guard lock(mSharedLock); const AAudioStreamConfiguration &configuration = request.getConstantConfiguration(); aaudio_direction_t direction = configuration.getDirection(); // Try to find an existing endpoint. sp 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 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) { 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(spserviceEndpoint) { if (serviceEndpoint->getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) { return closeExclusiveEndpoint(serviceEndpoint); } else { return closeSharedEndpoint(serviceEndpoint); } } void AAudioEndpointManager::closeExclusiveEndpoint(sp serviceEndpoint) { if (serviceEndpoint.get() == nullptr) { return; } // Decrement the reference count under this lock. std::lock_guard lock(mExclusiveLock); 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(sp serviceEndpoint) { if (serviceEndpoint.get() == nullptr) { return; } // Decrement the reference count under this lock. std::lock_guard lock(mSharedLock); 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()); } }