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