1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "AAudioEndpointManager"
18 //#define LOG_NDEBUG 0
19 #include <utils/Log.h>
20 
21 #include <assert.h>
22 #include <functional>
23 #include <map>
24 #include <mutex>
25 #include <sstream>
26 #include <utility/AAudioUtilities.h>
27 
28 #include "AAudioClientTracker.h"
29 #include "AAudioEndpointManager.h"
30 #include "AAudioServiceEndpointShared.h"
31 #include "AAudioServiceEndpointMMAP.h"
32 #include "AAudioServiceEndpointCapture.h"
33 #include "AAudioServiceEndpointPlay.h"
34 
35 using namespace android;
36 using namespace aaudio;
37 
38 ANDROID_SINGLETON_STATIC_INSTANCE(AAudioEndpointManager);
39 
AAudioEndpointManager()40 AAudioEndpointManager::AAudioEndpointManager()
41         : Singleton<AAudioEndpointManager>()
42         , mSharedStreams()
43         , mExclusiveStreams() {
44 }
45 
dump() const46 std::string AAudioEndpointManager::dump() const {
47     std::stringstream result;
48     int index = 0;
49 
50     result << "AAudioEndpointManager:" << "\n";
51 
52     const bool isSharedLocked = AAudio_tryUntilTrue(
53             [this]()->bool { return mSharedLock.try_lock(); } /* f */,
54             50 /* times */,
55             20 /* sleepMs */);
56     if (!isSharedLocked) {
57         result << "AAudioEndpointManager Shared may be deadlocked\n";
58     }
59 
60     {
61         const bool isExclusiveLocked = AAudio_tryUntilTrue(
62                 [this]() -> bool { return mExclusiveLock.try_lock(); } /* f */,
63                 50 /* times */,
64                 20 /* sleepMs */);
65         if (!isExclusiveLocked) {
66             result << "AAudioEndpointManager Exclusive may be deadlocked\n";
67         }
68 
69         result << "Exclusive MMAP Endpoints: " << mExclusiveStreams.size() << "\n";
70         index = 0;
71         for (const auto &stream : mExclusiveStreams) {
72             result << "  #" << index++ << ":";
73             result << stream->dump() << "\n";
74         }
75 
76         result << "  ExclusiveSearchCount:  " << mExclusiveSearchCount << "\n";
77         result << "  ExclusiveFoundCount:   " << mExclusiveFoundCount << "\n";
78         result << "  ExclusiveOpenCount:    " << mExclusiveOpenCount << "\n";
79         result << "  ExclusiveCloseCount:   " << mExclusiveCloseCount << "\n";
80         result << "  ExclusiveStolenCount:  " << mExclusiveStolenCount << "\n";
81         result << "\n";
82 
83         if (isExclusiveLocked) {
84             mExclusiveLock.unlock();
85         }
86     }
87 
88     result << "Shared Endpoints: " << mSharedStreams.size() << "\n";
89     index = 0;
90     for (const auto &stream : mSharedStreams) {
91         result << "  #" << index++ << ":";
92         result << stream->dump() << "\n";
93     }
94 
95     result << "  SharedSearchCount:     " << mSharedSearchCount << "\n";
96     result << "  SharedFoundCount:      " << mSharedFoundCount << "\n";
97     result << "  SharedOpenCount:       " << mSharedOpenCount << "\n";
98     result << "  SharedCloseCount:      " << mSharedCloseCount << "\n";
99     result << "\n";
100 
101     if (isSharedLocked) {
102         mSharedLock.unlock();
103     }
104     return result.str();
105 }
106 
107 
108 // Try to find an existing endpoint.
findExclusiveEndpoint_l(const AAudioStreamConfiguration & configuration)109 sp<AAudioServiceEndpoint> AAudioEndpointManager::findExclusiveEndpoint_l(
110         const AAudioStreamConfiguration &configuration) {
111     sp<AAudioServiceEndpoint> endpoint;
112     mExclusiveSearchCount++;
113     for (const auto& ep : mExclusiveStreams) {
114         if (ep->matches(configuration)) {
115             mExclusiveFoundCount++;
116             endpoint = ep;
117             break;
118         }
119     }
120 
121     ALOGV("findExclusiveEndpoint_l(), found %p for device = %d, sessionId = %d",
122           endpoint.get(), configuration.getDeviceId(), configuration.getSessionId());
123     return endpoint;
124 }
125 
126 // Try to find an existing endpoint.
findSharedEndpoint_l(const AAudioStreamConfiguration & configuration)127 sp<AAudioServiceEndpointShared> AAudioEndpointManager::findSharedEndpoint_l(
128         const AAudioStreamConfiguration &configuration) {
129     sp<AAudioServiceEndpointShared> endpoint;
130     mSharedSearchCount++;
131     for (const auto& ep  : mSharedStreams) {
132         if (ep->matches(configuration)) {
133             mSharedFoundCount++;
134             endpoint = ep;
135             break;
136         }
137     }
138 
139     ALOGV("findSharedEndpoint_l(), found %p for device = %d, sessionId = %d",
140           endpoint.get(), configuration.getDeviceId(), configuration.getSessionId());
141     return endpoint;
142 }
143 
openEndpoint(AAudioService & audioService,const aaudio::AAudioStreamRequest & request)144 sp<AAudioServiceEndpoint> AAudioEndpointManager::openEndpoint(AAudioService &audioService,
145                                         const aaudio::AAudioStreamRequest &request) {
146     if (request.getConstantConfiguration().getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) {
147         sp<AAudioServiceEndpoint> endpointToSteal;
148         sp<AAudioServiceEndpoint> foundEndpoint =
149                 openExclusiveEndpoint(audioService, request, endpointToSteal);
150         if (endpointToSteal.get()) {
151             endpointToSteal->releaseRegisteredStreams(); // free the MMAP resource
152         }
153         return foundEndpoint;
154     } else {
155         return openSharedEndpoint(audioService, request);
156     }
157 }
158 
openExclusiveEndpoint(AAudioService & aaudioService,const aaudio::AAudioStreamRequest & request,sp<AAudioServiceEndpoint> & endpointToSteal)159 sp<AAudioServiceEndpoint> AAudioEndpointManager::openExclusiveEndpoint(
160         AAudioService &aaudioService,
161         const aaudio::AAudioStreamRequest &request,
162         sp<AAudioServiceEndpoint> &endpointToSteal) {
163 
164     std::lock_guard<std::mutex> lock(mExclusiveLock);
165 
166     const AAudioStreamConfiguration &configuration = request.getConstantConfiguration();
167 
168     // Try to find an existing endpoint.
169     sp<AAudioServiceEndpoint> endpoint = findExclusiveEndpoint_l(configuration);
170 
171     // If we find an existing one then this one cannot be exclusive.
172     if (endpoint.get() != nullptr) {
173         if (kStealingEnabled
174                 && !endpoint->isForSharing() // not currently SHARED
175                 && !request.isSharingModeMatchRequired()) { // app did not request a shared stream
176             ALOGD("%s() endpoint in EXCLUSIVE use. Steal it!", __func__);
177             mExclusiveStolenCount++;
178             // Prevent this process from getting another EXCLUSIVE stream.
179             // This will prevent two clients from colliding after a DISCONNECTION
180             // when they both try to open an exclusive stream at the same time.
181             // That can result in a stream getting disconnected between the OPEN
182             // and START calls. This will help preserve app compatibility.
183             // An app can avoid having this happen by closing their streams when
184             // the app is paused.
185             AAudioClientTracker::getInstance().setExclusiveEnabled(request.getProcessId(), false);
186             endpointToSteal = endpoint; // return it to caller
187         }
188         return nullptr;
189     } else {
190         sp<AAudioServiceEndpointMMAP> endpointMMap = new AAudioServiceEndpointMMAP(aaudioService);
191         ALOGV("%s(), no match so try to open MMAP %p for dev %d",
192               __func__, endpointMMap.get(), configuration.getDeviceId());
193         endpoint = endpointMMap;
194 
195         aaudio_result_t result = endpoint->open(request);
196         if (result != AAUDIO_OK) {
197             endpoint.clear();
198         } else {
199             mExclusiveStreams.push_back(endpointMMap);
200             mExclusiveOpenCount++;
201         }
202     }
203 
204     if (endpoint.get() != nullptr) {
205         // Increment the reference count under this lock.
206         endpoint->setOpenCount(endpoint->getOpenCount() + 1);
207         endpoint->setForSharing(request.isSharingModeMatchRequired());
208     }
209 
210     return endpoint;
211 }
212 
openSharedEndpoint(AAudioService & aaudioService,const aaudio::AAudioStreamRequest & request)213 sp<AAudioServiceEndpoint> AAudioEndpointManager::openSharedEndpoint(
214         AAudioService &aaudioService,
215         const aaudio::AAudioStreamRequest &request) {
216 
217     std::lock_guard<std::mutex> lock(mSharedLock);
218 
219     const AAudioStreamConfiguration &configuration = request.getConstantConfiguration();
220     aaudio_direction_t direction = configuration.getDirection();
221 
222     // Try to find an existing endpoint.
223     sp<AAudioServiceEndpointShared> endpoint = findSharedEndpoint_l(configuration);
224 
225     // If we can't find an existing one then open a new one.
226     if (endpoint.get() == nullptr) {
227         // we must call openStream with audioserver identity
228         int64_t token = IPCThreadState::self()->clearCallingIdentity();
229         switch (direction) {
230             case AAUDIO_DIRECTION_INPUT:
231                 endpoint = new AAudioServiceEndpointCapture(aaudioService);
232                 break;
233             case AAUDIO_DIRECTION_OUTPUT:
234                 endpoint = new AAudioServiceEndpointPlay(aaudioService);
235                 break;
236             default:
237                 break;
238         }
239 
240         if (endpoint.get() != nullptr) {
241             aaudio_result_t result = endpoint->open(request);
242             if (result != AAUDIO_OK) {
243                 endpoint.clear();
244             } else {
245                 mSharedStreams.push_back(endpoint);
246                 mSharedOpenCount++;
247             }
248         }
249         ALOGV("%s(), created endpoint %p, requested device = %d, dir = %d",
250               __func__, endpoint.get(), configuration.getDeviceId(), (int)direction);
251         IPCThreadState::self()->restoreCallingIdentity(token);
252     }
253 
254     if (endpoint.get() != nullptr) {
255         // Increment the reference count under this lock.
256         endpoint->setOpenCount(endpoint->getOpenCount() + 1);
257     }
258     return endpoint;
259 }
260 
closeEndpoint(sp<AAudioServiceEndpoint> serviceEndpoint)261 void AAudioEndpointManager::closeEndpoint(sp<AAudioServiceEndpoint>serviceEndpoint) {
262     if (serviceEndpoint->getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) {
263         return closeExclusiveEndpoint(serviceEndpoint);
264     } else {
265         return closeSharedEndpoint(serviceEndpoint);
266     }
267 }
268 
closeExclusiveEndpoint(sp<AAudioServiceEndpoint> serviceEndpoint)269 void AAudioEndpointManager::closeExclusiveEndpoint(sp<AAudioServiceEndpoint> serviceEndpoint) {
270     if (serviceEndpoint.get() == nullptr) {
271         return;
272     }
273 
274     // Decrement the reference count under this lock.
275     std::lock_guard<std::mutex> lock(mExclusiveLock);
276     int32_t newRefCount = serviceEndpoint->getOpenCount() - 1;
277     serviceEndpoint->setOpenCount(newRefCount);
278 
279     // If no longer in use then actually close it.
280     if (newRefCount <= 0) {
281         mExclusiveStreams.erase(
282                 std::remove(mExclusiveStreams.begin(), mExclusiveStreams.end(), serviceEndpoint),
283                 mExclusiveStreams.end());
284 
285         serviceEndpoint->close();
286         mExclusiveCloseCount++;
287         ALOGV("%s() %p for device %d",
288               __func__, serviceEndpoint.get(), serviceEndpoint->getDeviceId());
289     }
290 }
291 
closeSharedEndpoint(sp<AAudioServiceEndpoint> serviceEndpoint)292 void AAudioEndpointManager::closeSharedEndpoint(sp<AAudioServiceEndpoint> serviceEndpoint) {
293     if (serviceEndpoint.get() == nullptr) {
294         return;
295     }
296 
297     // Decrement the reference count under this lock.
298     std::lock_guard<std::mutex> lock(mSharedLock);
299     int32_t newRefCount = serviceEndpoint->getOpenCount() - 1;
300     serviceEndpoint->setOpenCount(newRefCount);
301 
302     // If no longer in use then actually close it.
303     if (newRefCount <= 0) {
304         mSharedStreams.erase(
305                 std::remove(mSharedStreams.begin(), mSharedStreams.end(), serviceEndpoint),
306                 mSharedStreams.end());
307 
308         serviceEndpoint->close();
309         mSharedCloseCount++;
310         ALOGV("%s(%p) closed for device %d",
311               __func__, serviceEndpoint.get(), serviceEndpoint->getDeviceId());
312     }
313 }
314