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 "AAudioService"
18//#define LOG_NDEBUG 0
19#include <utils/Log.h>
20
21#include <assert.h>
22#include <map>
23#include <mutex>
24#include <utils/Singleton.h>
25
26#include "AAudioEndpointManager.h"
27#include "AAudioServiceEndpoint.h"
28#include <algorithm>
29#include <mutex>
30#include <vector>
31
32#include "core/AudioStreamBuilder.h"
33#include "AAudioServiceEndpoint.h"
34#include "AAudioServiceStreamShared.h"
35
36using namespace android;  // TODO just import names needed
37using namespace aaudio;   // TODO just import names needed
38
39#define MIN_TIMEOUT_NANOS        (1000 * AAUDIO_NANOS_PER_MILLISECOND)
40
41// Wait at least this many times longer than the operation should take.
42#define MIN_TIMEOUT_OPERATIONS    4
43
44// This is the maximum size in frames. The effective size can be tuned smaller at runtime.
45#define DEFAULT_BUFFER_CAPACITY   (48 * 8)
46
47// Set up an EXCLUSIVE MMAP stream that will be shared.
48aaudio_result_t AAudioServiceEndpoint::open(int32_t deviceId) {
49    mStreamInternal = getStreamInternal();
50
51    AudioStreamBuilder builder;
52    builder.setSharingMode(AAUDIO_SHARING_MODE_EXCLUSIVE);
53    // Don't fall back to SHARED because that would cause recursion.
54    builder.setSharingModeMatchRequired(true);
55    builder.setDeviceId(deviceId);
56    builder.setDirection(getDirection());
57    builder.setBufferCapacity(DEFAULT_BUFFER_CAPACITY);
58
59    return getStreamInternal()->open(builder);
60}
61
62aaudio_result_t AAudioServiceEndpoint::close() {
63    return getStreamInternal()->close();
64}
65
66// TODO, maybe use an interface to reduce exposure
67aaudio_result_t AAudioServiceEndpoint::registerStream(AAudioServiceStreamShared *sharedStream) {
68    std::lock_guard<std::mutex> lock(mLockStreams);
69    mRegisteredStreams.push_back(sharedStream);
70    return AAUDIO_OK;
71}
72
73aaudio_result_t AAudioServiceEndpoint::unregisterStream(AAudioServiceStreamShared *sharedStream) {
74    std::lock_guard<std::mutex> lock(mLockStreams);
75    mRegisteredStreams.erase(std::remove(mRegisteredStreams.begin(), mRegisteredStreams.end(), sharedStream),
76              mRegisteredStreams.end());
77    return AAUDIO_OK;
78}
79
80aaudio_result_t AAudioServiceEndpoint::startStream(AAudioServiceStreamShared *sharedStream) {
81    // TODO use real-time technique to avoid mutex, eg. atomic command FIFO
82    std::lock_guard<std::mutex> lock(mLockStreams);
83    mRunningStreams.push_back(sharedStream);
84    if (mRunningStreams.size() == 1) {
85        startSharingThread_l();
86    }
87    return AAUDIO_OK;
88}
89
90aaudio_result_t AAudioServiceEndpoint::stopStream(AAudioServiceStreamShared *sharedStream) {
91    int numRunningStreams = 0;
92    {
93        std::lock_guard<std::mutex> lock(mLockStreams);
94        mRunningStreams.erase(
95                std::remove(mRunningStreams.begin(), mRunningStreams.end(), sharedStream),
96                mRunningStreams.end());
97        numRunningStreams = mRunningStreams.size();
98    }
99    if (numRunningStreams == 0) {
100        // Don't call this under a lock because the callbackLoop also uses the lock.
101        stopSharingThread();
102    }
103    return AAUDIO_OK;
104}
105
106static void *aaudio_endpoint_thread_proc(void *context) {
107    AAudioServiceEndpoint *endpoint = (AAudioServiceEndpoint *) context;
108    if (endpoint != NULL) {
109        return endpoint->callbackLoop();
110    } else {
111        return NULL;
112    }
113}
114
115aaudio_result_t AAudioServiceEndpoint::startSharingThread_l() {
116    // Launch the callback loop thread.
117    int64_t periodNanos = getStreamInternal()->getFramesPerBurst()
118                          * AAUDIO_NANOS_PER_SECOND
119                          / getSampleRate();
120    mCallbackEnabled.store(true);
121    return getStreamInternal()->createThread(periodNanos, aaudio_endpoint_thread_proc, this);
122}
123
124aaudio_result_t AAudioServiceEndpoint::stopSharingThread() {
125    mCallbackEnabled.store(false);
126    aaudio_result_t result = getStreamInternal()->joinThread(NULL);
127    return result;
128}
129
130void AAudioServiceEndpoint::disconnectRegisteredStreams() {
131    std::lock_guard<std::mutex> lock(mLockStreams);
132    for(AAudioServiceStreamShared *sharedStream : mRunningStreams) {
133        sharedStream->onStop();
134    }
135    mRunningStreams.clear();
136    for(AAudioServiceStreamShared *sharedStream : mRegisteredStreams) {
137        sharedStream->onDisconnect();
138    }
139    mRegisteredStreams.clear();
140}
141