blob: 9386c6a53867d7da4f04b51dcd0f721b7860c2a0 [file] [log] [blame]
/*
* Copyright (C) 2016 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 "guest/hals/audio/audio_hal.h"
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <fcntl.h>
extern "C" {
#include <cutils/str_parms.h>
}
#include "common/libs/auto_resources/auto_resources.h"
#include "common/libs/fs/shared_select.h"
#include "common/libs/threads/cuttlefish_thread.h"
#include "common/libs/threads/thunkers.h"
#include "common/vsoc/lib/circqueue_impl.h"
#include "guest/hals/audio/vsoc_audio.h"
#include "guest/hals/audio/vsoc_audio_input_stream.h"
#include "guest/hals/audio/vsoc_audio_output_stream.h"
#include "guest/libs/platform_support/api_level_fixes.h"
#include "guest/libs/remoter/remoter_framework_pkt.h"
using cvd::LockGuard;
using cvd::Mutex;
namespace cvd {
GceAudio::~GceAudio() { }
int GceAudio::Close() {
D("GceAudio::%s", __FUNCTION__);
{
LockGuard<Mutex> guard(lock_);
for (std::list<GceAudioOutputStream*>::iterator it = output_list_.begin();
it != output_list_.end(); ++it) {
delete *it;
}
for (input_map_t::iterator it = input_map_.begin();
it != input_map_.end(); ++it) {
delete it->second;
}
}
delete this;
return 0;
}
size_t GceAudio::GetInputBufferSize(const audio_config*) const {
return IN_BUFFER_BYTES;
}
uint32_t GceAudio::GetSupportedDevices() const {
return AUDIO_DEVICE_OUT_EARPIECE |
AUDIO_DEVICE_OUT_SPEAKER |
AUDIO_DEVICE_OUT_DEFAULT |
AUDIO_DEVICE_IN_COMMUNICATION |
AUDIO_DEVICE_IN_BUILTIN_MIC |
AUDIO_DEVICE_IN_WIRED_HEADSET |
AUDIO_DEVICE_IN_VOICE_CALL |
AUDIO_DEVICE_IN_DEFAULT;
}
int GceAudio::InitCheck() const {
D("GceAudio::%s", __FUNCTION__);
return 0;
}
int GceAudio::SetMicMute(bool state) {
D("GceAudio::%s", __FUNCTION__);
LockGuard<Mutex> guard(lock_);
mic_muted_ = state;
return 0;
}
int GceAudio::GetMicMute(bool *state) const {
D("GceAudio::%s", __FUNCTION__);
LockGuard<Mutex> guard(lock_);
*state = mic_muted_;
return 0;
}
int GceAudio::OpenInputStream(audio_io_handle_t handle,
audio_devices_t devices,
audio_config *config,
audio_stream_in **stream_in,
audio_input_flags_t /*flags*/,
const char * /*address*/,
audio_source_t /*source*/) {
GceAudioInputStream* new_stream;
int rval = GceAudioInputStream::Open(
this, handle, devices, *config, &new_stream);
uint32_t stream_number;
if (new_stream) {
LockGuard<Mutex> guard(lock_);
stream_number = next_stream_number_++;
input_map_[stream_number] = new_stream;
}
// This should happen after the lock is released, hence the double check
if (new_stream) {
SendStreamUpdate(new_stream->GetStreamDescriptor(
stream_number, gce_audio_message::OPEN_INPUT_STREAM), MSG_DONTWAIT);
}
*stream_in = new_stream;
return rval;
}
void GceAudio::CloseInputStream(audio_stream_in *stream) {
GceAudioInputStream* astream = static_cast<GceAudioInputStream*>(stream);
gce_audio_message descriptor;
{
LockGuard<Mutex> guard(lock_);
// TODO(ghartman): This could be optimized if stream knew it's number.
for (input_map_t::iterator it = input_map_.begin();
it != input_map_.end(); ++it) {
if (it->second == stream) {
descriptor = it->second->GetStreamDescriptor(
it->first, gce_audio_message::CLOSE_INPUT_STREAM);
input_map_.erase(it);
break;
}
}
}
SendStreamUpdate(descriptor, MSG_DONTWAIT);
delete astream;
}
int GceAudio::OpenOutputStream(audio_io_handle_t handle,
audio_devices_t devices,
audio_output_flags_t flags,
audio_config *config,
audio_stream_out **stream_out,
const char * /*address*/) {
GceAudioOutputStream* new_stream;
int rval;
{
LockGuard<Mutex> guard(lock_);
rval = GceAudioOutputStream::Open(
this, handle, devices, flags, config, next_stream_number_++,
&new_stream);
if (new_stream) {
output_list_.push_back(new_stream);
}
}
if (new_stream) {
SendStreamUpdate(new_stream->GetStreamDescriptor(
gce_audio_message::OPEN_OUTPUT_STREAM), MSG_DONTWAIT);
}
*stream_out = new_stream;
return rval;
}
void GceAudio::CloseOutputStream(audio_stream_out *stream) {
GceAudioOutputStream* astream = static_cast<GceAudioOutputStream*>(stream);
gce_audio_message close;
{
LockGuard<Mutex> guard(lock_);
output_list_.remove(astream);
close = astream->GetStreamDescriptor(
gce_audio_message::CLOSE_OUTPUT_STREAM);
}
SendStreamUpdate(close, MSG_DONTWAIT);
delete astream;
}
int GceAudio::Dump(int fd) const {
LockGuard<Mutex> guard(lock_);
VSOC_FDPRINTF(
fd,
"\nadev_dump:\n"
"\tmic_mute: %s\n"
"\tnum_outputs: %zu\n"
"\tnum_inputs: %zu\n\n",
mic_muted_ ? "true": "false",
output_list_.size(), input_map_.size());
for (std::list<GceAudioOutputStream*>::const_iterator it =
output_list_.begin();
it != output_list_.end(); ++it) {
(*it)->common.dump(&(*it)->common, fd);
}
for (input_map_t::const_iterator it = input_map_.begin();
it != input_map_.end(); ++it) {
(*it).second->common.dump(&(*it).second->common, fd);
}
return 0;
}
ssize_t GceAudio::SendMsg(const msghdr& msg, int /* flags */) {
intptr_t res = audio_data_rv_->data()->audio_queue.Writev(
audio_data_rv_,
msg.msg_iov,
msg.msg_iovlen,
true /* non_blocking */);
if (res < 0) {
ALOGV("GceAudio::%s: CircularPacketQueue::Write returned %" PRIiPTR,
__FUNCTION__,
res);
}
return static_cast<ssize_t>(res);
}
ssize_t GceAudio::SendStreamUpdate(
const gce_audio_message& stream_info, int flags) {
msghdr msg;
iovec msg_iov[1];
msg_iov[0].iov_base = const_cast<gce_audio_message*>(&stream_info);
msg_iov[0].iov_len = sizeof(gce_audio_message);
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = msg_iov;
msg.msg_iovlen = arraysize(msg_iov);
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
return SendMsg(msg, flags);
}
int GceAudio::SetVoiceVolume(float volume) {
D("GceAudio::%s: set voice volume %f", __FUNCTION__, volume);
voice_volume_ = volume;
return 0;
}
int GceAudio::SetMasterVolume(float volume) {
D("GceAudio::%s: set master volume %f", __FUNCTION__, volume);
master_volume_ = volume;
return 0;
}
int GceAudio::GetMasterVolume(float* volume) {
D("GceAudio::%s: get master volume %f", __FUNCTION__, master_volume_);
*volume = master_volume_;
return 0;
}
int GceAudio::SetMasterMute(bool muted) {
D("GceAudio::%s: set master muted %d", __FUNCTION__, muted);
master_muted_ = muted;
return 0;
}
int GceAudio::GetMasterMute(bool* muted) {
D("GceAudio::%s: get master muted %d", __FUNCTION__, master_muted_);
*muted = master_muted_;
return 0;
}
int GceAudio::SetMode(audio_mode_t mode) {
D("GceAudio::%s: new mode %d", __FUNCTION__, mode);
mode_ = mode;
return 0;
}
int GceAudio::Open(const hw_module_t* module, const char* name,
hw_device_t** device) {
D("GceAudio::%s", __FUNCTION__);
if (strcmp(name, AUDIO_HARDWARE_INTERFACE)) {
ALOGE("GceAudio::%s: invalid module name %s (expected %s)",
__FUNCTION__, name, AUDIO_HARDWARE_INTERFACE);
return -EINVAL;
}
GceAudio* rval = new GceAudio;
rval->audio_data_rv_ = AudioDataRegionView::GetInstance();
rval->audio_worker_ = rval->audio_data_rv_->StartWorker();
rval->common.tag = HARDWARE_DEVICE_TAG;
rval->common.version = version_;
rval->common.module = const_cast<hw_module_t *>(module);
rval->common.close = cvd::thunk<hw_device_t, &GceAudio::Close>;
#if !defined(AUDIO_DEVICE_API_VERSION_2_0)
// This HAL entry is supported only on AUDIO_DEVICE_API_VERSION_1_0.
// In fact, with version 2.0 the device numbers were orgainized in a
// way that makes the return value nonsense.
// Skipping the assignment is ok: the memset in the constructor already
// put a NULL here.
rval->get_supported_devices =
cvd::thunk<audio_hw_device, &GceAudio::GetSupportedDevices>;
#endif
rval->init_check = cvd::thunk<audio_hw_device, &GceAudio::InitCheck>;
rval->set_voice_volume =
cvd::thunk<audio_hw_device, &GceAudio::SetVoiceVolume>;
rval->set_master_volume =
cvd::thunk<audio_hw_device, &GceAudio::SetMasterVolume>;
rval->get_master_volume =
cvd::thunk<audio_hw_device, &GceAudio::GetMasterVolume>;
#if defined(AUDIO_DEVICE_API_VERSION_2_0)
rval->set_master_mute =
cvd::thunk<audio_hw_device, &GceAudio::SetMasterMute>;
rval->get_master_mute =
cvd::thunk<audio_hw_device, &GceAudio::GetMasterMute>;
#endif
rval->set_mode = cvd::thunk<audio_hw_device, &GceAudio::SetMode>;
rval->set_mic_mute = cvd::thunk<audio_hw_device, &GceAudio::SetMicMute>;
rval->get_mic_mute = cvd::thunk<audio_hw_device, &GceAudio::GetMicMute>;
rval->set_parameters = cvd::thunk<audio_hw_device, &GceAudio::SetParameters>;
rval->get_parameters = cvd::thunk<audio_hw_device, &GceAudio::GetParameters>;
rval->get_input_buffer_size =
cvd::thunk<audio_hw_device, &GceAudio::GetInputBufferSize>;
rval->open_input_stream =
cvd::thunk<audio_hw_device, &GceAudio::OpenInputStreamCurrentHAL>;
rval->close_input_stream =
cvd::thunk<audio_hw_device, &GceAudio::CloseInputStream>;
rval->open_output_stream =
cvd::thunk<audio_hw_device, &GceAudio::OpenOutputStreamCurrentHAL>;
rval->close_output_stream =
cvd::thunk<audio_hw_device, &GceAudio::CloseOutputStream>;
rval->dump = cvd::thunk<audio_hw_device, &GceAudio::Dump>;
*device = &rval->common;
return 0;
}
int GceAudio::SetParameters(const char *kvpairs) {
ALOGE("GceAudio::%s: not implemented", __FUNCTION__);
if (kvpairs) D("GceAudio::%s: kvpairs %s", __FUNCTION__, kvpairs);
return 0;
}
char* GceAudio::GetParameters(const char *keys) const {
ALOGE("GceAudio::%s: not implemented", __FUNCTION__);
if (keys) D("GceAudio::%s: kvpairs %s", __FUNCTION__, keys);
return strdup("");
}
int GceAudio::SetStreamParameters(
struct audio_stream *stream, const char *kv_pairs) {
struct str_parms *parms = str_parms_create_str(kv_pairs);
if (!parms) {
return 0;
}
int sample_rate;
if (str_parms_get_int(parms, AUDIO_PARAMETER_STREAM_SAMPLING_RATE,
&sample_rate) >= 0) {
stream->set_sample_rate(stream, sample_rate);
}
int format;
if (str_parms_get_int(parms, AUDIO_PARAMETER_STREAM_FORMAT,
&format) >= 0) {
stream->set_format(stream, static_cast<audio_format_t>(format));
}
int routing;
if (str_parms_get_int(parms, AUDIO_PARAMETER_STREAM_ROUTING,
&routing) >= 0) {
stream->set_device(stream, static_cast<audio_devices_t>(routing));
}
if (str_parms_get_int(parms, AUDIO_PARAMETER_STREAM_INPUT_SOURCE,
&routing) >= 0) {
stream->set_device(stream, static_cast<audio_devices_t>(routing));
}
str_parms_destroy(parms);
return 0;
}
}