diff options
author | Hao Chen <chenhaosjtuacm@google.com> | 2020-06-30 21:36:21 -0700 |
---|---|---|
committer | Hao Chen <chenhaosjtuacm@google.com> | 2020-07-09 16:35:41 -0700 |
commit | 4a3edafb5591a1899b029edd9919acb28a0e2339 (patch) | |
tree | a93c0e69ca2481ff8115b3dae43463514a86c518 | |
parent | 66d53b741fbce15bbc539d86e1c94667fb269b8d (diff) | |
download | device_google_trout-4a3edafb5591a1899b029edd9919acb28a0e2339.tar.gz device_google_trout-4a3edafb5591a1899b029edd9919acb28a0e2339.tar.bz2 device_google_trout-4a3edafb5591a1899b029edd9919acb28a0e2339.zip |
Audio Control Server on Android Side Receiving Focus Requests
Test: Build; see pa/1722029
Bug: 141473213
Change-Id: Ic30061041da8fae5ab16d83ea10413c9ccb61de9
6 files changed, 528 insertions, 10 deletions
diff --git a/hal/audiocontrol/2.0/Android.bp b/hal/audiocontrol/2.0/Android.bp index 4f77ef6..e796929 100644 --- a/hal/audiocontrol/2.0/Android.bp +++ b/hal/audiocontrol/2.0/Android.bp @@ -14,9 +14,77 @@ * limitations under the License. */ +cc_defaults { + name: "audio_control_hal_v2_0_default_trout", + defaults: [ + "hidl_defaults", + ], + shared_libs: [ + "android.hardware.automotive.audiocontrol@2.0", + "libbase", + "libhidlbase", + "liblog", + "libutils", + ], +} + +genrule { + name: "AudioFocusControlProtoStub_h", + tools: [ + "aprotoc", + "protoc-gen-grpc-cpp-plugin", + ], + cmd: "$(location aprotoc) -I$$(dirname $(in)) -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)", + srcs: [ + "proto/AudioFocusControl.proto", + ], + out: [ + "AudioFocusControl.pb.h", + "AudioFocusControl.grpc.pb.h", + ], +} + +genrule { + name: "AudioFocusControlProtoStub_cc", + tools: [ + "aprotoc", + "protoc-gen-grpc-cpp-plugin", + ], + cmd: "$(location aprotoc) -I$$(dirname $(in)) -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-cpp-plugin) $(in) --grpc_out=$(genDir) --cpp_out=$(genDir)", + srcs: [ + "proto/AudioFocusControl.proto", + ], + out: [ + "AudioFocusControl.pb.cc", + "AudioFocusControl.grpc.pb.cc", + ], +} + +cc_library { + name: "android.hardware.audiocontrol@2.0-service-audio-control-grpc-server", + vendor: true, + defaults: ["audio_control_hal_v2_0_default_trout"], + srcs: [ + "AudioControlServer.cpp", + ], + generated_headers: [ + "AudioFocusControlProtoStub_h", + ], + generated_sources: [ + "AudioFocusControlProtoStub_cc", + ], + shared_libs: [ + "libgrpc++", + "libprotobuf-cpp-full", + ], + cflags: [ + "-Wno-unused-parameter", + ], +} + cc_binary { name: "android.hardware.audiocontrol@2.0-service.trout", - defaults: ["hidl_defaults"], + defaults: ["audio_control_hal_v2_0_default_trout"], vendor: true, relative_install_path: "hw", srcs: [ @@ -26,14 +94,10 @@ cc_binary { ], init_rc: ["android.hardware.audiocontrol@2.0-service.trout.rc"], shared_libs: [ - "android.hardware.automotive.audiocontrol@2.0", + "android.hardware.audiocontrol@2.0-service-audio-control-grpc-server", "android.hardware.automotive.utils.watchdog", "carwatchdog_aidl_interface-ndk_platform", - "libbase", "libbinder_ndk", - "libhidlbase", - "liblog", - "libutils", ], vintf_fragments: ["manifest_android.hardware.audiocontrol@2.0-service.trout.xml"], } diff --git a/hal/audiocontrol/2.0/AudioControlServer.cpp b/hal/audiocontrol/2.0/AudioControlServer.cpp new file mode 100644 index 0000000..1452549 --- /dev/null +++ b/hal/audiocontrol/2.0/AudioControlServer.cpp @@ -0,0 +1,376 @@ +/* + * Copyright (C) 2020 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. + */ + +#include "AudioControlServer.h" + +#include <deque> +#include <string> +#include <thread> + +#include <android-base/logging.h> +#include <grpc++/grpc++.h> + +#include "AudioFocusControl.grpc.pb.h" +#include "AudioFocusControl.pb.h" +#include "libandroid_audio_controller/utils.h" + +using android::hardware::audio::common::V6_0::AudioUsage; +using std::literals::chrono_literals::operator""s; + +namespace android::hardware::automotive::audiocontrol::V2_0::implementation { + +class AudioControlServerImpl : public AudioControlServer, + audio_focus_control_proto::AudioFocusControlServer::Service { + public: + explicit AudioControlServerImpl(const std::string& addr); + + ~AudioControlServerImpl(); + + close_handle_func_t RegisterFocusListener(const sp<IFocusListener>& focusListener) override; + + grpc::Status AudioRequests(::grpc::ServerContext* context, + const audio_focus_control_proto::AudioFocusControlMessage* message, + ::google::protobuf::Empty*) override; + + void Start() override; + + void Join() override; + + private: + void RequestWorker(); + + void CheckSessionHeartbeats(std::chrono::steady_clock::time_point current_timestamp); + + void HandleHeartbeat(aafc_session_id_t session, + std::chrono::steady_clock::time_point timestamp); + + void HandleAcquiring(audio_focus_control_proto::AudioFocusRequest&& acquire_request, + std::chrono::steady_clock::time_point timestamp); + + void HandleReleasing(aafc_session_id_t release_session); + + void RequestAudioFocus(aafc_audio_usage_t usage, aafc_zone_id_t zone, + hidl_bitfield<AudioFocusChange> focus_change); + + void AbandonAudioFocus(aafc_audio_usage_t usage, aafc_zone_id_t zone); + + using grpc_request_t = audio_focus_control_proto::AudioFocusControlMessage; + using focus_listener_request_key_t = std::pair<aafc_audio_usage_t, aafc_zone_id_t>; + + struct AudioFocusSession { + audio_focus_control_proto::AudioFocusRequest mRequest; + std::chrono::steady_clock::time_point mLastHeartbeat; + + focus_listener_request_key_t GetRequestKey() const; + hidl_bitfield<AudioFocusChange> GetFocusChange() const; + }; + + using session_pool_t = std::map<aafc_session_id_t, AudioFocusSession>; + + // data members + + std::string mServiceAddr; + std::unique_ptr<::grpc::Server> mGrpcServer; + sp<IFocusListener> mFocusListener{nullptr}; + + // grpc request queue + std::deque<grpc_request_t> mRequestQueue; + + // On the focus listener side, the usage/zone pair is used as the key, + // and acquiring focus multiple times on the same usage and zone will + // be treated as once, so we have to maintain the "sessions" and ref count + // by ourselves here. + // + // Active audio focus sessions from grpc clients + session_pool_t mSessionPool; + + // ref counts of usage/zone pair + std::map<focus_listener_request_key_t, unsigned> mAudioFocusCount; + + std::atomic<bool> mShutdownFlag{false}; + std::thread mRequestWorker; + + mutable std::mutex mFocusListenerMutex; + mutable std::mutex mRequestQueueMutex; + + std::condition_variable mRequestQueueCV; +}; + +static std::shared_ptr<::grpc::ServerCredentials> getServerCredentials() { + // TODO(chenhaosjtuacm): get secured credentials here + return ::grpc::InsecureServerCredentials(); +} + +AudioControlServerImpl::AudioControlServerImpl(const std::string& addr) : mServiceAddr(addr) {} + +AudioControlServerImpl::~AudioControlServerImpl() { + mShutdownFlag.store(true); + if (mRequestWorker.joinable()) { + mRequestWorker.join(); + } +} + +AudioControlServer::close_handle_func_t AudioControlServerImpl::RegisterFocusListener( + const sp<IFocusListener>& focusListener) { + std::lock_guard<std::mutex> lock(mFocusListenerMutex); + mFocusListener = focusListener; + + return [this, focusListener]() { + std::lock_guard<std::mutex> lock(mFocusListenerMutex); + if (mFocusListener == focusListener) { + mFocusListener = nullptr; + } + }; +} + +void AudioControlServerImpl::Start() { + if (mGrpcServer) { + LOG(WARNING) << __func__ << ": GRPC Server is running."; + return; + } + + ::grpc::ServerBuilder builder; + builder.RegisterService(this); + builder.AddListeningPort(mServiceAddr, getServerCredentials()); + + mGrpcServer = builder.BuildAndStart(); + + CHECK(mGrpcServer) << __func__ << ": failed to create the GRPC server, " + << "please make sure the configuration and permissions are correct."; + + mRequestWorker = std::thread(std::bind(&AudioControlServerImpl::RequestWorker, this)); +} + +void AudioControlServerImpl::Join() { + if (!mGrpcServer) { + LOG(WARNING) << __func__ << ": GRPC Server is not running."; + return; + } + mGrpcServer->Wait(); +} + +grpc::Status AudioControlServerImpl::AudioRequests( + ::grpc::ServerContext* context, + const audio_focus_control_proto::AudioFocusControlMessage* message, + ::google::protobuf::Empty*) { + { + std::lock_guard<std::mutex> lock(mRequestQueueMutex); + mRequestQueue.emplace_back(*message); + } + mRequestQueueCV.notify_all(); + return ::grpc::Status::OK; +} + +void AudioControlServerImpl::RequestWorker() { + constexpr auto kCheckHeartbeatFreq = 1s; + auto nextHeartbeatCheckTime = std::chrono::steady_clock::now(); + while (!mShutdownFlag.load()) { + std::optional<grpc_request_t> message; + { + std::unique_lock<std::mutex> lock(mRequestQueueMutex); + if (mRequestQueue.empty()) { + mRequestQueueCV.wait_until(lock, nextHeartbeatCheckTime, + [this]() { return !mRequestQueue.empty(); }); + } + if (!mRequestQueue.empty()) { + message = std::move(*mRequestQueue.begin()); + mRequestQueue.pop_front(); + } + } + + auto current_timestamp = std::chrono::steady_clock::now(); + if (message) { + for (auto&& active_session : message->active_sessions()) { + HandleHeartbeat(active_session, current_timestamp); + } + + for (auto&& acquire_request : *message->mutable_acquire_requests()) { + HandleAcquiring(std::move(acquire_request), current_timestamp); + } + + for (auto&& release_session : message->release_requests()) { + HandleReleasing(release_session); + } + } + if (current_timestamp >= nextHeartbeatCheckTime) { + nextHeartbeatCheckTime += kCheckHeartbeatFreq; + CheckSessionHeartbeats(current_timestamp); + } + } +} + +void AudioControlServerImpl::HandleHeartbeat(aafc_session_id_t session, + std::chrono::steady_clock::time_point timestamp) { + auto session_search = mSessionPool.find(session); + if (session_search == mSessionPool.end()) { + LOG(ERROR) << __func__ << ": unknown session ID: " << session; + return; + } + auto& session_info = session_search->second; + session_info.mLastHeartbeat = timestamp; +} + +void AudioControlServerImpl::HandleAcquiring( + audio_focus_control_proto::AudioFocusRequest&& acquire_request, + std::chrono::steady_clock::time_point timestamp) { + const auto session_id = acquire_request.session_id(); + const auto session_emplace = mSessionPool.emplace( + session_id, AudioFocusSession{std::move(acquire_request), timestamp}); + if (session_emplace.second == false) { + LOG(ERROR) << __func__ << ": duplicate session ID: " << session_id; + return; + } + + const auto& session_emplace_iter = session_emplace.first; + const auto& session_info = session_emplace_iter->second; + const auto request_key = session_info.GetRequestKey(); + const auto focus_change = session_info.GetFocusChange(); + const auto ref_count_search = mAudioFocusCount.find(request_key); + const auto& [audio_usage, zone_id] = request_key; + LOG(DEBUG) << __func__ << ": acquiring: " << toString(static_cast<AudioUsage>(audio_usage)) + << " " << zone_id << " " << focus_change; + + const bool not_found = ref_count_search == mAudioFocusCount.end(); + const bool count_zero = !not_found && ref_count_search->second == 0; + + if (count_zero) { + LOG(WARNING) << __func__ << ": unexcepted unremoved zero ref count, treating as missing."; + } + + if (not_found || count_zero) { + mAudioFocusCount[request_key] = 1; + RequestAudioFocus(audio_usage, zone_id, focus_change); + } else { + ++ref_count_search->second; + } +} + +void AudioControlServerImpl::HandleReleasing(aafc_session_id_t release_session) { + const auto session_search = mSessionPool.find(release_session); + if (session_search == mSessionPool.end()) { + LOG(ERROR) << __func__ << ": unknown session ID: " << release_session; + return; + } + const auto& session_info = session_search->second; + const auto request_key = session_info.GetRequestKey(); + const auto& [audio_usage, zone_id] = request_key; + mSessionPool.erase(session_search); + LOG(DEBUG) << __func__ << ": releasing: " << toString(static_cast<AudioUsage>(audio_usage)) + << " " << zone_id; + + const auto ref_count_search = mAudioFocusCount.find(request_key); + if (ref_count_search == mAudioFocusCount.end()) { + LOG(ERROR) << __func__ << ": unknown request, audio usage: " + << toString(static_cast<AudioUsage>(audio_usage)) << ", zone: " << zone_id; + return; + } + auto& request_ref_count = ref_count_search->second; + if (--request_ref_count == 0) { + AbandonAudioFocus(audio_usage, zone_id); + mAudioFocusCount.erase(ref_count_search); + } +} + +void AudioControlServerImpl::RequestAudioFocus(aafc_audio_usage_t usage, aafc_zone_id_t zone, + hidl_bitfield<AudioFocusChange> focus_change) { + std::lock_guard<std::mutex> lock(mFocusListenerMutex); + auto listener = mFocusListener; + LOG(DEBUG) << __func__ + << ": requesting focus, usage: " << toString(static_cast<AudioUsage>(usage)) + << ", zone: " << zone + << ", focus change: " << toString(static_cast<AudioFocusChange>(focus_change)); + if (!listener) { + LOG(ERROR) << __func__ << ": audio focus listener has not been registered."; + return; + } + listener->requestAudioFocus(usage, zone, focus_change); +} + +void AudioControlServerImpl::AbandonAudioFocus(aafc_audio_usage_t usage, aafc_zone_id_t zone) { + std::lock_guard<std::mutex> lock(mFocusListenerMutex); + auto listener = mFocusListener; + LOG(DEBUG) << __func__ + << ": abandoning focus, usage: " << toString(static_cast<AudioUsage>(usage)) + << ", zone: " << zone; + if (!listener) { + LOG(ERROR) << __func__ << ": audio focus listener has not been registered."; + return; + } + listener->abandonAudioFocus(usage, zone); +} + +void AudioControlServerImpl::CheckSessionHeartbeats( + std::chrono::steady_clock::time_point current_timestamp) { + constexpr auto kSessionHeartbeatTimeout = 5s; + const auto timestamp_to_sec = [](auto&& timestamp) { + return std::chrono::duration_cast<std::chrono::duration<double>>( + timestamp.time_since_epoch()) + .count(); + }; + + constexpr size_t max_timeout_session_num = 256; + std::array<aafc_session_id_t, max_timeout_session_num> timeout_sessions; + size_t num_of_timeout_sessions = 0; + + for (auto&& current_session : mSessionPool) { + const auto& current_session_id = current_session.first; + const auto& current_session_info = current_session.second; + if (current_session_info.mLastHeartbeat + kSessionHeartbeatTimeout < current_timestamp) { + if (num_of_timeout_sessions >= max_timeout_session_num) { + LOG(ERROR) << __func__ << ": timeout session number exceeds the limit: " + << max_timeout_session_num; + break; + } + LOG(WARNING) << __func__ << ": timeout on session " << current_session_id + << ", last heartbeat at " + << timestamp_to_sec(current_session_info.mLastHeartbeat) + << ", current timestamp is " << timestamp_to_sec(current_timestamp) + << ", timeout limit " << kSessionHeartbeatTimeout.count() << "s"; + timeout_sessions[num_of_timeout_sessions++] = current_session_id; + } + } + + for (int i = 0; i < num_of_timeout_sessions; ++i) { + HandleReleasing(timeout_sessions[i]); + } +} + +AudioControlServerImpl::focus_listener_request_key_t +AudioControlServerImpl::AudioFocusSession::GetRequestKey() const { + return {mRequest.audio_usage(), mRequest.zone_id()}; +} + +hidl_bitfield<AudioFocusChange> AudioControlServerImpl::AudioFocusSession::GetFocusChange() const { + constexpr auto cast_to_bitfield = [](auto&& focus_change) { + return static_cast<hidl_bitfield<AudioFocusChange>>(focus_change); + }; + if (!mRequest.is_transient()) { + return cast_to_bitfield(AudioFocusChange::GAIN); + } + if (mRequest.is_exclusive()) { + return cast_to_bitfield(AudioFocusChange::GAIN_TRANSIENT_EXCLUSIVE); + } + if (mRequest.allow_duck()) { + return cast_to_bitfield(AudioFocusChange::GAIN_TRANSIENT_MAY_DUCK); + } + return cast_to_bitfield(AudioFocusChange::GAIN_TRANSIENT); +} + +std::unique_ptr<AudioControlServer> MakeAudioControlServer(const std::string& addr) { + return std::make_unique<AudioControlServerImpl>(addr); +} + +} // namespace android::hardware::automotive::audiocontrol::V2_0::implementation diff --git a/hal/audiocontrol/2.0/AudioControlServer.h b/hal/audiocontrol/2.0/AudioControlServer.h new file mode 100644 index 0000000..b0cc60b --- /dev/null +++ b/hal/audiocontrol/2.0/AudioControlServer.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include <functional> +#include <memory> +#include <string> + +#include <android/hardware/automotive/audiocontrol/2.0/IFocusListener.h> + +namespace android::hardware::automotive::audiocontrol::V2_0::implementation { + +class AudioControlServer { + public: + using close_handle_func_t = std::function<void()>; + + virtual ~AudioControlServer() = default; + + virtual close_handle_func_t RegisterFocusListener(const sp<IFocusListener>& focusListener) = 0; + + virtual void Start() = 0; + + virtual void Join() = 0; +}; + +std::unique_ptr<AudioControlServer> MakeAudioControlServer(const std::string& addr); + +} // namespace android::hardware::automotive::audiocontrol::V2_0::implementation diff --git a/hal/audiocontrol/2.0/libandroid_audio_controller/android_audio_controller.cpp b/hal/audiocontrol/2.0/libandroid_audio_controller/android_audio_controller.cpp index d68a3a6..ecfc8cc 100644 --- a/hal/audiocontrol/2.0/libandroid_audio_controller/android_audio_controller.cpp +++ b/hal/audiocontrol/2.0/libandroid_audio_controller/android_audio_controller.cpp @@ -16,7 +16,9 @@ #include "android_audio_controller.h" -#include <cutils/threads.h> +#include <sys/syscall.h> +#include <sys/types.h> +#include <unistd.h> #include <chrono> #include <condition_variable> @@ -34,6 +36,16 @@ namespace android::hardware::automotive::audiocontrol::V2_0::implementation { using std::literals::chrono_literals::operator""s; +static pid_t getCurrentThreadID() { +#ifdef gettid + return gettid(); +#elif defined(SYS_gettid) + return syscall(SYS_gettid); +#else + return getpid(); +#endif +} + class AudioFocusControllerImpl { public: static AudioFocusControllerImpl* GetInstance(); @@ -86,6 +98,23 @@ static std::shared_ptr<::grpc::ChannelCredentials> getChannelCredentials() { return ::grpc::InsecureChannelCredentials(); } +static void validateRequest(aafc_audio_focus_request_t* request) { + if (!request) { + std::cerr << "Validate null request is a no-op"; + return; + } + if (!request->is_transient && (request->allow_duck || request->is_exclusive)) { + std::cerr << "If request is not transient, allow_duck and " + "exclusive options will be ignored." + << std::endl; + } else if (request->allow_duck && request->is_exclusive) { + std::cerr << "allow_duck and is_exclusive cannot be set together, " + "disabled ducking." + << std::endl; + request->allow_duck = false; + } +} + AudioFocusControllerImpl::AudioFocusControllerImpl() : mRequestWorkerThread(std::bind(&AudioFocusControllerImpl::RequestWorker, this)) {} @@ -120,7 +149,7 @@ AudioFocusControllerImpl* AudioFocusControllerImpl::GetInstance() { } aafc_session_id_t AudioFocusControllerImpl::GetNewUniqueSessionID() { - static const auto tid = static_cast<uint64_t>(gettid()); + static const auto tid = static_cast<uint64_t>(getCurrentThreadID()); // 48 bits for timestamp (in nanoseconds), so a session ID // within a thread is guaranteed not to reappear in about 3 days, @@ -139,6 +168,8 @@ aafc_session_id_t AudioFocusControllerImpl::GetNewUniqueSessionID() { } aafc_session_id_t AudioFocusControllerImpl::AcquireFocus(aafc_audio_focus_request_t&& request) { + validateRequest(&request); + auto session_id = AAFC_SESSION_ID_INVALID; { @@ -226,6 +257,7 @@ void AudioFocusControllerImpl::RequestWorker() { acquire_request.set_zone_id(request.request.zone_id); acquire_request.set_allow_duck(request.request.allow_duck); acquire_request.set_is_transient(request.request.is_transient); + acquire_request.set_is_exclusive(request.request.is_exclusive); } for (auto session_id : mSessionsReleaseRequests) { audio_requests.add_release_requests(session_id); diff --git a/hal/audiocontrol/2.0/libandroid_audio_controller/android_audio_controller.h b/hal/audiocontrol/2.0/libandroid_audio_controller/android_audio_controller.h index ee44317..2d5311d 100644 --- a/hal/audiocontrol/2.0/libandroid_audio_controller/android_audio_controller.h +++ b/hal/audiocontrol/2.0/libandroid_audio_controller/android_audio_controller.h @@ -16,6 +16,8 @@ #pragma once +#include <stdbool.h> + #include "utils.h" #ifdef __cplusplus @@ -25,8 +27,9 @@ extern "C" { typedef struct { aafc_audio_usage_t audio_usage; aafc_zone_id_t zone_id; - int allow_duck; - int is_transient; + bool allow_duck; + bool is_transient; + bool is_exclusive; } aafc_audio_focus_request_t; /* Initialize the audio focus controller before using, return 0 when success. diff --git a/hal/audiocontrol/2.0/proto/AudioFocusControl.proto b/hal/audiocontrol/2.0/proto/AudioFocusControl.proto index 1887cae..04db33d 100644 --- a/hal/audiocontrol/2.0/proto/AudioFocusControl.proto +++ b/hal/audiocontrol/2.0/proto/AudioFocusControl.proto @@ -26,6 +26,7 @@ message AudioFocusRequest { int32 zone_id = 3; bool allow_duck = 4; bool is_transient = 5; + bool is_exclusive = 6; } message AudioFocusControlMessage { |