| /* |
| * 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 { |
| |
| namespace { |
| template <typename F> struct HWDeviceThunker : |
| ThunkerBase<hw_device_t, GceAudio, F>{}; |
| template <typename F> struct AudioThunker : |
| ThunkerBase<audio_hw_device, GceAudio, F>{}; |
| template <typename F> struct AudioThreadThunker : |
| ThunkerBase<void, GceAudio, F>{}; |
| } |
| |
| 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 = HWDeviceThunker<int()>::call<&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 = |
| AudioThunker<uint32_t()>::call<&GceAudio::GetSupportedDevices>; |
| #endif |
| rval->init_check = AudioThunker<int()>::call<&GceAudio::InitCheck>; |
| |
| rval->set_voice_volume = |
| AudioThunker<int(float)>::call<&GceAudio::SetVoiceVolume>; |
| rval->set_master_volume = |
| AudioThunker<int(float)>::call<&GceAudio::SetMasterVolume>; |
| rval->get_master_volume = |
| AudioThunker<int(float*)>::call<&GceAudio::GetMasterVolume>; |
| |
| #if defined(AUDIO_DEVICE_API_VERSION_2_0) |
| rval->set_master_mute = |
| AudioThunker<int(bool)>::call<&GceAudio::SetMasterMute>; |
| rval->get_master_mute = |
| AudioThunker<int(bool*)>::call<&GceAudio::GetMasterMute>; |
| #endif |
| |
| rval->set_mode = AudioThunker<int(audio_mode_t)>::call<&GceAudio::SetMode>; |
| rval->set_mic_mute = AudioThunker<int(bool)>::call<&GceAudio::SetMicMute>; |
| rval->get_mic_mute = |
| AudioThunker<int(bool*)>::call<&GceAudio::GetMicMute>; |
| |
| rval->set_parameters = |
| AudioThunker<int(const char*)>::call<&GceAudio::SetParameters>; |
| rval->get_parameters = |
| AudioThunker<char*(const char*)>::call<&GceAudio::GetParameters>; |
| |
| rval->get_input_buffer_size = |
| AudioThunker<size_t(const audio_config*)>::call< |
| &GceAudio::GetInputBufferSize>; |
| |
| rval->open_input_stream = |
| AudioThunker<GceAudio::OpenInputStreamHAL_t>::call< |
| &GceAudio::OpenInputStreamCurrentHAL>; |
| rval->close_input_stream = |
| AudioThunker<void(audio_stream_in*)>::call<&GceAudio::CloseInputStream>; |
| |
| rval->open_output_stream = |
| AudioThunker<GceAudio::OpenOutputStreamHAL_t>::call< |
| &GceAudio::OpenOutputStreamCurrentHAL>; |
| rval->close_output_stream = |
| AudioThunker<void(audio_stream_out*)>::call<&GceAudio::CloseOutputStream>; |
| |
| rval->dump = AudioThunker<int(int)>::call<&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; |
| } |
| |
| } |