You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

393 lines
15 KiB

/*
* Copyright (C) 2021 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 <android-base/parseint.h>
#include <android-base/strings.h>
#include <grpc++/grpc++.h>
#include <aidl/android/hardware/automotive/audiocontrol/AudioFocusChange.h>
#include <aidl/android/hardware/automotive/audiocontrol/BnAudioControl.h>
#include <aidl/android/hardware/automotive/audiocontrol/DuckingInfo.h>
#include <aidl/android/hardware/automotive/audiocontrol/IFocusListener.h>
#include <android_audio_policy_configuration_V7_0.h>
#include "AudioFocusControl.grpc.pb.h"
#include "AudioFocusControl.pb.h"
#include "libandroid_audio_controller/utils.h"
using std::literals::chrono_literals::operator""s;
namespace xsd {
using namespace ::android::audio::policy::configuration::V7_0;
}
using xsd::AudioUsage;
namespace aidl::android::hardware::automotive::audiocontrol {
class AudioControlServerImpl : public AudioControlServer,
audio_focus_control_proto::AudioFocusControlServer::Service {
public:
explicit AudioControlServerImpl(const std::string& addr);
~AudioControlServerImpl();
close_handle_func_t RegisterFocusListener(std::shared_ptr<IFocusListener> focusListener) override {
std::lock_guard<std::mutex> lock(mFocusListenerMutex);
mFocusListener = focusListener;
return [this, focusListener]() {
std::lock_guard<std::mutex> lock(mFocusListenerMutex);
if (mFocusListener == focusListener) {
mFocusListener = nullptr;
}
};
}
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,
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;
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;
std::shared_ptr<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();
}
}
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();
if (!mGrpcServer) {
LOG(ERROR) << __func__ << ": failed to create the GRPC server, "
<< "please make sure the configuration and permissions are correct.";
return;
}
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 << " " << toString(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,
AudioFocusChange focus_change) {
std::lock_guard<std::mutex> lock(mFocusListenerMutex);
auto listener = mFocusListener;
const auto audio_usage = static_cast<AudioUsage>(usage);
LOG(DEBUG) << __func__
<< ": requesting focus, usage: " << toString(audio_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(toString(audio_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;
const auto audio_usage = static_cast<AudioUsage>(usage);
LOG(DEBUG) << __func__
<< ": abandoning focus, usage: " << toString(audio_usage)
<< ", zone: " << zone;
if (!listener) {
LOG(ERROR) << __func__ << ": audio focus listener has not been registered.";
return;
}
listener->abandonAudioFocus(toString(audio_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()};
}
AudioFocusChange AudioControlServerImpl::AudioFocusSession::GetFocusChange() const {
constexpr auto cast_to_bitfield = [](auto&& focus_change) {
return static_cast<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 aidl::android::hardware::automotive::audiocontrol