1 /*
2 * Copyright (C) 2020 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 #include "android_audio_controller.h"
18
19 #include <sys/syscall.h>
20 #include <sys/types.h>
21 #include <unistd.h>
22
23 #include <chrono>
24 #include <condition_variable>
25 #include <functional>
26 #include <mutex>
27 #include <set>
28 #include <thread>
29 #include <vector>
30
31 #include <grpc++/grpc++.h>
32 #include "AudioFocusControl.grpc.pb.h"
33 #include "AudioFocusControl.pb.h"
34
35 namespace android::hardware::automotive::audiocontrol::V2_0::implementation {
36
37 using std::literals::chrono_literals::operator""s;
38
getCurrentThreadID()39 static pid_t getCurrentThreadID() {
40 #ifdef gettid
41 return gettid();
42 #elif defined(SYS_gettid)
43 return syscall(SYS_gettid);
44 #else
45 return getpid();
46 #endif
47 }
48
49 class AudioFocusControllerImpl {
50 public:
51 static AudioFocusControllerImpl* GetInstance();
52
53 int SetServerAddr(const std::string& addr);
54
55 aafc_session_id_t AcquireFocus(aafc_audio_focus_request_t&& request);
56
57 void ReleaseFocus(aafc_session_id_t session_id);
58
59 private:
60 AudioFocusControllerImpl();
61
62 ~AudioFocusControllerImpl();
63
64 AudioFocusControllerImpl(const AudioFocusControllerImpl&) = delete;
65
66 AudioFocusControllerImpl& operator=(const AudioFocusControllerImpl&) = delete;
67
68 AudioFocusControllerImpl(AudioFocusControllerImpl&&) = delete;
69
70 struct AudioFocusRequest {
71 aafc_session_id_t session_id;
72 aafc_audio_focus_request_t request;
73 };
74
75 void RequestWorker();
76
77 static aafc_session_id_t GetNewUniqueSessionID();
78
79 // data members
80
81 mutable std::mutex mMutex;
82 std::condition_variable mRequestWorkerCV;
83
84 std::thread mRequestWorkerThread;
85 std::set<aafc_session_id_t> mActiveSessions;
86 std::vector<AudioFocusRequest> mAudioFocusRequests;
87 std::vector<aafc_session_id_t> mSessionsReleaseRequests;
88
89 std::atomic<bool> mShutdownFlag{false};
90
91 std::string mServiceAddr;
92 std::shared_ptr<::grpc::Channel> mGrpcChannel;
93 std::unique_ptr<audio_focus_control_proto::AudioFocusControlServer::Stub> mGrpcStub;
94 };
95
getChannelCredentials()96 static std::shared_ptr<::grpc::ChannelCredentials> getChannelCredentials() {
97 // TODO(chenhaosjtuacm): get secured credentials here
98 return ::grpc::InsecureChannelCredentials();
99 }
100
validateRequest(aafc_audio_focus_request_t * request)101 static void validateRequest(aafc_audio_focus_request_t* request) {
102 if (!request) {
103 std::cerr << "Validate null request is a no-op";
104 return;
105 }
106 if (!request->is_transient && (request->allow_duck || request->is_exclusive)) {
107 std::cerr << "If request is not transient, allow_duck and "
108 "exclusive options will be ignored."
109 << std::endl;
110 } else if (request->allow_duck && request->is_exclusive) {
111 std::cerr << "allow_duck and is_exclusive cannot be set together, "
112 "disabled ducking."
113 << std::endl;
114 request->allow_duck = false;
115 }
116 }
117
AudioFocusControllerImpl()118 AudioFocusControllerImpl::AudioFocusControllerImpl()
119 : mRequestWorkerThread(std::bind(&AudioFocusControllerImpl::RequestWorker, this)) {}
120
~AudioFocusControllerImpl()121 AudioFocusControllerImpl::~AudioFocusControllerImpl() {
122 mShutdownFlag.store(true);
123 if (mRequestWorkerThread.joinable()) {
124 mRequestWorkerThread.join();
125 }
126 }
127
SetServerAddr(const std::string & addr)128 int AudioFocusControllerImpl::SetServerAddr(const std::string& addr) {
129 if (addr.empty()) {
130 std::cerr << "Error: Server address cannot be empty." << std::endl;
131 return -EINVAL;
132 }
133
134 // Although the server settings are guarded by mutex, it is still not safe to
135 // run concurrently with acquiring/releasing focus, or with active sessions,
136 // since the grpc operations in them are not guarded.
137 std::lock_guard<std::mutex> lock(mMutex);
138
139 mServiceAddr = addr;
140 mGrpcChannel = ::grpc::CreateChannel(mServiceAddr, getChannelCredentials());
141 mGrpcStub = audio_focus_control_proto::AudioFocusControlServer::NewStub(mGrpcChannel);
142
143 return 0;
144 }
145
GetInstance()146 AudioFocusControllerImpl* AudioFocusControllerImpl::GetInstance() {
147 static AudioFocusControllerImpl instance;
148 return &instance;
149 }
150
GetNewUniqueSessionID()151 aafc_session_id_t AudioFocusControllerImpl::GetNewUniqueSessionID() {
152 static const auto tid = static_cast<uint64_t>(getCurrentThreadID());
153
154 // 48 bits for timestamp (in nanoseconds), so a session ID
155 // within a thread is guaranteed not to reappear in about 3 days,
156 // which is much longer than any audio session should be.
157 //
158 // 16 bits for tid (65536 threads)
159 constexpr auto use_timestamp_bits = 48;
160 aafc_session_id_t session_id = AAFC_SESSION_ID_INVALID;
161
162 do {
163 uint64_t timestamp = std::chrono::steady_clock::now().time_since_epoch().count();
164 session_id = (tid << use_timestamp_bits) | (timestamp & ((1ull << use_timestamp_bits) - 1));
165 } while (session_id == AAFC_SESSION_ID_INVALID);
166
167 return session_id;
168 }
169
AcquireFocus(aafc_audio_focus_request_t && request)170 aafc_session_id_t AudioFocusControllerImpl::AcquireFocus(aafc_audio_focus_request_t&& request) {
171 validateRequest(&request);
172
173 auto session_id = AAFC_SESSION_ID_INVALID;
174
175 {
176 const std::lock_guard<std::mutex> lock(mMutex);
177
178 if (mServiceAddr.empty()) {
179 std::cerr << "Uninitialized Controller." << std::endl;
180 return session_id;
181 }
182
183 constexpr int max_attempt_times = 5;
184 for (int i = 0; i < max_attempt_times; ++i) {
185 auto session_id_candicate = GetNewUniqueSessionID();
186 auto session_id_insert = mActiveSessions.insert(session_id_candicate);
187 if (session_id_insert.second) {
188 session_id = session_id_candicate;
189 break;
190 }
191 }
192
193 if (session_id == AAFC_SESSION_ID_INVALID) {
194 return session_id;
195 }
196
197 mAudioFocusRequests.emplace_back((AudioFocusRequest){
198 .session_id = session_id,
199 .request = std::move(request),
200 });
201 }
202
203 mRequestWorkerCV.notify_all();
204
205 return session_id;
206 }
207
ReleaseFocus(aafc_session_id_t session_id)208 void AudioFocusControllerImpl::ReleaseFocus(aafc_session_id_t session_id) {
209 if (!session_id) {
210 return;
211 }
212
213 {
214 const std::lock_guard<std::mutex> lock(mMutex);
215 auto session_id_itr = mActiveSessions.find(session_id);
216 if (session_id_itr == mActiveSessions.end()) {
217 std::cerr << "Unknown session ID: " << session_id << std::endl;
218 return;
219 }
220 mActiveSessions.erase(session_id_itr);
221 mSessionsReleaseRequests.emplace_back(session_id);
222 }
223
224 mRequestWorkerCV.notify_all();
225 }
226
RequestWorker()227 void AudioFocusControllerImpl::RequestWorker() {
228 auto nextHeartbeatTime = std::chrono::steady_clock::now();
229 auto heartBeatPeriod = 1s;
230
231 const auto ok_to_proceed = [this, &nextHeartbeatTime]() {
232 auto current_timestamp = std::chrono::steady_clock::now();
233 return !mAudioFocusRequests.empty() || !mSessionsReleaseRequests.empty() ||
234 (current_timestamp > nextHeartbeatTime && !mActiveSessions.empty());
235 };
236 while (!mShutdownFlag.load()) {
237 audio_focus_control_proto::AudioFocusControlMessage audio_requests;
238 {
239 std::unique_lock<std::mutex> lock(mMutex);
240 if (!mRequestWorkerCV.wait_until(lock, nextHeartbeatTime, ok_to_proceed)) {
241 nextHeartbeatTime = std::chrono::steady_clock::now() + heartBeatPeriod;
242 continue;
243 }
244
245 auto current_timestamp = std::chrono::steady_clock::now();
246 if (current_timestamp > nextHeartbeatTime) {
247 nextHeartbeatTime = current_timestamp + 1s;
248 for (auto session_id : mActiveSessions) {
249 audio_requests.add_active_sessions(session_id);
250 }
251 }
252
253 for (auto& request : mAudioFocusRequests) {
254 auto& acquire_request = *audio_requests.add_acquire_requests();
255 acquire_request.set_session_id(request.session_id);
256 acquire_request.set_audio_usage(request.request.audio_usage);
257 acquire_request.set_zone_id(request.request.zone_id);
258 acquire_request.set_allow_duck(request.request.allow_duck);
259 acquire_request.set_is_transient(request.request.is_transient);
260 acquire_request.set_is_exclusive(request.request.is_exclusive);
261 }
262 for (auto session_id : mSessionsReleaseRequests) {
263 audio_requests.add_release_requests(session_id);
264 }
265 mAudioFocusRequests.clear();
266 mSessionsReleaseRequests.clear();
267 }
268
269 constexpr int max_attempt_times = 3;
270 constexpr auto wait_time_between_attempts = 1s;
271 ::grpc::Status grpc_status;
272 for (int current_attempt = 1; current_attempt <= max_attempt_times; ++current_attempt) {
273 ::grpc::ClientContext context;
274 ::google::protobuf::Empty empty_retval;
275 grpc_status = mGrpcStub->AudioRequests(&context, audio_requests, &empty_retval);
276 if (grpc_status.ok()) {
277 break;
278 }
279 std::cerr << "(Attempt " << current_attempt << "/" << max_attempt_times
280 << ") Failed to send audio requests: " << grpc_status.error_message()
281 << std::endl;
282 std::this_thread::sleep_for(wait_time_between_attempts);
283 }
284 if (!grpc_status.ok()) {
285 std::cerr << "Failed to send audio requests. Please check the server address setting "
286 "and make sure the server is running."
287 << std::endl;
288 }
289 }
290 }
291
292 } // namespace android::hardware::automotive::audiocontrol::V2_0::implementation
293
294 using android::hardware::automotive::audiocontrol::V2_0::implementation::AudioFocusControllerImpl;
295
aafc_init_audio_focus_controller(const char * audio_control_server_addr)296 int aafc_init_audio_focus_controller(const char* audio_control_server_addr) {
297 return AudioFocusControllerImpl::GetInstance()->SetServerAddr(audio_control_server_addr);
298 }
299
aafc_acquire_audio_focus(aafc_audio_focus_request_t request)300 aafc_session_id_t aafc_acquire_audio_focus(aafc_audio_focus_request_t request) {
301 return AudioFocusControllerImpl::GetInstance()->AcquireFocus(std::move(request));
302 }
303
aafc_release_audio_focus(aafc_session_id_t session_id)304 void aafc_release_audio_focus(aafc_session_id_t session_id) {
305 return AudioFocusControllerImpl::GetInstance()->ReleaseFocus(session_id);
306 }
307