diff options
author | android-build-prod (mdb) <android-build-team-robot@google.com> | 2020-04-17 21:30:55 +0000 |
---|---|---|
committer | android-build-prod (mdb) <android-build-team-robot@google.com> | 2020-04-17 21:30:55 +0000 |
commit | 5ea136308bbbc1636d92e5677464e9197b108453 (patch) | |
tree | f1eb71b87c2bc66eec663d9d615ad9ae7b33c171 | |
parent | 74ef2a94a30cca527355763b5183139213527a8a (diff) | |
parent | b822c952205e0aa242efe649b9f84372a6bf8537 (diff) |
Snap for 6405781 from b822c952205e0aa242efe649b9f84372a6bf8537 to sdk-releaseplatform-tools-30.0.0
Change-Id: I9f3afd16902b61af038d27c046a417901724266c
-rw-r--r-- | audio/Android.mk | 4 | ||||
-rw-r--r-- | audio/audio_hw.c | 1831 | ||||
-rw-r--r-- | db845c/BoardConfig.mk | 1 | ||||
-rw-r--r-- | device-common.mk | 3 | ||||
-rw-r--r-- | manifest.xml | 18 | ||||
-rw-r--r-- | sepolicy/te_macros | 1 |
6 files changed, 1512 insertions, 346 deletions
diff --git a/audio/Android.mk b/audio/Android.mk index afa804f..228ccf2 100644 --- a/audio/Android.mk +++ b/audio/Android.mk @@ -24,8 +24,7 @@ include $(CLEAR_VARS) LOCAL_HEADER_LIBRARIES += libhardware_headers LOCAL_MODULE := audio.primary.$(TARGET_BOARD_PLATFORM) -LOCAL_MODULE_PATH_32 := $(TARGET_OUT_VENDOR)/lib/hw -LOCAL_MODULE_PATH_64 := $(TARGET_OUT_VENDOR)/lib64/hw +LOCAL_MODULE_RELATIVE_PATH := hw LOCAL_VENDOR_MODULE := true LOCAL_SRC_FILES := audio_hw.c @@ -38,4 +37,3 @@ LOCAL_C_INCLUDES += \ system/media/audio_effects/include include $(BUILD_SHARED_LIBRARY) - diff --git a/audio/audio_hw.c b/audio/audio_hw.c index d601ea8..805e2cd 100644 --- a/audio/audio_hw.c +++ b/audio/audio_hw.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2012 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. @@ -12,352 +12,812 @@ * 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. + * + * Copied as it is from device/google/cuttlefish/guest/hals/audio/audio_hw.c + * and fixed couple of typos pointed out by Lint during review. */ -#define LOG_TAG "audio_hw_dragonboard" -//#define LOG_NDEBUG 0 +#define LOG_TAG "audio_hw_generic" +#include <assert.h> #include <errno.h> -#include <malloc.h> +#include <inttypes.h> #include <pthread.h> #include <stdint.h> -#include <sys/time.h> #include <stdlib.h> +#include <sys/time.h> +#include <dlfcn.h> +#include <fcntl.h> #include <unistd.h> #include <log/log.h> +#include <cutils/list.h> #include <cutils/str_parms.h> -#include <cutils/properties.h> #include <hardware/hardware.h> #include <system/audio.h> #include <hardware/audio.h> - -#include <sound/asound.h> #include <tinyalsa/asoundlib.h> -#include <audio_utils/resampler.h> -#include <audio_utils/echo_reference.h> -#include <hardware/audio_effect.h> -#include <hardware/audio_alsaops.h> -#include <audio_effects/effect_aec.h> - - -#define CARD_OUT 0 -#define PORT_CODEC 0 -/* Minimum granularity - Arbitrary but small value */ -#define CODEC_BASE_FRAME_COUNT 32 - -/* number of base blocks in a short period (low latency) */ -#define PERIOD_MULTIPLIER 32 /* 21 ms */ -/* number of frames per short period (low latency) */ -#define PERIOD_SIZE (CODEC_BASE_FRAME_COUNT * PERIOD_MULTIPLIER) -/* number of pseudo periods for low latency playback */ -#define PLAYBACK_PERIOD_COUNT 2 -#define PLAYBACK_PERIOD_START_THRESHOLD 2 -#define CODEC_SAMPLING_RATE 48000 -#define CHANNEL_STEREO 2 -#define MIN_WRITE_SLEEP_US 5000 - -struct stub_stream_in { - struct audio_stream_in stream; -}; -struct alsa_audio_device { - struct audio_hw_device hw_device; +#define PCM_CARD 0 +#define PCM_DEVICE 0 - pthread_mutex_t lock; /* see note below on mutex acquisition order */ - int devices; - struct alsa_stream_in *active_input; - struct alsa_stream_out *active_output; - bool mic_mute; -}; -struct alsa_stream_out { - struct audio_stream_out stream; - - pthread_mutex_t lock; /* see note below on mutex acquisition order */ - struct pcm_config config; - struct pcm *pcm; - bool unavailable; - int standby; - struct alsa_audio_device *dev; - int write_threshold; - unsigned int written; +#define OUT_PERIOD_MS 15 +#define OUT_PERIOD_COUNT 4 + +#define IN_PERIOD_MS 15 +#define IN_PERIOD_COUNT 4 + +struct generic_audio_device { + struct audio_hw_device device; // Constant after init + pthread_mutex_t lock; + bool mic_mute; // Protected by this->lock + struct mixer* mixer; // Protected by this->lock + struct listnode out_streams; // Record for output streams, protected by this->lock + struct listnode in_streams; // Record for input streams, protected by this->lock + audio_patch_handle_t next_patch_handle; // Protected by this->lock }; +/* If not NULL, this is a pointer to the fallback module. + * This really is the original goldfish audio device /dev/eac which we will use + * if no alsa devices are detected. + */ +static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state); +static int adev_get_microphones(const audio_hw_device_t *dev, + struct audio_microphone_characteristic_t *mic_array, + size_t *mic_count); + + +typedef struct audio_vbuffer { + pthread_mutex_t lock; + uint8_t * data; + size_t frame_size; + size_t frame_count; + size_t head; + size_t tail; + size_t live; +} audio_vbuffer_t; + +static int audio_vbuffer_init (audio_vbuffer_t * audio_vbuffer, size_t frame_count, + size_t frame_size) { + if (!audio_vbuffer) { + return -EINVAL; + } + audio_vbuffer->frame_size = frame_size; + audio_vbuffer->frame_count = frame_count; + size_t bytes = frame_count * frame_size; + audio_vbuffer->data = calloc(bytes, 1); + if (!audio_vbuffer->data) { + return -ENOMEM; + } + audio_vbuffer->head = 0; + audio_vbuffer->tail = 0; + audio_vbuffer->live = 0; + pthread_mutex_init (&audio_vbuffer->lock, (const pthread_mutexattr_t *) NULL); + return 0; +} -/* must be called with hw device and output stream mutexes locked */ -static int start_output_stream(struct alsa_stream_out *out) -{ - struct alsa_audio_device *adev = out->dev; +static int audio_vbuffer_destroy (audio_vbuffer_t * audio_vbuffer) { + if (!audio_vbuffer) { + return -EINVAL; + } + free(audio_vbuffer->data); + pthread_mutex_destroy(&audio_vbuffer->lock); + return 0; +} - if (out->unavailable) - return -ENODEV; +static int audio_vbuffer_live (audio_vbuffer_t * audio_vbuffer) { + if (!audio_vbuffer) { + return -EINVAL; + } + pthread_mutex_lock (&audio_vbuffer->lock); + int live = audio_vbuffer->live; + pthread_mutex_unlock (&audio_vbuffer->lock); + return live; +} - /* default to low power: will be corrected in out_write if necessary before first write to - * tinyalsa. - */ - out->write_threshold = PLAYBACK_PERIOD_COUNT * PERIOD_SIZE; - out->config.start_threshold = PLAYBACK_PERIOD_START_THRESHOLD * PERIOD_SIZE; - out->config.avail_min = PERIOD_SIZE; +#define MIN(a,b) (((a)<(b))?(a):(b)) +static size_t audio_vbuffer_write (audio_vbuffer_t * audio_vbuffer, const void * buffer, size_t frame_count) { + size_t frames_written = 0; + pthread_mutex_lock (&audio_vbuffer->lock); + + while (frame_count != 0) { + int frames = 0; + if (audio_vbuffer->live == 0 || audio_vbuffer->head > audio_vbuffer->tail) { + frames = MIN(frame_count, audio_vbuffer->frame_count - audio_vbuffer->head); + } else if (audio_vbuffer->head < audio_vbuffer->tail) { + frames = MIN(frame_count, audio_vbuffer->tail - (audio_vbuffer->head)); + } else { + // Full + break; + } + memcpy(&audio_vbuffer->data[audio_vbuffer->head*audio_vbuffer->frame_size], + &((uint8_t*)buffer)[frames_written*audio_vbuffer->frame_size], + frames*audio_vbuffer->frame_size); + audio_vbuffer->live += frames; + frames_written += frames; + frame_count -= frames; + audio_vbuffer->head = (audio_vbuffer->head + frames) % audio_vbuffer->frame_count; + } - out->pcm = pcm_open(CARD_OUT, PORT_CODEC, PCM_OUT | PCM_MMAP | PCM_NOIRQ | PCM_MONOTONIC, &out->config); + pthread_mutex_unlock (&audio_vbuffer->lock); + return frames_written; +} - if (!pcm_is_ready(out->pcm)) { - ALOGE("cannot open pcm_out driver: %s", pcm_get_error(out->pcm)); - pcm_close(out->pcm); - adev->active_output = NULL; - out->unavailable = true; - return -ENODEV; +static size_t audio_vbuffer_read (audio_vbuffer_t * audio_vbuffer, void * buffer, size_t frame_count) { + size_t frames_read = 0; + pthread_mutex_lock (&audio_vbuffer->lock); + + while (frame_count != 0) { + int frames = 0; + if (audio_vbuffer->live == audio_vbuffer->frame_count || + audio_vbuffer->tail > audio_vbuffer->head) { + frames = MIN(frame_count, audio_vbuffer->frame_count - audio_vbuffer->tail); + } else if (audio_vbuffer->tail < audio_vbuffer->head) { + frames = MIN(frame_count, audio_vbuffer->head - audio_vbuffer->tail); + } else { + break; + } + memcpy(&((uint8_t*)buffer)[frames_read*audio_vbuffer->frame_size], + &audio_vbuffer->data[audio_vbuffer->tail*audio_vbuffer->frame_size], + frames*audio_vbuffer->frame_size); + audio_vbuffer->live -= frames; + frames_read += frames; + frame_count -= frames; + audio_vbuffer->tail = (audio_vbuffer->tail + frames) % audio_vbuffer->frame_count; } - adev->active_output = out; - return 0; + pthread_mutex_unlock (&audio_vbuffer->lock); + return frames_read; } +struct generic_stream_out { + struct audio_stream_out stream; // Constant after init + pthread_mutex_t lock; + struct generic_audio_device *dev; // Constant after init + uint32_t num_devices; // Protected by this->lock + audio_devices_t devices[AUDIO_PATCH_PORTS_MAX]; // Protected by this->lock + struct audio_config req_config; // Constant after init + struct pcm_config pcm_config; // Constant after init + audio_vbuffer_t buffer; // Constant after init + + // Time & Position Keeping + bool standby; // Protected by this->lock + uint64_t underrun_position; // Protected by this->lock + struct timespec underrun_time; // Protected by this->lock + uint64_t last_write_time_us; // Protected by this->lock + uint64_t frames_total_buffered; // Protected by this->lock + uint64_t frames_written; // Protected by this->lock + uint64_t frames_rendered; // Protected by this->lock + + // Worker + pthread_t worker_thread; // Constant after init + pthread_cond_t worker_wake; // Protected by this->lock + bool worker_standby; // Protected by this->lock + bool worker_exit; // Protected by this->lock + + audio_io_handle_t handle; // Constant after init + audio_patch_handle_t patch_handle; // Protected by this->dev->lock + + struct listnode stream_node; // Protected by this->dev->lock +}; + +struct generic_stream_in { + struct audio_stream_in stream; // Constant after init + pthread_mutex_t lock; + struct generic_audio_device *dev; // Constant after init + audio_devices_t device; // Protected by this->lock + struct audio_config req_config; // Constant after init + struct pcm *pcm; // Protected by this->lock + struct pcm_config pcm_config; // Constant after init + int16_t *stereo_to_mono_buf; // Protected by this->lock + size_t stereo_to_mono_buf_size; // Protected by this->lock + audio_vbuffer_t buffer; // Protected by this->lock + + // Time & Position Keeping + bool standby; // Protected by this->lock + int64_t standby_position; // Protected by this->lock + struct timespec standby_exit_time;// Protected by this->lock + int64_t standby_frames_read; // Protected by this->lock + + // Worker + pthread_t worker_thread; // Constant after init + pthread_cond_t worker_wake; // Protected by this->lock + bool worker_standby; // Protected by this->lock + bool worker_exit; // Protected by this->lock + + audio_io_handle_t handle; // Constant after init + audio_patch_handle_t patch_handle; // Protected by this->dev->lock + + struct listnode stream_node; // Protected by this->dev->lock +}; + +static struct pcm_config pcm_config_out = { + .channels = 2, + .rate = 0, + .period_size = 0, + .period_count = OUT_PERIOD_COUNT, + .format = PCM_FORMAT_S16_LE, + .start_threshold = 0, +}; + +static struct pcm_config pcm_config_in = { + .channels = 2, + .rate = 0, + .period_size = 0, + .period_count = IN_PERIOD_COUNT, + .format = PCM_FORMAT_S16_LE, + .start_threshold = 0, + .stop_threshold = INT_MAX, +}; + +static pthread_mutex_t adev_init_lock = PTHREAD_MUTEX_INITIALIZER; +static unsigned int audio_device_ref_count = 0; + static uint32_t out_get_sample_rate(const struct audio_stream *stream) { - struct alsa_stream_out *out = (struct alsa_stream_out *)stream; - return out->config.rate; + struct generic_stream_out *out = (struct generic_stream_out *)stream; + return out->req_config.sample_rate; } static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate) { - ALOGV("out_set_sample_rate: %d", 0); return -ENOSYS; } static size_t out_get_buffer_size(const struct audio_stream *stream) { - ALOGV("out_get_buffer_size: %d", 4096); + struct generic_stream_out *out = (struct generic_stream_out *)stream; + int size = out->pcm_config.period_size * + audio_stream_out_frame_size(&out->stream); - /* return the closest majoring multiple of 16 frames, as - * audioflinger expects audio buffers to be a multiple of 16 frames */ - size_t size = PERIOD_SIZE; - size = ((size + 15) / 16) * 16; - return size * audio_stream_out_frame_size((struct audio_stream_out *)stream); + return size; } static audio_channel_mask_t out_get_channels(const struct audio_stream *stream) { - ALOGV("out_get_channels"); - struct alsa_stream_out *out = (struct alsa_stream_out *)stream; - return audio_channel_out_mask_from_count(out->config.channels); + struct generic_stream_out *out = (struct generic_stream_out *)stream; + return out->req_config.channel_mask; } static audio_format_t out_get_format(const struct audio_stream *stream) { - ALOGV("out_get_format"); - struct alsa_stream_out *out = (struct alsa_stream_out *)stream; - return audio_format_from_pcm_format(out->config.format); + struct generic_stream_out *out = (struct generic_stream_out *)stream; + + return out->req_config.format; } static int out_set_format(struct audio_stream *stream, audio_format_t format) { - ALOGV("out_set_format: %d",format); return -ENOSYS; } -static int do_output_standby(struct alsa_stream_out *out) -{ - struct alsa_audio_device *adev = out->dev; - - if (!out->standby) { - pcm_close(out->pcm); - out->pcm = NULL; - adev->active_output = NULL; - out->standby = 1; - } - return 0; -} - -static int out_standby(struct audio_stream *stream) +static int out_dump(const struct audio_stream *stream, int fd) { - ALOGV("out_standby"); - struct alsa_stream_out *out = (struct alsa_stream_out *)stream; - int status; - - pthread_mutex_lock(&out->dev->lock); + struct generic_stream_out *out = (struct generic_stream_out *)stream; pthread_mutex_lock(&out->lock); - status = do_output_standby(out); + dprintf(fd, "\tout_dump:\n" + "\t\tsample rate: %u\n" + "\t\tbuffer size: %zu\n" + "\t\tchannel mask: %08x\n" + "\t\tformat: %d\n" + "\t\tdevice(s): ", + out_get_sample_rate(stream), + out_get_buffer_size(stream), + out_get_channels(stream), + out_get_format(stream)); + if (out->num_devices == 0) { + dprintf(fd, "%08x\n", AUDIO_DEVICE_NONE); + } else { + for (uint32_t i = 0; i < out->num_devices; i++) { + if (i != 0) { + dprintf(fd, ", "); + } + dprintf(fd, "%08x", out->devices[i]); + } + dprintf(fd, "\n"); + } + dprintf(fd, "\t\taudio dev: %p\n\n", out->dev); pthread_mutex_unlock(&out->lock); - pthread_mutex_unlock(&out->dev->lock); - return status; -} - -static int out_dump(const struct audio_stream *stream, int fd) -{ - ALOGV("out_dump"); return 0; } static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) { - ALOGV("out_set_parameters"); - struct alsa_stream_out *out = (struct alsa_stream_out *)stream; - struct alsa_audio_device *adev = out->dev; struct str_parms *parms; char value[32]; - int ret, val = 0; + int success; + int ret = -EINVAL; + if (kvpairs == NULL || kvpairs[0] == 0) { + return 0; + } parms = str_parms_create_str(kvpairs); - - ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value)); - if (ret >= 0) { - val = atoi(value); - pthread_mutex_lock(&adev->lock); - pthread_mutex_lock(&out->lock); - if (((adev->devices & AUDIO_DEVICE_OUT_ALL) != val) && (val != 0)) { - adev->devices &= ~AUDIO_DEVICE_OUT_ALL; - adev->devices |= val; - } - pthread_mutex_unlock(&out->lock); - pthread_mutex_unlock(&adev->lock); + success = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, + value, sizeof(value)); + // As the hal version is 3.0, it must not use set parameters API to set audio devices. + // Instead, it should use create_audio_patch API. + assert(("Must not use set parameters API to set audio devices", success < 0)); + + if (str_parms_has_key(parms, AUDIO_PARAMETER_STREAM_FORMAT)) { + // match the return value of out_set_format + ret = -ENOSYS; } str_parms_destroy(parms); + + if (ret == -EINVAL) { + ALOGW("%s(), unsupported parameter %s", __func__, kvpairs); + // There is not any key supported for set_parameters API. + // Return error when there is non-null value passed in. + } return ret; } static char * out_get_parameters(const struct audio_stream *stream, const char *keys) { - ALOGV("out_get_parameters"); - return strdup(""); + struct generic_stream_out *out = (struct generic_stream_out *)stream; + struct str_parms *query = str_parms_create_str(keys); + char *str = NULL; + char value[256]; + struct str_parms *reply = str_parms_create(); + int ret; + bool get = false; + + ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value)); + if (ret >= 0) { + pthread_mutex_lock(&out->lock); + audio_devices_t device = AUDIO_DEVICE_NONE; + for (uint32_t i = 0; i < out->num_devices; i++) { + device |= out->devices[i]; + } + str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_ROUTING, device); + pthread_mutex_unlock(&out->lock); + get = true; + } + + if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_FORMATS)) { + value[0] = 0; + strcat(value, "AUDIO_FORMAT_PCM_16_BIT"); + str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_FORMATS, value); + get = true; + } + + if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_FORMAT)) { + value[0] = 0; + strcat(value, "AUDIO_FORMAT_PCM_16_BIT"); + str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_FORMAT, value); + get = true; + } + + if (get) { + str = str_parms_to_str(reply); + } + else { + ALOGD("%s Unsupported parameter: %s", __FUNCTION__, keys); + } + + str_parms_destroy(query); + str_parms_destroy(reply); + return str; } static uint32_t out_get_latency(const struct audio_stream_out *stream) { - ALOGV("out_get_latency"); - struct alsa_stream_out *out = (struct alsa_stream_out *)stream; - return (PERIOD_SIZE * PLAYBACK_PERIOD_COUNT * 1000) / out->config.rate; + struct generic_stream_out *out = (struct generic_stream_out *)stream; + return (out->pcm_config.period_size * 1000) / out->pcm_config.rate; } static int out_set_volume(struct audio_stream_out *stream, float left, - float right) + float right) { - ALOGV("out_set_volume: Left:%f Right:%f", left, right); - return 0; + return -ENOSYS; } -static ssize_t out_write(struct audio_stream_out *stream, const void* buffer, - size_t bytes) +static void *out_write_worker(void * args) { - int ret; - struct alsa_stream_out *out = (struct alsa_stream_out *)stream; - struct alsa_audio_device *adev = out->dev; - size_t frame_size = audio_stream_out_frame_size(stream); - size_t out_frames = bytes / frame_size; - - /* acquiring hw device mutex systematically is useful if a low priority thread is waiting - * on the output stream mutex - e.g. executing select_mode() while holding the hw device - * mutex - */ - pthread_mutex_lock(&adev->lock); - pthread_mutex_lock(&out->lock); - if (out->standby) { - ret = start_output_stream(out); + struct generic_stream_out *out = (struct generic_stream_out *)args; + struct pcm *pcm = NULL; + uint8_t *buffer = NULL; + int buffer_frames; + int buffer_size; + bool restart = false; + bool shutdown = false; + while (true) { + pthread_mutex_lock(&out->lock); + while (out->worker_standby || restart) { + restart = false; + if (pcm) { + pcm_close(pcm); // Frees pcm + pcm = NULL; + free(buffer); + buffer=NULL; + } + if (out->worker_exit) { + break; + } + pthread_cond_wait(&out->worker_wake, &out->lock); + } + + if (out->worker_exit) { + if (!out->worker_standby) { + ALOGE("Out worker not in standby before exiting"); + } + shutdown = true; + } + + while (!shutdown && audio_vbuffer_live(&out->buffer) == 0) { + pthread_cond_wait(&out->worker_wake, &out->lock); + } + + if (shutdown) { + pthread_mutex_unlock(&out->lock); + break; + } + + if (!pcm) { + pcm = pcm_open(PCM_CARD, PCM_DEVICE, + PCM_OUT | PCM_MONOTONIC, &out->pcm_config); + if (!pcm_is_ready(pcm)) { + ALOGE("pcm_open(out) failed: %s: channels %d format %d rate %d", + pcm_get_error(pcm), + out->pcm_config.channels, + out->pcm_config.format, + out->pcm_config.rate + ); + pthread_mutex_unlock(&out->lock); + break; + } + buffer_frames = out->pcm_config.period_size; + buffer_size = pcm_frames_to_bytes(pcm, buffer_frames); + buffer = malloc(buffer_size); + if (!buffer) { + ALOGE("could not allocate write buffer"); + pthread_mutex_unlock(&out->lock); + break; + } + } + int frames = audio_vbuffer_read(&out->buffer, buffer, buffer_frames); + pthread_mutex_unlock(&out->lock); + int ret = pcm_write(pcm, buffer, pcm_frames_to_bytes(pcm, frames)); if (ret != 0) { - pthread_mutex_unlock(&adev->lock); - goto exit; + ALOGE("pcm_write failed %s", pcm_get_error(pcm)); + restart = true; } - out->standby = 0; + } + if (buffer) { + free(buffer); } - pthread_mutex_unlock(&adev->lock); + return NULL; +} - ret = pcm_mmap_write(out->pcm, buffer, out_frames * frame_size); - if (ret == 0) { - out->written += out_frames; +// Call with in->lock held +static void get_current_output_position(struct generic_stream_out *out, + uint64_t * position, + struct timespec * timestamp) { + struct timespec curtime = { .tv_sec = 0, .tv_nsec = 0 }; + clock_gettime(CLOCK_MONOTONIC, &curtime); + const int64_t now_us = (curtime.tv_sec * 1000000000LL + curtime.tv_nsec) / 1000; + if (timestamp) { + *timestamp = curtime; } -exit: + int64_t position_since_underrun; + if (out->standby) { + position_since_underrun = 0; + } else { + const int64_t first_us = (out->underrun_time.tv_sec * 1000000000LL + + out->underrun_time.tv_nsec) / 1000; + position_since_underrun = (now_us - first_us) * + out_get_sample_rate(&out->stream.common) / + 1000000; + if (position_since_underrun < 0) { + position_since_underrun = 0; + } + } + *position = out->underrun_position + position_since_underrun; + + // The device will reuse the same output stream leading to periods of + // underrun. + if (*position > out->frames_written) { + ALOGW("Not supplying enough data to HAL, expected position %" PRIu64 " , only wrote " + "%" PRIu64, + *position, out->frames_written); + + *position = out->frames_written; + out->underrun_position = *position; + out->underrun_time = curtime; + out->frames_total_buffered = 0; + } +} + + +static ssize_t out_write(struct audio_stream_out *stream, const void *buffer, + size_t bytes) +{ + struct generic_stream_out *out = (struct generic_stream_out *)stream; + const size_t frames = bytes / audio_stream_out_frame_size(stream); + + pthread_mutex_lock(&out->lock); + + if (out->worker_standby) { + out->worker_standby = false; + } + + uint64_t current_position; + struct timespec current_time; + + get_current_output_position(out, ¤t_position, ¤t_time); + const uint64_t now_us = (current_time.tv_sec * 1000000000LL + + current_time.tv_nsec) / 1000; + if (out->standby) { + out->standby = false; + out->underrun_time = current_time; + out->frames_rendered = 0; + out->frames_total_buffered = 0; + } + + size_t frames_written = audio_vbuffer_write(&out->buffer, buffer, frames); + pthread_cond_signal(&out->worker_wake); + + /* Implementation just consumes bytes if we start getting backed up */ + out->frames_written += frames; + out->frames_rendered += frames; + out->frames_total_buffered += frames; + + // We simulate the audio device blocking when it's write buffers become + // full. + + // At the beginning or after an underrun, try to fill up the vbuffer. + // This will be throttled by the PlaybackThread + int frames_sleep = out->frames_total_buffered < out->buffer.frame_count ? 0 : frames; + + uint64_t sleep_time_us = frames_sleep * 1000000LL / + out_get_sample_rate(&stream->common); + + // If the write calls are delayed, subtract time off of the sleep to + // compensate + uint64_t time_since_last_write_us = now_us - out->last_write_time_us; + if (time_since_last_write_us < sleep_time_us) { + sleep_time_us -= time_since_last_write_us; + } else { + sleep_time_us = 0; + } + out->last_write_time_us = now_us + sleep_time_us; + pthread_mutex_unlock(&out->lock); - if (ret != 0) { - usleep((int64_t)bytes * 1000000 / audio_stream_out_frame_size(stream) / - out_get_sample_rate(&stream->common)); + if (sleep_time_us > 0) { + usleep(sleep_time_us); } + if (frames_written < frames) { + ALOGW("Hardware backing HAL too slow, could only write %zu of %zu frames", frames_written, frames); + } + + /* Always consume all bytes */ return bytes; } +static int out_get_presentation_position(const struct audio_stream_out *stream, + uint64_t *frames, struct timespec *timestamp) + +{ + if (stream == NULL || frames == NULL || timestamp == NULL) { + return -EINVAL; + } + struct generic_stream_out *out = (struct generic_stream_out *)stream; + + pthread_mutex_lock(&out->lock); + get_current_output_position(out, frames, timestamp); + pthread_mutex_unlock(&out->lock); + + return 0; +} + static int out_get_render_position(const struct audio_stream_out *stream, - uint32_t *dsp_frames) + uint32_t *dsp_frames) { - *dsp_frames = 0; - ALOGV("out_get_render_position: dsp_frames: %p", dsp_frames); - return -EINVAL; + if (stream == NULL || dsp_frames == NULL) { + return -EINVAL; + } + struct generic_stream_out *out = (struct generic_stream_out *)stream; + pthread_mutex_lock(&out->lock); + *dsp_frames = out->frames_rendered; + pthread_mutex_unlock(&out->lock); + return 0; } -static int out_get_presentation_position(const struct audio_stream_out *stream, - uint64_t *frames, struct timespec *timestamp) +// Must be called with out->lock held +static void do_out_standby(struct generic_stream_out *out) { - struct alsa_stream_out *out = (struct alsa_stream_out *)stream; - int ret = -1; - - if (out->pcm) { - unsigned int avail; - if (pcm_get_htimestamp(out->pcm, &avail, timestamp) == 0) { - size_t kernel_buffer_size = out->config.period_size * out->config.period_count; - int64_t signed_frames = out->written - kernel_buffer_size + avail; - if (signed_frames >= 0) { - *frames = signed_frames; - ret = 0; - } - } + int frames_sleep = 0; + uint64_t sleep_time_us = 0; + if (out->standby) { + return; + } + while (true) { + get_current_output_position(out, &out->underrun_position, NULL); + frames_sleep = out->frames_written - out->underrun_position; + + if (frames_sleep == 0) { + break; } - return ret; + sleep_time_us = frames_sleep * 1000000LL / + out_get_sample_rate(&out->stream.common); + + pthread_mutex_unlock(&out->lock); + usleep(sleep_time_us); + pthread_mutex_lock(&out->lock); + } + out->worker_standby = true; + out->standby = true; } +static int out_standby(struct audio_stream *stream) +{ + struct generic_stream_out *out = (struct generic_stream_out *)stream; + pthread_mutex_lock(&out->lock); + do_out_standby(out); + pthread_mutex_unlock(&out->lock); + return 0; +} static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) { - ALOGV("out_add_audio_effect: %p", effect); + // out_add_audio_effect is a no op return 0; } static int out_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) { - ALOGV("out_remove_audio_effect: %p", effect); + // out_remove_audio_effect is a no op return 0; } static int out_get_next_write_timestamp(const struct audio_stream_out *stream, - int64_t *timestamp) + int64_t *timestamp) { - *timestamp = 0; - ALOGV("out_get_next_write_timestamp: %ld", (long int)(*timestamp)); - return -EINVAL; + return -ENOSYS; } -/** audio_stream_in implementation **/ static uint32_t in_get_sample_rate(const struct audio_stream *stream) { - ALOGV("in_get_sample_rate"); - return 8000; + struct generic_stream_in *in = (struct generic_stream_in *)stream; + return in->req_config.sample_rate; } static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate) { - ALOGV("in_set_sample_rate: %d", rate); return -ENOSYS; } +static int refine_output_parameters(uint32_t *sample_rate, audio_format_t *format, audio_channel_mask_t *channel_mask) +{ + static const uint32_t sample_rates [] = {8000,11025,16000,22050,24000,32000, + 44100,48000}; + static const int sample_rates_count = sizeof(sample_rates)/sizeof(uint32_t); + bool inval = false; + if (*format != AUDIO_FORMAT_PCM_16_BIT) { + *format = AUDIO_FORMAT_PCM_16_BIT; + inval = true; + } + + int channel_count = popcount(*channel_mask); + if (channel_count != 1 && channel_count != 2) { + *channel_mask = AUDIO_CHANNEL_IN_STEREO; + inval = true; + } + + int i; + for (i = 0; i < sample_rates_count; i++) { + if (*sample_rate < sample_rates[i]) { + *sample_rate = sample_rates[i]; + inval=true; + break; + } + else if (*sample_rate == sample_rates[i]) { + break; + } + else if (i == sample_rates_count-1) { + // Cap it to the highest rate we support + *sample_rate = sample_rates[i]; + inval=true; + } + } + + if (inval) { + return -EINVAL; + } + return 0; +} + +static int refine_input_parameters(uint32_t *sample_rate, audio_format_t *format, audio_channel_mask_t *channel_mask) +{ + static const uint32_t sample_rates [] = {8000, 11025, 16000, 22050, 44100, 48000}; + static const int sample_rates_count = sizeof(sample_rates)/sizeof(uint32_t); + bool inval = false; + // Only PCM_16_bit is supported. If this is changed, stereo to mono drop + // must be fixed in in_read + if (*format != AUDIO_FORMAT_PCM_16_BIT) { + *format = AUDIO_FORMAT_PCM_16_BIT; + inval = true; + } + + int channel_count = popcount(*channel_mask); + if (channel_count != 1 && channel_count != 2) { + *channel_mask = AUDIO_CHANNEL_IN_STEREO; + inval = true; + } + + int i; + for (i = 0; i < sample_rates_count; i++) { + if (*sample_rate < sample_rates[i]) { + *sample_rate = sample_rates[i]; + inval=true; + break; + } + else if (*sample_rate == sample_rates[i]) { + break; + } + else if (i == sample_rates_count-1) { + // Cap it to the highest rate we support + *sample_rate = sample_rates[i]; + inval=true; + } + } + + if (inval) { + return -EINVAL; + } + return 0; +} + +static int check_input_parameters(uint32_t sample_rate, audio_format_t format, + audio_channel_mask_t channel_mask) +{ + return refine_input_parameters(&sample_rate, &format, &channel_mask); +} + +static size_t get_input_buffer_size(uint32_t sample_rate, audio_format_t format, + audio_channel_mask_t channel_mask) +{ + size_t size; + int channel_count = popcount(channel_mask); + if (check_input_parameters(sample_rate, format, channel_mask) != 0) + return 0; + + size = sample_rate*IN_PERIOD_MS/1000; + // Audioflinger expects audio buffers to be multiple of 16 frames + size = ((size + 15) / 16) * 16; + size *= sizeof(short) * channel_count; + + return size; +} + + static size_t in_get_buffer_size(const struct audio_stream *stream) { - ALOGV("in_get_buffer_size: %d", 320); - return 320; + struct generic_stream_in *in = (struct generic_stream_in *)stream; + int size = get_input_buffer_size(in->req_config.sample_rate, + in->req_config.format, + in->req_config.channel_mask); + + return size; } static audio_channel_mask_t in_get_channels(const struct audio_stream *stream) { - ALOGV("in_get_channels: %d", AUDIO_CHANNEL_IN_MONO); - return AUDIO_CHANNEL_IN_MONO; + struct generic_stream_in *in = (struct generic_stream_in *)stream; + return in->req_config.channel_mask; } static audio_format_t in_get_format(const struct audio_stream *stream) { - return AUDIO_FORMAT_PCM_16_BIT; + struct generic_stream_in *in = (struct generic_stream_in *)stream; + return in->req_config.format; } static int in_set_format(struct audio_stream *stream, audio_format_t format) @@ -365,40 +825,326 @@ static int in_set_format(struct audio_stream *stream, audio_format_t format) return -ENOSYS; } -static int in_standby(struct audio_stream *stream) -{ - return 0; -} - static int in_dump(const struct audio_stream *stream, int fd) { + struct generic_stream_in *in = (struct generic_stream_in *)stream; + + pthread_mutex_lock(&in->lock); + dprintf(fd, "\tin_dump:\n" + "\t\tsample rate: %u\n" + "\t\tbuffer size: %zu\n" + "\t\tchannel mask: %08x\n" + "\t\tformat: %d\n" + "\t\tdevice: %08x\n" + "\t\taudio dev: %p\n\n", + in_get_sample_rate(stream), + in_get_buffer_size(stream), + in_get_channels(stream), + in_get_format(stream), + in->device, + in->dev); + pthread_mutex_unlock(&in->lock); return 0; } static int in_set_parameters(struct audio_stream *stream, const char *kvpairs) { - return 0; + struct str_parms *parms; + char value[32]; + int success; + int ret = -EINVAL; + + if (kvpairs == NULL || kvpairs[0] == 0) { + return 0; + } + parms = str_parms_create_str(kvpairs); + success = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING, + value, sizeof(value)); + // As the hal version is 3.0, it must not use set parameters API to set audio device. + // Instead, it should use create_audio_patch API. + assert(("Must not use set parameters API to set audio devices", success < 0)); + + if (str_parms_has_key(parms, AUDIO_PARAMETER_STREAM_FORMAT)) { + // match the return value of in_set_format + ret = -ENOSYS; + } + + str_parms_destroy(parms); + + if (ret == -EINVAL) { + ALOGW("%s(), unsupported parameter %s", __func__, kvpairs); + // There is not any key supported for set_parameters API. + // Return error when there is non-null value passed in. + } + return ret; } static char * in_get_parameters(const struct audio_stream *stream, - const char *keys) + const char *keys) { - return strdup(""); + struct generic_stream_in *in = (struct generic_stream_in *)stream; + struct str_parms *query = str_parms_create_str(keys); + char *str = NULL; + char value[256]; + struct str_parms *reply = str_parms_create(); + int ret; + bool get = false; + + ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value)); + if (ret >= 0) { + str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_ROUTING, in->device); + get = true; + } + + if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_SUP_FORMATS)) { + value[0] = 0; + strcat(value, "AUDIO_FORMAT_PCM_16_BIT"); + str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_SUP_FORMATS, value); + get = true; + } + + if (str_parms_has_key(query, AUDIO_PARAMETER_STREAM_FORMAT)) { + value[0] = 0; + strcat(value, "AUDIO_FORMAT_PCM_16_BIT"); + str_parms_add_str(reply, AUDIO_PARAMETER_STREAM_FORMAT, value); + get = true; + } + + if (get) { + str = str_parms_to_str(reply); + } + else { + ALOGD("%s Unsupported parameter: %s", __FUNCTION__, keys); + } + + str_parms_destroy(query); + str_parms_destroy(reply); + return str; } static int in_set_gain(struct audio_stream_in *stream, float gain) { + // in_set_gain is a no op return 0; } +// Call with in->lock held +static void get_current_input_position(struct generic_stream_in *in, + int64_t * position, + struct timespec * timestamp) { + struct timespec t = { .tv_sec = 0, .tv_nsec = 0 }; + clock_gettime(CLOCK_MONOTONIC, &t); + const int64_t now_us = (t.tv_sec * 1000000000LL + t.tv_nsec) / 1000; + if (timestamp) { + *timestamp = t; + } + int64_t position_since_standby; + if (in->standby) { + position_since_standby = 0; + } else { + const int64_t first_us = (in->standby_exit_time.tv_sec * 1000000000LL + + in->standby_exit_time.tv_nsec) / 1000; + position_since_standby = (now_us - first_us) * + in_get_sample_rate(&in->stream.common) / + 1000000; + if (position_since_standby < 0) { + position_since_standby = 0; + } + } + *position = in->standby_position + position_since_standby; +} + +// Must be called with in->lock held +static void do_in_standby(struct generic_stream_in *in) +{ + if (in->standby) { + return; + } + in->worker_standby = true; + get_current_input_position(in, &in->standby_position, NULL); + in->standby = true; +} + +static int in_standby(struct audio_stream *stream) +{ + struct generic_stream_in *in = (struct generic_stream_in *)stream; + pthread_mutex_lock(&in->lock); + do_in_standby(in); + pthread_mutex_unlock(&in->lock); + return 0; +} + +static void *in_read_worker(void * args) +{ + struct generic_stream_in *in = (struct generic_stream_in *)args; + struct pcm *pcm = NULL; + uint8_t *buffer = NULL; + size_t buffer_frames; + int buffer_size; + + bool restart = false; + bool shutdown = false; + while (true) { + pthread_mutex_lock(&in->lock); + while (in->worker_standby || restart) { + restart = false; + if (pcm) { + pcm_close(pcm); // Frees pcm + pcm = NULL; + free(buffer); + buffer=NULL; + } + if (in->worker_exit) { + break; + } + pthread_cond_wait(&in->worker_wake, &in->lock); + } + + if (in->worker_exit) { + if (!in->worker_standby) { + ALOGE("In worker not in standby before exiting"); + } + shutdown = true; + } + if (shutdown) { + pthread_mutex_unlock(&in->lock); + break; + } + if (!pcm) { + pcm = pcm_open(PCM_CARD, PCM_DEVICE, + PCM_IN | PCM_MONOTONIC, &in->pcm_config); + if (!pcm_is_ready(pcm)) { + ALOGE("pcm_open(in) failed: %s: channels %d format %d rate %d", + pcm_get_error(pcm), + in->pcm_config.channels, + in->pcm_config.format, + in->pcm_config.rate + ); + pthread_mutex_unlock(&in->lock); + break; + } + buffer_frames = in->pcm_config.period_size; + buffer_size = pcm_frames_to_bytes(pcm, buffer_frames); + buffer = malloc(buffer_size); + if (!buffer) { + ALOGE("could not allocate worker read buffer"); + pthread_mutex_unlock(&in->lock); + break; + } + } + pthread_mutex_unlock(&in->lock); + int ret = pcm_read(pcm, buffer, pcm_frames_to_bytes(pcm, buffer_frames)); + if (ret != 0) { + ALOGW("pcm_read failed %s", pcm_get_error(pcm)); + restart = true; + continue; + } + + pthread_mutex_lock(&in->lock); + size_t frames_written = audio_vbuffer_write(&in->buffer, buffer, buffer_frames); + pthread_mutex_unlock(&in->lock); + + if (frames_written != buffer_frames) { + ALOGW("in_read_worker only could write %zu / %zu frames", frames_written, buffer_frames); + } + } + if (buffer) { + free(buffer); + } + return NULL; +} + static ssize_t in_read(struct audio_stream_in *stream, void* buffer, - size_t bytes) + size_t bytes) { - ALOGV("in_read: bytes %zu", bytes); - /* XXX: fake timing for audio input */ - usleep((int64_t)bytes * 1000000 / audio_stream_in_frame_size(stream) / - in_get_sample_rate(&stream->common)); - memset(buffer, 0, bytes); + struct generic_stream_in *in = (struct generic_stream_in *)stream; + struct generic_audio_device *adev = in->dev; + const size_t frames = bytes / audio_stream_in_frame_size(stream); + bool mic_mute = false; + size_t read_bytes = 0; + + adev_get_mic_mute(&adev->device, &mic_mute); + pthread_mutex_lock(&in->lock); + + if (in->worker_standby) { + in->worker_standby = false; + } + pthread_cond_signal(&in->worker_wake); + + int64_t current_position; + struct timespec current_time; + + get_current_input_position(in, ¤t_position, ¤t_time); + if (in->standby) { + in->standby = false; + in->standby_exit_time = current_time; + in->standby_frames_read = 0; + } + + const int64_t frames_available = current_position - in->standby_position - in->standby_frames_read; + assert(frames_available >= 0); + + const size_t frames_wait = ((uint64_t)frames_available > frames) ? 0 : frames - frames_available; + + int64_t sleep_time_us = frames_wait * 1000000LL / + in_get_sample_rate(&stream->common); + + pthread_mutex_unlock(&in->lock); + + if (sleep_time_us > 0) { + usleep(sleep_time_us); + } + + pthread_mutex_lock(&in->lock); + int read_frames = 0; + if (in->standby) { + ALOGW("Input put to sleep while read in progress"); + goto exit; + } + in->standby_frames_read += frames; + + if (popcount(in->req_config.channel_mask) == 1 && + in->pcm_config.channels == 2) { + // Need to resample to mono + if (in->stereo_to_mono_buf_size < bytes*2) { + in->stereo_to_mono_buf = realloc(in->stereo_to_mono_buf, + bytes*2); + if (!in->stereo_to_mono_buf) { + ALOGE("Failed to allocate stereo_to_mono_buff"); + goto exit; + } + } + + read_frames = audio_vbuffer_read(&in->buffer, in->stereo_to_mono_buf, frames); + + // Currently only pcm 16 is supported. + uint16_t *src = (uint16_t *)in->stereo_to_mono_buf; + uint16_t *dst = (uint16_t *)buffer; + size_t i; + // Resample stereo 16 to mono 16 by dropping one channel. + // The stereo stream is interleaved L-R-L-R + for (i = 0; i < frames; i++) { + *dst = *src; + src += 2; + dst += 1; + } + } else { + read_frames = audio_vbuffer_read(&in->buffer, buffer, frames); + } + +exit: + read_bytes = read_frames*audio_stream_in_frame_size(stream); + + if (mic_mute) { + read_bytes = 0; + } + + if (read_bytes < bytes) { + memset (&((uint8_t *)buffer)[read_bytes], 0, bytes-read_bytes); + } + + pthread_mutex_unlock(&in->lock); + return bytes; } @@ -407,36 +1153,58 @@ static uint32_t in_get_input_frames_lost(struct audio_stream_in *stream) return 0; } +static int in_get_capture_position(const struct audio_stream_in *stream, + int64_t *frames, int64_t *time) +{ + struct generic_stream_in *in = (struct generic_stream_in *)stream; + pthread_mutex_lock(&in->lock); + struct timespec current_time; + get_current_input_position(in, frames, ¤t_time); + *time = (current_time.tv_sec * 1000000000LL + current_time.tv_nsec); + pthread_mutex_unlock(&in->lock); + return 0; +} + +static int in_get_active_microphones(const struct audio_stream_in *stream, + struct audio_microphone_characteristic_t *mic_array, + size_t *mic_count) +{ + return adev_get_microphones(NULL, mic_array, mic_count); +} + static int in_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect) { + // in_add_audio_effect is a no op return 0; } static int in_remove_audio_effect(const struct audio_stream *stream, effect_handle_t effect) { + // in_add_audio_effect is a no op return 0; } static int adev_open_output_stream(struct audio_hw_device *dev, - audio_io_handle_t handle, - audio_devices_t devices, - audio_output_flags_t flags, - struct audio_config *config, - struct audio_stream_out **stream_out, - const char *address __unused) -{ - ALOGV("adev_open_output_stream..."); - - struct alsa_audio_device *ladev = (struct alsa_audio_device *)dev; - struct alsa_stream_out *out; - struct pcm_params *params; + audio_io_handle_t handle, + audio_devices_t devices, + audio_output_flags_t flags, + struct audio_config *config, + struct audio_stream_out **stream_out, + const char *address __unused) +{ + struct generic_audio_device *adev = (struct generic_audio_device *)dev; + struct generic_stream_out *out; int ret = 0; - params = pcm_params_get(CARD_OUT, PORT_CODEC, PCM_OUT); - if (!params) - return -ENOSYS; + if (refine_output_parameters(&config->sample_rate, &config->format, &config->channel_mask)) { + ALOGE("Error opening output stream format %d, channel_mask %04x, sample_rate %u", + config->format, config->channel_mask, config->sample_rate); + ret = -EINVAL; + goto error; + } + + out = (struct generic_stream_out *)calloc(1, sizeof(struct generic_stream_out)); - out = (struct alsa_stream_out *)calloc(1, sizeof(struct alsa_stream_out)); if (!out) return -ENOMEM; @@ -456,221 +1224,598 @@ static int adev_open_output_stream(struct audio_hw_device *dev, out->stream.set_volume = out_set_volume; out->stream.write = out_write; out->stream.get_render_position = out_get_render_position; - out->stream.get_next_write_timestamp = out_get_next_write_timestamp; out->stream.get_presentation_position = out_get_presentation_position; + out->stream.get_next_write_timestamp = out_get_next_write_timestamp; - out->config.channels = CHANNEL_STEREO; - out->config.rate = CODEC_SAMPLING_RATE; - out->config.format = PCM_FORMAT_S16_LE; - out->config.period_size = PERIOD_SIZE; - out->config.period_count = PLAYBACK_PERIOD_COUNT; - - if (out->config.rate != config->sample_rate || - audio_channel_count_from_out_mask(config->channel_mask) != CHANNEL_STEREO || - out->config.format != pcm_format_from_audio_format(config->format) ) { - config->sample_rate = out->config.rate; - config->format = audio_format_from_pcm_format(out->config.format); - config->channel_mask = audio_channel_out_mask_from_count(CHANNEL_STEREO); - ret = -EINVAL; - } - - ALOGI("adev_open_output_stream selects channels=%d rate=%d format=%d", - out->config.channels, out->config.rate, out->config.format); + out->handle = handle; + + pthread_mutex_init(&out->lock, (const pthread_mutexattr_t *) NULL); + out->dev = adev; + // Only 1 device is expected despite the argument being named 'devices' + out->num_devices = 1; + out->devices[0] = devices; + memcpy(&out->req_config, config, sizeof(struct audio_config)); + memcpy(&out->pcm_config, &pcm_config_out, sizeof(struct pcm_config)); + out->pcm_config.rate = config->sample_rate; + out->pcm_config.period_size = out->pcm_config.rate*OUT_PERIOD_MS/1000; + + out->standby = true; + out->underrun_position = 0; + out->underrun_time.tv_sec = 0; + out->underrun_time.tv_nsec = 0; + out->last_write_time_us = 0; + out->frames_total_buffered = 0; + out->frames_written = 0; + out->frames_rendered = 0; + + ret = audio_vbuffer_init(&out->buffer, + out->pcm_config.period_size*out->pcm_config.period_count, + out->pcm_config.channels * + pcm_format_to_bits(out->pcm_config.format) >> 3); + if (ret == 0) { + pthread_cond_init(&out->worker_wake, NULL); + out->worker_standby = true; + out->worker_exit = false; + pthread_create(&out->worker_thread, NULL, out_write_worker, out); - out->dev = ladev; - out->standby = 1; - out->unavailable = false; + } - config->format = out_get_format(&out->stream.common); - config->channel_mask = out_get_channels(&out->stream.common); - config->sample_rate = out_get_sample_rate(&out->stream.common); + pthread_mutex_lock(&adev->lock); + list_add_tail(&adev->out_streams, &out->stream_node); + pthread_mutex_unlock(&adev->lock); *stream_out = &out->stream; - /* TODO The retry mechanism isn't implemented in AudioPolicyManager/AudioFlinger. */ - ret = 0; +error: return ret; } +// This must be called with adev->lock held. +struct generic_stream_out *get_stream_out_by_io_handle_l( + struct generic_audio_device *adev, audio_io_handle_t handle) { + struct listnode *node; + + list_for_each(node, &adev->out_streams) { + struct generic_stream_out *out = node_to_item( + node, struct generic_stream_out, stream_node); + if (out->handle == handle) { + return out; + } + } + return NULL; +} + static void adev_close_output_stream(struct audio_hw_device *dev, - struct audio_stream_out *stream) + struct audio_stream_out *stream) { - ALOGV("adev_close_output_stream..."); + struct generic_stream_out *out = (struct generic_stream_out *)stream; + pthread_mutex_lock(&out->lock); + do_out_standby(out); + + out->worker_exit = true; + pthread_cond_signal(&out->worker_wake); + pthread_mutex_unlock(&out->lock); + + pthread_join(out->worker_thread, NULL); + pthread_mutex_destroy(&out->lock); + audio_vbuffer_destroy(&out->buffer); + + struct generic_audio_device *adev = (struct generic_audio_device *) dev; + pthread_mutex_lock(&adev->lock); + list_remove(&out->stream_node); + pthread_mutex_unlock(&adev->lock); free(stream); } static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) { - ALOGV("adev_set_parameters"); - return -ENOSYS; + return 0; } static char * adev_get_parameters(const struct audio_hw_device *dev, - const char *keys) + const char *keys) { - ALOGV("adev_get_parameters"); return strdup(""); } static int adev_init_check(const struct audio_hw_device *dev) { - ALOGV("adev_init_check"); return 0; } static int adev_set_voice_volume(struct audio_hw_device *dev, float volume) { - ALOGV("adev_set_voice_volume: %f", volume); - return -ENOSYS; + // adev_set_voice_volume is a no op (simulates phones) + return 0; } static int adev_set_master_volume(struct audio_hw_device *dev, float volume) { - ALOGV("adev_set_master_volume: %f", volume); return -ENOSYS; } static int adev_get_master_volume(struct audio_hw_device *dev, float *volume) { - ALOGV("adev_get_master_volume: %f", *volume); return -ENOSYS; } static int adev_set_master_mute(struct audio_hw_device *dev, bool muted) { - ALOGV("adev_set_master_mute: %d", muted); return -ENOSYS; } static int adev_get_master_mute(struct audio_hw_device *dev, bool *muted) { - ALOGV("adev_get_master_mute: %d", *muted); return -ENOSYS; } static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t mode) { - ALOGV("adev_set_mode: %d", mode); + // adev_set_mode is a no op (simulates phones) return 0; } static int adev_set_mic_mute(struct audio_hw_device *dev, bool state) { - ALOGV("adev_set_mic_mute: %d",state); - return -ENOSYS; + struct generic_audio_device *adev = (struct generic_audio_device *)dev; + pthread_mutex_lock(&adev->lock); + adev->mic_mute = state; + pthread_mutex_unlock(&adev->lock); + return 0; } static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state) { - ALOGV("adev_get_mic_mute"); - return -ENOSYS; + struct generic_audio_device *adev = (struct generic_audio_device *)dev; + pthread_mutex_lock(&adev->lock); + *state = adev->mic_mute; + pthread_mutex_unlock(&adev->lock); + return 0; } + static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev, - const struct audio_config *config) + const struct audio_config *config) { - ALOGV("adev_get_input_buffer_size: %d", 320); - return 320; + return get_input_buffer_size(config->sample_rate, config->format, config->channel_mask); +} + +// This must be called with adev->lock held. +struct generic_stream_in *get_stream_in_by_io_handle_l( + struct generic_audio_device *adev, audio_io_handle_t handle) { + struct listnode *node; + + list_for_each(node, &adev->in_streams) { + struct generic_stream_in *in = node_to_item( + node, struct generic_stream_in, stream_node); + if (in->handle == handle) { + return in; + } + } + return NULL; } -static int adev_open_input_stream(struct audio_hw_device __unused *dev, - audio_io_handle_t handle, - audio_devices_t devices, - struct audio_config *config, - struct audio_stream_in **stream_in, - audio_input_flags_t flags __unused, - const char *address __unused, - audio_source_t source __unused) +static void adev_close_input_stream(struct audio_hw_device *dev, + struct audio_stream_in *stream) { - struct stub_stream_in *in; + struct generic_stream_in *in = (struct generic_stream_in *)stream; + pthread_mutex_lock(&in->lock); + do_in_standby(in); + + in->worker_exit = true; + pthread_cond_signal(&in->worker_wake); + pthread_mutex_unlock(&in->lock); + pthread_join(in->worker_thread, NULL); + + if (in->stereo_to_mono_buf != NULL) { + free(in->stereo_to_mono_buf); + in->stereo_to_mono_buf_size = 0; + } - ALOGV("adev_open_input_stream..."); + pthread_mutex_destroy(&in->lock); + audio_vbuffer_destroy(&in->buffer); - in = (struct stub_stream_in *)calloc(1, sizeof(struct stub_stream_in)); - if (!in) - return -ENOMEM; + struct generic_audio_device *adev = (struct generic_audio_device *) dev; + pthread_mutex_lock(&adev->lock); + list_remove(&in->stream_node); + pthread_mutex_unlock(&adev->lock); + free(stream); +} + + +static int adev_open_input_stream(struct audio_hw_device *dev, + audio_io_handle_t handle, + audio_devices_t devices, + struct audio_config *config, + struct audio_stream_in **stream_in, + audio_input_flags_t flags __unused, + const char *address __unused, + audio_source_t source __unused) +{ + struct generic_audio_device *adev = (struct generic_audio_device *)dev; + struct generic_stream_in *in; + int ret = 0; + if (refine_input_parameters(&config->sample_rate, &config->format, &config->channel_mask)) { + ALOGE("Error opening input stream format %d, channel_mask %04x, sample_rate %u", + config->format, config->channel_mask, config->sample_rate); + ret = -EINVAL; + goto error; + } + + in = (struct generic_stream_in *)calloc(1, sizeof(struct generic_stream_in)); + if (!in) { + ret = -ENOMEM; + goto error; + } in->stream.common.get_sample_rate = in_get_sample_rate; - in->stream.common.set_sample_rate = in_set_sample_rate; + in->stream.common.set_sample_rate = in_set_sample_rate; // no op in->stream.common.get_buffer_size = in_get_buffer_size; in->stream.common.get_channels = in_get_channels; in->stream.common.get_format = in_get_format; - in->stream.common.set_format = in_set_format; + in->stream.common.set_format = in_set_format; // no op in->stream.common.standby = in_standby; in->stream.common.dump = in_dump; in->stream.common.set_parameters = in_set_parameters; in->stream.common.get_parameters = in_get_parameters; - in->stream.common.add_audio_effect = in_add_audio_effect; - in->stream.common.remove_audio_effect = in_remove_audio_effect; - in->stream.set_gain = in_set_gain; + in->stream.common.add_audio_effect = in_add_audio_effect; // no op + in->stream.common.remove_audio_effect = in_remove_audio_effect; // no op + in->stream.set_gain = in_set_gain; // no op in->stream.read = in_read; - in->stream.get_input_frames_lost = in_get_input_frames_lost; + in->stream.get_input_frames_lost = in_get_input_frames_lost; // no op + in->stream.get_capture_position = in_get_capture_position; + in->stream.get_active_microphones = in_get_active_microphones; + + pthread_mutex_init(&in->lock, (const pthread_mutexattr_t *) NULL); + in->dev = adev; + in->device = devices; + memcpy(&in->req_config, config, sizeof(struct audio_config)); + memcpy(&in->pcm_config, &pcm_config_in, sizeof(struct pcm_config)); + in->pcm_config.rate = config->sample_rate; + in->pcm_config.period_size = in->pcm_config.rate*IN_PERIOD_MS/1000; + + in->stereo_to_mono_buf = NULL; + in->stereo_to_mono_buf_size = 0; + + in->standby = true; + in->standby_position = 0; + in->standby_exit_time.tv_sec = 0; + in->standby_exit_time.tv_nsec = 0; + in->standby_frames_read = 0; + + ret = audio_vbuffer_init(&in->buffer, + in->pcm_config.period_size*in->pcm_config.period_count, + in->pcm_config.channels * + pcm_format_to_bits(in->pcm_config.format) >> 3); + if (ret == 0) { + pthread_cond_init(&in->worker_wake, NULL); + in->worker_standby = true; + in->worker_exit = false; + pthread_create(&in->worker_thread, NULL, in_read_worker, in); + } + in->handle = handle; + + pthread_mutex_lock(&adev->lock); + list_add_tail(&adev->in_streams, &in->stream_node); + pthread_mutex_unlock(&adev->lock); *stream_in = &in->stream; - return 0; + +error: + return ret; } -static void adev_close_input_stream(struct audio_hw_device *dev, - struct audio_stream_in *in) + +static int adev_dump(const audio_hw_device_t *dev, int fd) { - ALOGV("adev_close_input_stream..."); - return; + return 0; } -static int adev_dump(const audio_hw_device_t *device, int fd) +static int adev_get_microphones(const audio_hw_device_t *dev, + struct audio_microphone_characteristic_t *mic_array, + size_t *mic_count) { - ALOGV("adev_dump"); + if (mic_count == NULL) { + return -ENOSYS; + } + + if (*mic_count == 0) { + *mic_count = 1; + return 0; + } + + if (mic_array == NULL) { + return -ENOSYS; + } + + strncpy(mic_array->device_id, "mic_goldfish", AUDIO_MICROPHONE_ID_MAX_LEN - 1); + mic_array->device = AUDIO_DEVICE_IN_BUILTIN_MIC; + strncpy(mic_array->address, AUDIO_BOTTOM_MICROPHONE_ADDRESS, + AUDIO_DEVICE_MAX_ADDRESS_LEN - 1); + memset(mic_array->channel_mapping, AUDIO_MICROPHONE_CHANNEL_MAPPING_UNUSED, + sizeof(mic_array->channel_mapping)); + mic_array->location = AUDIO_MICROPHONE_LOCATION_UNKNOWN; + mic_array->group = 0; + mic_array->index_in_the_group = 0; + mic_array->sensitivity = AUDIO_MICROPHONE_SENSITIVITY_UNKNOWN; + mic_array->max_spl = AUDIO_MICROPHONE_SPL_UNKNOWN; + mic_array->min_spl = AUDIO_MICROPHONE_SPL_UNKNOWN; + mic_array->directionality = AUDIO_MICROPHONE_DIRECTIONALITY_UNKNOWN; + mic_array->num_frequency_responses = 0; + mic_array->geometric_location.x = AUDIO_MICROPHONE_COORDINATE_UNKNOWN; + mic_array->geometric_location.y = AUDIO_MICROPHONE_COORDINATE_UNKNOWN; + mic_array->geometric_location.z = AUDIO_MICROPHONE_COORDINATE_UNKNOWN; + mic_array->orientation.x = AUDIO_MICROPHONE_COORDINATE_UNKNOWN; + mic_array->orientation.y = AUDIO_MICROPHONE_COORDINATE_UNKNOWN; + mic_array->orientation.z = AUDIO_MICROPHONE_COORDINATE_UNKNOWN; + + *mic_count = 1; return 0; } -static int adev_close(hw_device_t *device) -{ - ALOGV("adev_close"); - free(device); +static int adev_create_audio_patch(struct audio_hw_device *dev, + unsigned int num_sources, + const struct audio_port_config *sources, + unsigned int num_sinks, + const struct audio_port_config *sinks, + audio_patch_handle_t *handle) { + if (num_sources != 1 || num_sinks == 0 || num_sinks > AUDIO_PATCH_PORTS_MAX) { + return -EINVAL; + } + + if (sources[0].type == AUDIO_PORT_TYPE_DEVICE) { + // If source is a device, the number of sinks should be 1. + if (num_sinks != 1 || sinks[0].type != AUDIO_PORT_TYPE_MIX) { + return -EINVAL; + } + } else if (sources[0].type == AUDIO_PORT_TYPE_MIX) { + // If source is a mix, all sinks should be device. + for (unsigned int i = 0; i < num_sinks; i++) { + if (sinks[i].type != AUDIO_PORT_TYPE_DEVICE) { + ALOGE("%s() invalid sink type %#x for mix source", __func__, sinks[i].type); + return -EINVAL; + } + } + } else { + // All other cases are invalid. + return -EINVAL; + } + + struct generic_audio_device* adev = (struct generic_audio_device*) dev; + int ret = 0; + bool generatedPatchHandle = false; + pthread_mutex_lock(&adev->lock); + if (*handle == AUDIO_PATCH_HANDLE_NONE) { + *handle = ++adev->next_patch_handle; + generatedPatchHandle = true; + } + + // Only handle patches for mix->devices and device->mix case. + if (sources[0].type == AUDIO_PORT_TYPE_DEVICE) { + struct generic_stream_in *in = + get_stream_in_by_io_handle_l(adev, sinks[0].ext.mix.handle); + if (in == NULL) { + ALOGE("%s()can not find stream with handle(%d)", __func__, sources[0].ext.mix.handle); + ret = -EINVAL; + goto error; + } + + // Check if the patch handle match the recorded one if a valid patch handle is passed. + if (!generatedPatchHandle && in->patch_handle != *handle) { + ALOGE("%s() the patch handle(%d) does not match recorded one(%d) for stream " + "with handle(%d) when creating audio patch for device->mix", + __func__, *handle, in->patch_handle, in->handle); + ret = -EINVAL; + goto error; + } + pthread_mutex_lock(&in->lock); + in->device = sources[0].ext.device.type; + pthread_mutex_unlock(&in->lock); + in->patch_handle = *handle; + } else { + struct generic_stream_out *out = + get_stream_out_by_io_handle_l(adev, sources[0].ext.mix.handle); + if (out == NULL) { + ALOGE("%s()can not find stream with handle(%d)", __func__, sources[0].ext.mix.handle); + ret = -EINVAL; + goto error; + } + + // Check if the patch handle match the recorded one if a valid patch handle is passed. + if (!generatedPatchHandle && out->patch_handle != *handle) { + ALOGE("%s() the patch handle(%d) does not match recorded one(%d) for stream " + "with handle(%d) when creating audio patch for mix->device", + __func__, *handle, out->patch_handle, out->handle); + ret = -EINVAL; + pthread_mutex_unlock(&out->lock); + goto error; + } + pthread_mutex_lock(&out->lock); + for (out->num_devices = 0; out->num_devices < num_sinks; out->num_devices++) { + out->devices[out->num_devices] = sinks[out->num_devices].ext.device.type; + } + pthread_mutex_unlock(&out->lock); + out->patch_handle = *handle; + } + +error: + if (ret != 0 && generatedPatchHandle) { + *handle = AUDIO_PATCH_HANDLE_NONE; + } + pthread_mutex_unlock(&adev->lock); return 0; } -static int adev_open(const hw_module_t* module, const char* name, - hw_device_t** device) +// This must be called with adev->lock held. +struct generic_stream_out *get_stream_out_by_patch_handle_l( + struct generic_audio_device *adev, audio_patch_handle_t patch_handle) { + struct listnode *node; + + list_for_each(node, &adev->out_streams) { + struct generic_stream_out *out = node_to_item( + node, struct generic_stream_out, stream_node); + if (out->patch_handle == patch_handle) { + return out; + } + } + return NULL; +} + +// This must be called with adev->lock held. +struct generic_stream_in *get_stream_in_by_patch_handle_l( + struct generic_audio_device *adev, audio_patch_handle_t patch_handle) { + struct listnode *node; + + list_for_each(node, &adev->in_streams) { + struct generic_stream_in *in = node_to_item( + node, struct generic_stream_in, stream_node); + if (in->patch_handle == patch_handle) { + return in; + } + } + return NULL; +} + +static int adev_release_audio_patch(struct audio_hw_device *dev, + audio_patch_handle_t patch_handle) { + struct generic_audio_device *adev = (struct generic_audio_device *) dev; + + pthread_mutex_lock(&adev->lock); + struct generic_stream_out *out = get_stream_out_by_patch_handle_l(adev, patch_handle); + if (out != NULL) { + pthread_mutex_lock(&out->lock); + out->num_devices = 0; + memset(out->devices, 0, sizeof(out->devices)); + pthread_mutex_unlock(&out->lock); + out->patch_handle = AUDIO_PATCH_HANDLE_NONE; + pthread_mutex_unlock(&adev->lock); + return 0; + } + struct generic_stream_in *in = get_stream_in_by_patch_handle_l(adev, patch_handle); + if (in != NULL) { + pthread_mutex_lock(&in->lock); + in->device = AUDIO_DEVICE_NONE; + pthread_mutex_unlock(&in->lock); + in->patch_handle = AUDIO_PATCH_HANDLE_NONE; + pthread_mutex_unlock(&adev->lock); + return 0; + } + + pthread_mutex_unlock(&adev->lock); + ALOGW("%s() cannot find stream for patch handle: %d", __func__, patch_handle); + return -EINVAL; +} + +static int adev_close(hw_device_t *dev) { - struct alsa_audio_device *adev; + struct generic_audio_device *adev = (struct generic_audio_device *)dev; + int ret = 0; + if (!adev) + return 0; + + pthread_mutex_lock(&adev_init_lock); + + if (audio_device_ref_count == 0) { + ALOGE("adev_close called when ref_count 0"); + ret = -EINVAL; + goto error; + } + + if ((--audio_device_ref_count) == 0) { + if (adev->mixer) { + mixer_close(adev->mixer); + } + free(adev); + } - ALOGV("adev_open: %s", name); +error: + pthread_mutex_unlock(&adev_init_lock); + return ret; +} + +static int adev_open(const hw_module_t* module, const char* name, + hw_device_t** device) +{ + static struct generic_audio_device *adev; if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0) return -EINVAL; - adev = calloc(1, sizeof(struct alsa_audio_device)); - if (!adev) - return -ENOMEM; + pthread_mutex_lock(&adev_init_lock); + if (audio_device_ref_count != 0) { + *device = &adev->device.common; + audio_device_ref_count++; + ALOGV("%s: returning existing instance of adev", __func__); + ALOGV("%s: exit", __func__); + goto unlock; + } + adev = calloc(1, sizeof(struct generic_audio_device)); + + pthread_mutex_init(&adev->lock, (const pthread_mutexattr_t *) NULL); + + adev->device.common.tag = HARDWARE_DEVICE_TAG; + adev->device.common.version = AUDIO_DEVICE_API_VERSION_3_0; + adev->device.common.module = (struct hw_module_t *) module; + adev->device.common.close = adev_close; + + adev->device.init_check = adev_init_check; // no op + adev->device.set_voice_volume = adev_set_voice_volume; // no op + adev->device.set_master_volume = adev_set_master_volume; // no op + adev->device.get_master_volume = adev_get_master_volume; // no op + adev->device.set_master_mute = adev_set_master_mute; // no op + adev->device.get_master_mute = adev_get_master_mute; // no op + adev->device.set_mode = adev_set_mode; // no op + adev->device.set_mic_mute = adev_set_mic_mute; + adev->device.get_mic_mute = adev_get_mic_mute; + adev->device.set_parameters = adev_set_parameters; // no op + adev->device.get_parameters = adev_get_parameters; // no op + adev->device.get_input_buffer_size = adev_get_input_buffer_size; + adev->device.open_output_stream = adev_open_output_stream; + adev->device.close_output_stream = adev_close_output_stream; + adev->device.open_input_stream = adev_open_input_stream; + adev->device.close_input_stream = adev_close_input_stream; + adev->device.dump = adev_dump; + adev->device.get_microphones = adev_get_microphones; + adev->device.create_audio_patch = adev_create_audio_patch; + adev->device.release_audio_patch = adev_release_audio_patch; + + *device = &adev->device.common; + + adev->next_patch_handle = AUDIO_PATCH_HANDLE_NONE; + list_init(&adev->out_streams); + list_init(&adev->in_streams); + + adev->mixer = mixer_open(PCM_CARD); + struct mixer_ctl *ctl; + + // Set default mixer ctls + // Enable channels and set volume + for (int i = 0; i < (int)mixer_get_num_ctls(adev->mixer); i++) { + ctl = mixer_get_ctl(adev->mixer, i); + ALOGD("mixer %d name %s", i, mixer_ctl_get_name(ctl)); + if (!strcmp(mixer_ctl_get_name(ctl), "Master Playback Volume") || + !strcmp(mixer_ctl_get_name(ctl), "Capture Volume")) { + for (int z = 0; z < (int)mixer_ctl_get_num_values(ctl); z++) { + ALOGD("set ctl %d to %d", z, 100); + mixer_ctl_set_percent(ctl, z, 100); + } + continue; + } + if (!strcmp(mixer_ctl_get_name(ctl), "Master Playback Switch") || + !strcmp(mixer_ctl_get_name(ctl), "Capture Switch")) { + for (int z = 0; z < (int)mixer_ctl_get_num_values(ctl); z++) { + ALOGD("set ctl %d to %d", z, 1); + mixer_ctl_set_value(ctl, z, 1); + } + continue; + } + } - adev->hw_device.common.tag = HARDWARE_DEVICE_TAG; - adev->hw_device.common.version = AUDIO_DEVICE_API_VERSION_2_0; - adev->hw_device.common.module = (struct hw_module_t *) module; - adev->hw_device.common.close = adev_close; - adev->hw_device.init_check = adev_init_check; - adev->hw_device.set_voice_volume = adev_set_voice_volume; - adev->hw_device.set_master_volume = adev_set_master_volume; - adev->hw_device.get_master_volume = adev_get_master_volume; - adev->hw_device.set_master_mute = adev_set_master_mute; - adev->hw_device.get_master_mute = adev_get_master_mute; - adev->hw_device.set_mode = adev_set_mode; - adev->hw_device.set_mic_mute = adev_set_mic_mute; - adev->hw_device.get_mic_mute = adev_get_mic_mute; - adev->hw_device.set_parameters = adev_set_parameters; - adev->hw_device.get_parameters = adev_get_parameters; - adev->hw_device.get_input_buffer_size = adev_get_input_buffer_size; - adev->hw_device.open_output_stream = adev_open_output_stream; - adev->hw_device.close_output_stream = adev_close_output_stream; - adev->hw_device.open_input_stream = adev_open_input_stream; - adev->hw_device.close_input_stream = adev_close_input_stream; - adev->hw_device.dump = adev_dump; - - adev->devices = AUDIO_DEVICE_NONE; - - *device = &adev->hw_device.common; + audio_device_ref_count++; +unlock: + pthread_mutex_unlock(&adev_init_lock); return 0; } @@ -684,7 +1829,7 @@ struct audio_module HAL_MODULE_INFO_SYM = { .module_api_version = AUDIO_MODULE_API_VERSION_0_1, .hal_api_version = HARDWARE_HAL_API_VERSION, .id = AUDIO_HARDWARE_MODULE_ID, - .name = "Generic Audio HAL for dragonboards", + .name = "Generic audio HW HAL", .author = "The Android Open Source Project", .methods = &hal_module_methods, }, diff --git a/db845c/BoardConfig.mk b/db845c/BoardConfig.mk index 47602f6..816c465 100644 --- a/db845c/BoardConfig.mk +++ b/db845c/BoardConfig.mk @@ -12,6 +12,7 @@ BOARD_KERNEL_BASE := 0x80000000 BOARD_KERNEL_PAGESIZE := 2048 BOARD_KERNEL_CMDLINE := earlycon firmware_class.path=/vendor/firmware/ androidboot.hardware=db845c BOARD_KERNEL_CMDLINE += init=/init androidboot.boot_devices=soc@0/1d84000.ufshc printk.devkmsg=on +BOARD_KERNEL_CMDLINE += deferred_probe_timeout=30 # Image Configuration BOARD_BOOTIMAGE_PARTITION_SIZE := 67108864 #64M diff --git a/device-common.mk b/device-common.mk index 7be64f9..1ebe719 100644 --- a/device-common.mk +++ b/device-common.mk @@ -121,6 +121,7 @@ PRODUCT_COPY_FILES += \ $(LOCAL_PATH)/etc/audio_policy_configuration_bluetooth_legacy_hal.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_configuration_bluetooth_legacy_hal.xml \ frameworks/av/services/audiopolicy/config/a2dp_audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/a2dp_audio_policy_configuration.xml \ frameworks/av/services/audiopolicy/config/a2dp_in_audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/a2dp_in_audio_policy_configuration.xml \ + frameworks/av/services/audiopolicy/config/primary_audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/primary_audio_policy_configuration.xml \ frameworks/av/services/audiopolicy/config/bluetooth_audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/bluetooth_audio_policy_configuration.xml \ frameworks/av/services/audiopolicy/config/r_submix_audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/r_submix_audio_policy_configuration.xml \ frameworks/av/services/audiopolicy/config/usb_audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/usb_audio_policy_configuration.xml \ @@ -130,6 +131,7 @@ PRODUCT_COPY_FILES += \ # Copy media codecs config file PRODUCT_COPY_FILES += \ $(LOCAL_PATH)/etc/media_codecs.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs.xml \ + frameworks/av/media/libeffects/data/audio_effects.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_effects.xml \ frameworks/av/media/libstagefright/data/media_codecs_google_video.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_google_video.xml \ frameworks/av/media/libstagefright/data/media_codecs_google_audio.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_google_audio.xml @@ -165,6 +167,7 @@ PRODUCT_COPY_FILES += \ frameworks/native/data/etc/android.software.backup.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.backup.xml \ frameworks/native/data/etc/android.software.voice_recognizers.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.voice_recognizers.xml \ frameworks/native/data/etc/android.hardware.ethernet.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.ethernet.xml \ + frameworks/native/data/etc/android.software.print.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.print.xml \ frameworks/native/data/etc/android.hardware.usb.accessory.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.usb.accessory.xml \ frameworks/native/data/etc/android.hardware.usb.host.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.usb.host.xml \ frameworks/native/data/etc/android.software.device_admin.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.device_admin.xml diff --git a/manifest.xml b/manifest.xml index 0ff0eb2..fee2945 100644 --- a/manifest.xml +++ b/manifest.xml @@ -27,6 +27,24 @@ </interface> </hal> <hal format="hidl"> + <name>android.hardware.bluetooth.a2dp</name> + <transport>hwbinder</transport> + <version>1.0</version> + <interface> + <name>IBluetoothAudioOffload</name> + <instance>default</instance> + </interface> + </hal> + <hal format="hidl"> + <name>android.hardware.bluetooth.audio</name> + <transport>hwbinder</transport> + <version>2.0</version> + <interface> + <name>IBluetoothAudioProvidersFactory</name> + <instance>default</instance> + </interface> + </hal> + <hal format="hidl"> <name>android.hardware.configstore</name> <transport>hwbinder</transport> <version>1.1</version> diff --git a/sepolicy/te_macros b/sepolicy/te_macros index 322827a..3123f6b 100644 --- a/sepolicy/te_macros +++ b/sepolicy/te_macros @@ -5,4 +5,5 @@ define(`gpu_access', ` allow $1 dri_device:dir { open read search }; allow $1 sysfs_gpu:dir search; allow $1 sysfs_gpu:file { getattr open read }; +dontaudit $1 graphics_device:chr_file getattr; ') |