Migrate goldfish audio HAL to HIDL
Bug: 157158109
Test: atest VtsHalAudioV6_0TargetTest
Test: atest VtsHalAudioEffectV6_0TargetTest
Change-Id: I5f6749c533fed40a99b10e915112c68f9dc0519d
Merged-In: I5f6749c533fed40a99b10e915112c68f9dc0519d
Signed-off-by: Roman Kiryanov <rkir@google.com>
diff --git a/audio/Android.bp b/audio/Android.bp
index 28c563a..4b80fd7 100644
--- a/audio/Android.bp
+++ b/audio/Android.bp
@@ -14,24 +14,38 @@
// limitations under the License.
cc_library_shared {
- name: "audio.primary.goldfish",
+ name: "android.hardware.audio@6.0-impl.ranchu",
vendor: true,
+ vintf_fragments: ["android.hardware.audio@6.0-impl.ranchu.xml"],
relative_install_path: "hw",
+ defaults: ["hidl_defaults"],
+ srcs: [
+ "entry.cpp",
+ "device_factory.cpp",
+ "primary_device.cpp",
+ "stream_common.cpp",
+ "stream_in.cpp",
+ "stream_out.cpp",
+ "io_thread.cpp",
+ "talsa.cpp",
+ "util.cpp",
+ ],
shared_libs: [
+ "android.hardware.audio@6.0",
+ "android.hardware.audio.common@6.0",
+ "android.hardware.audio.common@6.0-util",
+ "libbase",
"libcutils",
+ "libhidlbase",
"liblog",
"libtinyalsa",
- ],
- srcs: [
- "audio_hw.c",
- "audio_vbuffer.c",
- "ext_pcm.c",
+ "libutils",
+ "libfmq",
],
header_libs: [
- "libhardware_headers",
- "libcutils_headers",
+ "libaudio_system_headers",
],
cflags: [
- "-Wno-unused-parameter"
+ "-DLOG_TAG=\"android.hardware.audio@6.0-impl.ranchu\"",
],
}
diff --git a/audio/android.hardware.audio@6.0-impl.ranchu.xml b/audio/android.hardware.audio@6.0-impl.ranchu.xml
new file mode 100644
index 0000000..b0a2aaa
--- /dev/null
+++ b/audio/android.hardware.audio@6.0-impl.ranchu.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+ <hal format="hidl">
+ <name>android.hardware.audio</name>
+ <transport>hwbinder</transport>
+ <version>6.0</version>
+ <interface>
+ <name>IDevicesFactory</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/audio/audio_hw.c b/audio/audio_hw.c
deleted file mode 100644
index 28991b6..0000000
--- a/audio/audio_hw.c
+++ /dev/null
@@ -1,1450 +0,0 @@
-/*
- * 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.
- * 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.
- */
-
-#define LOG_TAG "audio_hw_generic"
-
-#include <assert.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <math.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/time.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include <log/log.h>
-#include <cutils/properties.h>
-#include <cutils/str_parms.h>
-
-#include <hardware/hardware.h>
-#include <system/audio.h>
-
-#include "audio_hw.h"
-#include "ext_pcm.h"
-
-#define PCM_CARD 0
-#define PCM_DEVICE 0
-
-#define OUT_PERIOD_MS 15
-#define OUT_PERIOD_COUNT 4
-
-#define IN_PERIOD_MS 15
-#define IN_PERIOD_COUNT 4
-
-#define _bool_str(x) ((x)?"true":"false")
-
-#define PROP_KEY_SIMULATE_MULTI_ZONE_AUDIO "ro.aae.simulateMultiZoneAudio"
-
-static int adev_get_mic_mute(const struct audio_hw_device *dev, bool *state);
-
-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 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) {
- return -ENOSYS;
-}
-
-static size_t out_get_buffer_size(const struct audio_stream *stream) {
- 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 size;
-}
-
-static audio_channel_mask_t out_get_channels(const struct audio_stream *stream) {
- 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) {
- 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) {
- return -ENOSYS;
-}
-
-static int out_dump(const struct audio_stream *stream, int fd) {
- struct generic_stream_out *out = (struct generic_stream_out *)stream;
- pthread_mutex_lock(&out->lock);
- dprintf(fd, "\tout_dump:\n"
- "\t\taddress: %s\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\tamplitude ratio: %f\n"
- "\t\tenabled channels: %d\n"
- "\t\taudio dev: %p\n\n",
- out->bus_address,
- out_get_sample_rate(stream),
- out_get_buffer_size(stream),
- out_get_channels(stream),
- out_get_format(stream),
- out->device,
- out->amplitude_ratio,
- out->enabled_channels,
- out->dev);
- pthread_mutex_unlock(&out->lock);
- return 0;
-}
-
-static int out_set_parameters(struct audio_stream *stream, const char *kvpairs) {
- struct generic_stream_out *out = (struct generic_stream_out *)stream;
- struct str_parms *parms;
- char value[32];
- int ret;
- long val;
- char *end;
-
- pthread_mutex_lock(&out->lock);
- if (!out->standby) {
- //Do not support changing params while stream running
- ret = -ENOSYS;
- } else {
- parms = str_parms_create_str(kvpairs);
- ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
- value, sizeof(value));
- if (ret >= 0) {
- errno = 0;
- val = strtol(value, &end, 10);
- if (errno == 0 && (end != NULL) && (*end == '\0') && ((int)val == val)) {
- out->device = (int)val;
- ret = 0;
- } else {
- ret = -EINVAL;
- }
- }
- str_parms_destroy(parms);
- }
- pthread_mutex_unlock(&out->lock);
- return ret;
-}
-
-static char *out_get_parameters(const struct audio_stream *stream, const char *keys) {
- struct generic_stream_out *out = (struct generic_stream_out *)stream;
- struct str_parms *query = str_parms_create_str(keys);
- char *str;
- char value[256];
- struct str_parms *reply = str_parms_create();
- int ret;
-
- ret = str_parms_get_str(query, AUDIO_PARAMETER_STREAM_ROUTING, value, sizeof(value));
- if (ret >= 0) {
- pthread_mutex_lock(&out->lock);
- str_parms_add_int(reply, AUDIO_PARAMETER_STREAM_ROUTING, out->device);
- pthread_mutex_unlock(&out->lock);
- str = strdup(str_parms_to_str(reply));
- } else {
- str = strdup(keys);
- }
-
- str_parms_destroy(query);
- str_parms_destroy(reply);
- return str;
-}
-
-static uint32_t out_get_latency(const struct audio_stream_out *stream) {
- 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) {
- return -ENOSYS;
-}
-
-static void *out_write_worker(void *args) {
- struct generic_stream_out *out = (struct generic_stream_out *)args;
- struct ext_pcm *ext_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 (ext_pcm) {
- ext_pcm_close(ext_pcm); // Frees pcm
- ext_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:%s not in standby before exiting", out->bus_address);
- }
- 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 (!ext_pcm) {
- ext_pcm = ext_pcm_open(PCM_CARD, PCM_DEVICE,
- PCM_OUT | PCM_MONOTONIC, &out->pcm_config);
- if (!ext_pcm_is_ready(ext_pcm)) {
- ALOGE("pcm_open(out) failed: %s: address %s channels %d format %d rate %d",
- ext_pcm_get_error(ext_pcm),
- out->bus_address,
- 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 = ext_pcm_frames_to_bytes(ext_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 write_error = ext_pcm_write(ext_pcm, out->bus_address,
- buffer, ext_pcm_frames_to_bytes(ext_pcm, frames));
- if (write_error) {
- ALOGE("pcm_write failed %s address %s", ext_pcm_get_error(ext_pcm), out->bus_address);
- restart = true;
- } else {
- ALOGV("pcm_write succeed address %s", out->bus_address);
- }
- }
- if (buffer) {
- free(buffer);
- }
-
- return NULL;
-}
-
-// 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;
- }
- 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;
- }
-}
-
-// Applies gain naively, assumes AUDIO_FORMAT_PCM_16_BIT and stereo output
-static void out_apply_gain(struct generic_stream_out *out, const void *buffer, size_t bytes) {
- int16_t *int16_buffer = (int16_t *)buffer;
- size_t int16_size = bytes / sizeof(int16_t);
- for (int i = 0; i < int16_size; i++) {
- if ((i % 2) && !(out->enabled_channels & RIGHT_CHANNEL)) {
- int16_buffer[i] = 0;
- } else if (!(i % 2) && !(out->enabled_channels & LEFT_CHANNEL)) {
- int16_buffer[i] = 0;
- } else {
- float multiplied = int16_buffer[i] * out->amplitude_ratio;
- if (multiplied > INT16_MAX) int16_buffer[i] = INT16_MAX;
- else if (multiplied < INT16_MIN) int16_buffer[i] = INT16_MIN;
- else int16_buffer[i] = (int16_t)multiplied;
- }
- }
-}
-
-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;
- ALOGV("%s: to device %s", __func__, out->bus_address);
- 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 = frames;
- if (out->dev->master_mute) {
- ALOGV("%s: ignored due to master mute", __func__);
- } else {
- out_apply_gain(out, buffer, bytes);
- 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 (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) {
- int ret = -EINVAL;
- 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) {
- 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;
-}
-
-// Must be called with out->lock held
-static void do_out_standby(struct generic_stream_out *out) {
- 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;
- }
-
- 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) {
- // 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) {
- // 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) {
- return -ENOSYS;
-}
-
-static uint32_t in_get_sample_rate(const struct audio_stream *stream) {
- 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) {
- 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 size_t get_input_buffer_size(uint32_t sample_rate, audio_format_t format,
- audio_channel_mask_t channel_mask) {
- size_t size;
- size_t device_rate;
- int channel_count = popcount(channel_mask);
- if (refine_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) {
- 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) {
- 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) {
- 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) {
- return -ENOSYS;
-}
-
-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) {
- struct generic_stream_in *in = (struct generic_stream_in *)stream;
- struct str_parms *parms;
- char value[32];
- int ret;
- long val;
- char *end;
-
- pthread_mutex_lock(&in->lock);
- if (!in->standby) {
- ret = -ENOSYS;
- } else {
- parms = str_parms_create_str(kvpairs);
-
- ret = str_parms_get_str(parms, AUDIO_PARAMETER_STREAM_ROUTING,
- value, sizeof(value));
- if (ret >= 0) {
- errno = 0;
- val = strtol(value, &end, 10);
- if ((errno == 0) && (end != NULL) && (*end == '\0') && ((int)val == val)) {
- in->device = (int)val;
- ret = 0;
- } else {
- ret = -EINVAL;
- }
- }
-
- str_parms_destroy(parms);
- }
- pthread_mutex_unlock(&in->lock);
- return ret;
-}
-
-static char *in_get_parameters(const struct audio_stream *stream, const char *keys) {
- struct generic_stream_in *in = (struct generic_stream_in *)stream;
- struct str_parms *query = str_parms_create_str(keys);
- char *str;
- char value[256];
- struct str_parms *reply = str_parms_create();
- int ret;
-
- 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);
- str = strdup(str_parms_to_str(reply));
- } else {
- str = strdup(keys);
- }
-
- str_parms_destroy(query);
- str_parms_destroy(reply);
- return str;
-}
-
-static int in_set_gain(struct audio_stream_in *stream, float gain) {
- // TODO(hwwang): support adjusting input gain
- 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;
-}
-
-#define STEP (3.14159265 / 180)
-// Generates pure tone for FM_TUNER
-static int pseudo_pcm_read(void *data, unsigned int count) {
- unsigned int length = count / sizeof(short);
- short *sdata = (short *)data;
- for (int index = 0; index < length; index++) {
- sdata[index] = (short)(sin(index * STEP) * 4096);
- }
- return count;
-}
-
-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;
- }
-
- 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) {
- 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);
- int ret = 0;
- 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;
- }
- // FM_TUNER fills the buffer via pseudo_pcm_read directly
- if (in->device != AUDIO_DEVICE_IN_FM_TUNER) {
- 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 (in->device == AUDIO_DEVICE_IN_FM_TUNER) {
- int read_bytes = pseudo_pcm_read(buffer, bytes);
- read_frames = read_bytes / audio_stream_in_frame_size(stream);
- } else 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;
-}
-
-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_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) {
- struct generic_audio_device *adev = (struct generic_audio_device *)dev;
- struct generic_stream_out *out;
- int ret = 0;
-
- 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));
-
- if (!out)
- return -ENOMEM;
-
- out->stream.common.get_sample_rate = out_get_sample_rate;
- out->stream.common.set_sample_rate = out_set_sample_rate;
- out->stream.common.get_buffer_size = out_get_buffer_size;
- out->stream.common.get_channels = out_get_channels;
- out->stream.common.get_format = out_get_format;
- out->stream.common.set_format = out_set_format;
- out->stream.common.standby = out_standby;
- out->stream.common.dump = out_dump;
- out->stream.common.set_parameters = out_set_parameters;
- out->stream.common.get_parameters = out_get_parameters;
- out->stream.common.add_audio_effect = out_add_audio_effect;
- out->stream.common.remove_audio_effect = out_remove_audio_effect;
- out->stream.get_latency = out_get_latency;
- out->stream.set_volume = out_set_volume;
- out->stream.write = out_write;
- out->stream.get_render_position = out_get_render_position;
- out->stream.get_presentation_position = out_get_presentation_position;
- out->stream.get_next_write_timestamp = out_get_next_write_timestamp;
-
- pthread_mutex_init(&out->lock, (const pthread_mutexattr_t *) NULL);
- out->dev = adev;
- out->device = 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->enabled_channels = BOTH_CHANNELS;
- if (address) {
- out->bus_address = calloc(strlen(address) + 1, sizeof(char));
- strncpy(out->bus_address, address, strlen(address));
- hashmapPut(adev->out_bus_stream_map, out->bus_address, out);
- /* TODO: read struct audio_gain from audio_policy_configuration */
- out->gain_stage = (struct audio_gain) {
- .min_value = -3200,
- .max_value = 600,
- .step_value = 100,
- };
- out->amplitude_ratio = 1.0;
- if (property_get_bool(PROP_KEY_SIMULATE_MULTI_ZONE_AUDIO, false)) {
- out->enabled_channels = strstr(out->bus_address, "rear")
- ? RIGHT_CHANNEL: LEFT_CHANNEL;
- }
- }
- *stream_out = &out->stream;
- ALOGD("%s bus:%s", __func__, out->bus_address);
-
-error:
- return ret;
-}
-
-static void adev_close_output_stream(struct audio_hw_device *dev,
- struct audio_stream_out *stream) {
- struct generic_audio_device *adev = (struct generic_audio_device *)dev;
- struct generic_stream_out *out = (struct generic_stream_out *)stream;
- ALOGD("%s bus:%s", __func__, out->bus_address);
- 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);
-
- if (out->bus_address) {
- hashmapRemove(adev->out_bus_stream_map, out->bus_address);
- free(out->bus_address);
- }
- free(stream);
-}
-
-static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs) {
- return 0;
-}
-
-static char *adev_get_parameters(const struct audio_hw_device *dev, const char *keys) {
- return NULL;
-}
-
-static int adev_init_check(const struct audio_hw_device *dev) {
- return 0;
-}
-
-static int adev_set_voice_volume(struct audio_hw_device *dev, float volume) {
- // 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) {
- return -ENOSYS;
-}
-
-static int adev_get_master_volume(struct audio_hw_device *dev, float *volume) {
- return -ENOSYS;
-}
-
-static int adev_set_master_mute(struct audio_hw_device *dev, bool muted) {
- ALOGD("%s: %s", __func__, _bool_str(muted));
- struct generic_audio_device *adev = (struct generic_audio_device *)dev;
- pthread_mutex_lock(&adev->lock);
- adev->master_mute = muted;
- pthread_mutex_unlock(&adev->lock);
- return 0;
-}
-
-static int adev_get_master_mute(struct audio_hw_device *dev, bool *muted) {
- struct generic_audio_device *adev = (struct generic_audio_device *)dev;
- pthread_mutex_lock(&adev->lock);
- *muted = adev->master_mute;
- pthread_mutex_unlock(&adev->lock);
- ALOGD("%s: %s", __func__, _bool_str(*muted));
- return 0;
-}
-
-static int adev_set_mode(struct audio_hw_device *dev, audio_mode_t 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) {
- 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) {
- 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) {
- return get_input_buffer_size(config->sample_rate, config->format, config->channel_mask);
-}
-
-static void adev_close_input_stream(struct audio_hw_device *dev,
- struct audio_stream_in *stream) {
- 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;
- }
-
- if (in->bus_address) {
- free(in->bus_address);
- }
-
- pthread_mutex_destroy(&in->lock);
- audio_vbuffer_destroy(&in->buffer);
- 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,
- audio_source_t source) {
- ALOGV("%s: audio_source_t: %d", __func__, source);
- 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; // 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; // 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; // 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; // no op
- in->stream.get_capture_position = in_get_capture_position;
-
- 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);
- }
-
- if (address) {
- in->bus_address = calloc(strlen(address) + 1, sizeof(char));
- strncpy(in->bus_address, address, strlen(address));
- }
-
- *stream_in = &in->stream;
-
-error:
- return ret;
-}
-
-static int adev_dump(const audio_hw_device_t *dev, int fd) {
- return 0;
-}
-
-static int adev_set_audio_port_config(struct audio_hw_device *dev,
- const struct audio_port_config *config) {
- int ret = 0;
- struct generic_audio_device *adev = (struct generic_audio_device *)dev;
- const char *bus_address = config->ext.device.address;
- struct generic_stream_out *out = hashmapGet(adev->out_bus_stream_map, bus_address);
- if (out) {
- pthread_mutex_lock(&out->lock);
- int gainIndex = (config->gain.values[0] - out->gain_stage.min_value) /
- out->gain_stage.step_value;
- int totalSteps = (out->gain_stage.max_value - out->gain_stage.min_value) /
- out->gain_stage.step_value;
- int minDb = out->gain_stage.min_value / 100;
- int maxDb = out->gain_stage.max_value / 100;
- // curve: 10^((minDb + (maxDb - minDb) * gainIndex / totalSteps) / 20)
- out->amplitude_ratio = pow(10,
- (minDb + (maxDb - minDb) * (gainIndex / (float)totalSteps)) / 20);
- pthread_mutex_unlock(&out->lock);
- ALOGD("%s: set audio gain: %f on %s",
- __func__, out->amplitude_ratio, bus_address);
- } else {
- ALOGE("%s: can not find output stream by bus_address:%s", __func__, bus_address);
- ret = -EINVAL;
- }
- return ret;
-}
-
-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) {
- struct generic_audio_device *audio_dev = (struct generic_audio_device *)dev;
- for (int i = 0; i < num_sources; i++) {
- ALOGD("%s: source[%d] type=%d address=%s", __func__, i, sources[i].type,
- sources[i].type == AUDIO_PORT_TYPE_DEVICE
- ? sources[i].ext.device.address
- : "");
- }
- for (int i = 0; i < num_sinks; i++) {
- ALOGD("%s: sink[%d] type=%d address=%s", __func__, i, sinks[i].type,
- sinks[i].type == AUDIO_PORT_TYPE_DEVICE ? sinks[i].ext.device.address
- : "N/A");
- }
- if (num_sources == 1 && num_sinks == 1 &&
- sources[0].type == AUDIO_PORT_TYPE_DEVICE &&
- sinks[0].type == AUDIO_PORT_TYPE_DEVICE) {
- pthread_mutex_lock(&audio_dev->lock);
- audio_dev->last_patch_id += 1;
- pthread_mutex_unlock(&audio_dev->lock);
- *handle = audio_dev->last_patch_id;
- ALOGD("%s: handle: %d", __func__, *handle);
- }
- return 0;
-}
-
-static int adev_release_audio_patch(struct audio_hw_device *dev,
- audio_patch_handle_t handle) {
- ALOGD("%s: handle: %d", __func__, handle);
- return 0;
-}
-
-static int adev_close(hw_device_t *dev) {
- 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);
- }
- if (adev->out_bus_stream_map) {
- hashmapFree(adev->out_bus_stream_map);
- }
- free(adev);
- }
-
-error:
- pthread_mutex_unlock(&adev_init_lock);
- return ret;
-}
-
-/* copied from libcutils/str_parms.c */
-static bool str_eq(void *key_a, void *key_b) {
- return !strcmp((const char *)key_a, (const char *)key_b);
-}
-
-/**
- * use djb hash unless we find it inadequate.
- * copied from libcutils/str_parms.c
- */
-#ifdef __clang__
-__attribute__((no_sanitize("integer")))
-#endif
-static int str_hash_fn(void *str) {
- uint32_t hash = 5381;
- char *p;
- for (p = str; p && *p; p++) {
- hash = ((hash << 5) + hash) + *p;
- }
- return (int)hash;
-}
-
-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;
-
- 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;
- adev->device.get_master_mute = adev_get_master_mute;
- 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;
-
- // New in AUDIO_DEVICE_API_VERSION_3_0
- adev->device.set_audio_port_config = adev_set_audio_port_config;
- adev->device.create_audio_patch = adev_create_audio_patch;
- adev->device.release_audio_patch = adev_release_audio_patch;
-
- *device = &adev->device.common;
-
- 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;
- }
- }
-
- // Initialize the bus address to output stream map
- adev->out_bus_stream_map = hashmapCreate(5, str_hash_fn, str_eq);
-
- audio_device_ref_count++;
-
-unlock:
- pthread_mutex_unlock(&adev_init_lock);
- return 0;
-}
-
-static struct hw_module_methods_t hal_module_methods = {
- .open = adev_open,
-};
-
-struct audio_module HAL_MODULE_INFO_SYM = {
- .common = {
- .tag = HARDWARE_MODULE_TAG,
- .module_api_version = AUDIO_MODULE_API_VERSION_0_1,
- .hal_api_version = HARDWARE_HAL_API_VERSION,
- .id = AUDIO_HARDWARE_MODULE_ID,
- .name = "Generic audio HW HAL",
- .author = "The Android Open Source Project",
- .methods = &hal_module_methods,
- },
-};
diff --git a/audio/audio_hw.h b/audio/audio_hw.h
deleted file mode 100644
index 903a9b8..0000000
--- a/audio/audio_hw.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#ifndef AUDIO_HW_H
-#define AUDIO_HW_H
-
-#include <pthread.h>
-
-#include <cutils/hashmap.h>
-#include <hardware/audio.h>
-#include <tinyalsa/asoundlib.h>
-
-#include "audio_vbuffer.h"
-
-struct generic_audio_device {
- struct audio_hw_device device; // Constant after init
- pthread_mutex_t lock;
- unsigned int last_patch_id; // Protected by this->lock
- bool master_mute; // Protected by this->lock
- bool mic_mute; // Protected by this->lock
- struct mixer *mixer; // Protected by this->lock
- Hashmap *out_bus_stream_map; // Extended field. Constant after init
-};
-
-enum output_channel_enable {
- LEFT_CHANNEL = 1,
- RIGHT_CHANNEL = 1 << 1,
- BOTH_CHANNELS = LEFT_CHANNEL | RIGHT_CHANNEL
-};
-
-struct generic_stream_out {
- struct audio_stream_out 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_config pcm_config; // Constant after init
- audio_vbuffer_t buffer; // Constant after init
- const char *bus_address; // Extended field. Constant after init
- struct audio_gain gain_stage; // Constant after init
- float amplitude_ratio; // Protected by this->lock
- enum output_channel_enable enabled_channels; // 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
-};
-
-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
- const char *bus_address; // Extended field. Constant after init
-
- // 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
-};
-
-#endif // AUDIO_HW_H
diff --git a/audio/audio_vbuffer.c b/audio/audio_vbuffer.c
deleted file mode 100644
index 79be545..0000000
--- a/audio/audio_vbuffer.c
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#define LOG_TAG "audio_hw_generic"
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <log/log.h>
-
-#include "audio_vbuffer.h"
-
-#define MIN(a, b) (((a) < (b)) ? (a) : (b))
-
-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;
-}
-
-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;
-}
-
-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;
-}
-
-int audio_vbuffer_dead(audio_vbuffer_t *audio_vbuffer) {
- if (!audio_vbuffer) {
- return -EINVAL;
- }
- pthread_mutex_lock(&audio_vbuffer->lock);
- int dead = audio_vbuffer->frame_count - audio_vbuffer->live;
- pthread_mutex_unlock(&audio_vbuffer->lock);
- return dead;
-}
-
-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 {
- ALOGD("%s audio_vbuffer is full", __func__);
- 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;
- }
-
- pthread_mutex_unlock(&audio_vbuffer->lock);
- return frames_written;
-}
-
-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;
- }
-
- pthread_mutex_unlock(&audio_vbuffer->lock);
- return frames_read;
-}
diff --git a/audio/audio_vbuffer.h b/audio/audio_vbuffer.h
deleted file mode 100644
index 24bf986..0000000
--- a/audio/audio_vbuffer.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#ifndef AUDIO_VBUFFER_H
-#define AUDIO_VBUFFER_H
-
-#include <pthread.h>
-
-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;
-
-int audio_vbuffer_init(audio_vbuffer_t *audio_vbuffer, size_t frame_count,
- size_t frame_size);
-
-int audio_vbuffer_destroy(audio_vbuffer_t *audio_vbuffer);
-
-int audio_vbuffer_live(audio_vbuffer_t *audio_vbuffer);
-
-int audio_vbuffer_dead(audio_vbuffer_t *audio_vbuffer);
-
-size_t audio_vbuffer_write(audio_vbuffer_t *audio_vbuffer, const void *buffer,
- size_t frame_count);
-
-size_t audio_vbuffer_read(audio_vbuffer_t *audio_vbuffer, void *buffer,
- size_t frame_count);
-
-#endif // AUDIO_VBUFFER_H
diff --git a/audio/deleters.h b/audio/deleters.h
new file mode 100644
index 0000000..882308d
--- /dev/null
+++ b/audio/deleters.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <fmq/EventFlag.h>
+#include <log/log.h>
+
+namespace deleters {
+
+struct forEventFlag {
+ typedef ::android::hardware::EventFlag EventFlag;
+
+ void operator()(EventFlag *x) const {
+ LOG_ALWAYS_FATAL_IF(EventFlag::deleteEventFlag(&x) != ::android::OK);
+ };
+};
+
+} // namespace deleters
diff --git a/audio/device_factory.cpp b/audio/device_factory.cpp
new file mode 100644
index 0000000..e0bbf1f
--- /dev/null
+++ b/audio/device_factory.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "device_factory.h"
+#include "primary_device.h"
+#include <system/audio.h>
+#include <log/log.h>
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+
+using ::android::hardware::Void;
+
+Return<void> DevicesFactory::openDevice(const hidl_string& device,
+ openDevice_cb _hidl_cb) {
+ Result result = Result::OK;
+ std::unique_ptr<IDevice> dev;
+
+ if (device == AUDIO_HARDWARE_MODULE_ID_PRIMARY) {
+ dev = std::make_unique<PrimaryDevice>();
+ } else {
+ result = Result::INVALID_ARGUMENTS;
+ }
+
+ if (!dev) {
+ ALOGE("DevicesFactory::%s:%d: failed, device='%s' result='%s'",
+ __func__, __LINE__, device.c_str(), toString(result).c_str());
+ }
+
+ _hidl_cb(result, dev.release());
+ return Void();
+}
+
+Return<void> DevicesFactory::openPrimaryDevice(openPrimaryDevice_cb _hidl_cb) {
+ _hidl_cb(Result::OK, new PrimaryDevice);
+ return Void();
+}
+
+} // namespace implementation
+} // namespace V6_0
+} // namespace audio
+} // namespace hardware
+} // namespace android
diff --git a/audio/device_factory.h b/audio/device_factory.h
new file mode 100644
index 0000000..ac653d0
--- /dev/null
+++ b/audio/device_factory.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <android/hardware/audio/6.0/IDevicesFactory.h>
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_string;
+using ::android::hardware::Return;
+using namespace ::android::hardware::audio::V6_0;
+
+struct DevicesFactory : public IDevicesFactory {
+ Return<void> openDevice(const hidl_string& device, openDevice_cb _hidl_cb) override;
+ Return<void> openPrimaryDevice(openPrimaryDevice_cb _hidl_cb) override;
+};
+
+} // namespace implementation
+} // namespace V6_0
+} // namespace audio
+} // namespace hardware
+} // namespace android
diff --git a/audio/entry.cpp b/audio/entry.cpp
new file mode 100644
index 0000000..1d48f43
--- /dev/null
+++ b/audio/entry.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "device_factory.h"
+
+using android::hardware::audio::V6_0::IDevicesFactory;
+using android::hardware::audio::V6_0::implementation::DevicesFactory;
+
+extern "C" IDevicesFactory* HIDL_FETCH_IDevicesFactory(const char* name) {
+ (void)name;
+ return new DevicesFactory();
+}
diff --git a/audio/ext_pcm.c b/audio/ext_pcm.c
deleted file mode 100644
index de2b0c4..0000000
--- a/audio/ext_pcm.c
+++ /dev/null
@@ -1,239 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#define LOG_TAG "audio_hw_generic"
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <log/log.h>
-#include <cutils/str_parms.h>
-
-#include "ext_pcm.h"
-
-static pthread_mutex_t ext_pcm_init_lock = PTHREAD_MUTEX_INITIALIZER;
-static struct ext_pcm *shared_ext_pcm = NULL;
-
-// Sleep 10ms between each mixing, this interval value is arbitrary chosen
-#define MIXER_INTERVAL_MS 10
-#define MAX(a, b) (((a) > (b)) ? (a) : (b))
-#define MIN(a, b) (((a) < (b)) ? (a) : (b))
-
-/* copied from libcutils/str_parms.c */
-static bool str_eq(void *key_a, void *key_b) {
- return !strcmp((const char *)key_a, (const char *)key_b);
-}
-
-/**
- * use djb hash unless we find it inadequate.
- * copied from libcutils/str_parms.c
- */
-#ifdef __clang__
-__attribute__((no_sanitize("integer")))
-#endif
-static int str_hash_fn(void *str) {
- uint32_t hash = 5381;
- char *p;
- for (p = str; p && *p; p++) {
- hash = ((hash << 5) + hash) + *p;
- }
- return (int)hash;
-}
-
-static bool mixer_thread_mix(__unused void *key, void *value, void *context) {
- struct ext_mixer_pipeline *pipeline_out = (struct ext_mixer_pipeline *)context;
- struct ext_mixer_pipeline *pipeline_in = (struct ext_mixer_pipeline *)value;
-
- const unsigned in_pos = pipeline_in->position;
- const unsigned out_pos = pipeline_out->position;
- const unsigned mix_pos = MIN(out_pos, in_pos);
- const int16_t* in = pipeline_in->buffer;
- int16_t* out = pipeline_out->buffer;
- int16_t* out_end = out + mix_pos;
-
- switch (mix_pos % 8) {
- case 7: goto rem7;
- case 6: goto rem6;
- case 5: goto rem5;
- case 4: goto rem4;
- case 3: goto rem3;
- case 2: goto rem2;
- case 1: goto rem1;
- case 0: break;
- }
-
-#define ONE_ITER \
- mixed = *in + *out; \
- clamper = \
- (mixed > INT16_MAX) * (mixed - INT16_MAX) + \
- (mixed < INT16_MIN) * (mixed - INT16_MIN); \
- *out = mixed - clamper; \
- ++in; \
- ++out
-
- while (out < out_end) {
- int32_t mixed;
- int32_t clamper;
-
- ONE_ITER;
- rem7: ONE_ITER;
- rem6: ONE_ITER;
- rem5: ONE_ITER;
- rem4: ONE_ITER;
- rem3: ONE_ITER;
- rem2: ONE_ITER;
- rem1: ONE_ITER;
- }
-
-#undef ONE_ITER
-
- if (in_pos > out_pos) {
- memcpy(&pipeline_out->buffer[out_pos],
- &pipeline_in->buffer[out_pos],
- sizeof(pipeline_in->buffer[0]) * (in_pos - out_pos));
- pipeline_out->position = in_pos;
- }
-
- pipeline_in->position = 0;
- return true;
-}
-
-static void *mixer_thread_loop(void *context) {
- ALOGD("%s: __enter__", __func__);
- struct ext_pcm *ext_pcm = (struct ext_pcm *)context;
- do {
- pthread_mutex_lock(&ext_pcm->mixer_lock);
- ext_pcm->mixer_pipeline.position = 0;
- // Combine the output from every pipeline into one output buffer
- hashmapForEach(ext_pcm->mixer_pipeline_map, mixer_thread_mix,
- &ext_pcm->mixer_pipeline);
- if (ext_pcm->mixer_pipeline.position > 0) {
- pcm_write(ext_pcm->pcm, (void *)ext_pcm->mixer_pipeline.buffer,
- ext_pcm->mixer_pipeline.position * sizeof(int16_t));
- }
- ext_pcm->mixer_pipeline.position = 0;
- pthread_mutex_unlock(&ext_pcm->mixer_lock);
- usleep(MIXER_INTERVAL_MS * 1000);
- } while (1);
-}
-
-static int mixer_pipeline_write(struct ext_pcm *ext_pcm, const char *bus_address,
- const void *data, unsigned int count) {
- pthread_mutex_lock(&ext_pcm->mixer_lock);
- struct ext_mixer_pipeline *pipeline = hashmapGet(
- ext_pcm->mixer_pipeline_map, bus_address);
- if (!pipeline) {
- pipeline = calloc(1, sizeof(struct ext_mixer_pipeline));
- hashmapPut(ext_pcm->mixer_pipeline_map, bus_address, pipeline);
- }
- unsigned int byteCount = MIN(count,
- (MIXER_BUFFER_SIZE - pipeline->position) * sizeof(int16_t));
- unsigned int int16Count = byteCount / sizeof(int16_t);
- if (int16Count > 0) {
- memcpy(&pipeline->buffer[pipeline->position], data, byteCount);
- pipeline->position += int16Count;
- }
- pthread_mutex_unlock(&ext_pcm->mixer_lock);
- return 0;
-}
-
-struct ext_pcm *ext_pcm_open(unsigned int card, unsigned int device,
- unsigned int flags, struct pcm_config *config) {
- pthread_mutex_lock(&ext_pcm_init_lock);
- if (shared_ext_pcm == NULL) {
- shared_ext_pcm = calloc(1, sizeof(struct ext_pcm));
- pthread_mutex_init(&shared_ext_pcm->lock, (const pthread_mutexattr_t *) NULL);
- shared_ext_pcm->pcm = pcm_open(card, device, flags, config);
- pthread_mutex_init(&shared_ext_pcm->mixer_lock, (const pthread_mutexattr_t *)NULL);
- pthread_create(&shared_ext_pcm->mixer_thread, (const pthread_attr_t *)NULL,
- mixer_thread_loop, shared_ext_pcm);
- shared_ext_pcm->mixer_pipeline_map = hashmapCreate(8, str_hash_fn, str_eq);
- }
- pthread_mutex_unlock(&ext_pcm_init_lock);
-
- pthread_mutex_lock(&shared_ext_pcm->lock);
- shared_ext_pcm->ref_count += 1;
- pthread_mutex_unlock(&shared_ext_pcm->lock);
-
- return shared_ext_pcm;
-}
-
-static bool mixer_free_pipeline(__unused void *key, void *value, void *context) {
- struct ext_mixer_pipeline *pipeline = (struct ext_mixer_pipeline *)value;
- free(pipeline);
- return true;
-}
-
-int ext_pcm_close(struct ext_pcm *ext_pcm) {
- if (ext_pcm == NULL || ext_pcm->pcm == NULL) {
- return -EINVAL;
- }
-
- pthread_mutex_lock(&ext_pcm->lock);
- ext_pcm->ref_count -= 1;
- pthread_mutex_unlock(&ext_pcm->lock);
-
- pthread_mutex_lock(&ext_pcm_init_lock);
- if (ext_pcm->ref_count <= 0) {
- pthread_mutex_destroy(&ext_pcm->lock);
- pcm_close(ext_pcm->pcm);
- pthread_mutex_destroy(&ext_pcm->mixer_lock);
- hashmapForEach(ext_pcm->mixer_pipeline_map, mixer_free_pipeline,
- (void *)NULL);
- hashmapFree(ext_pcm->mixer_pipeline_map);
- pthread_kill(ext_pcm->mixer_thread, SIGINT);
- free(ext_pcm);
- shared_ext_pcm = NULL;
- }
- pthread_mutex_unlock(&ext_pcm_init_lock);
- return 0;
-}
-
-int ext_pcm_is_ready(struct ext_pcm *ext_pcm) {
- if (ext_pcm == NULL || ext_pcm->pcm == NULL) {
- return 0;
- }
-
- return pcm_is_ready(ext_pcm->pcm);
-}
-
-int ext_pcm_write(struct ext_pcm *ext_pcm, const char *address,
- const void *data, unsigned int count) {
- if (ext_pcm == NULL || ext_pcm->pcm == NULL) {
- return -EINVAL;
- }
-
- return mixer_pipeline_write(ext_pcm, address, data, count);
-}
-
-const char *ext_pcm_get_error(struct ext_pcm *ext_pcm) {
- if (ext_pcm == NULL || ext_pcm->pcm == NULL) {
- return NULL;
- }
-
- return pcm_get_error(ext_pcm->pcm);
-}
-
-unsigned int ext_pcm_frames_to_bytes(struct ext_pcm *ext_pcm,
- unsigned int frames) {
- if (ext_pcm == NULL || ext_pcm->pcm == NULL) {
- return -EINVAL;
- }
-
- return pcm_frames_to_bytes(ext_pcm->pcm, frames);
-}
diff --git a/audio/ext_pcm.h b/audio/ext_pcm.h
deleted file mode 100644
index 0bc8df5..0000000
--- a/audio/ext_pcm.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#ifndef EXT_PCM_H
-#define EXT_PCM_H
-
-#include <pthread.h>
-
-#include <cutils/hashmap.h>
-#include <tinyalsa/asoundlib.h>
-
-// Holds up to 4KB buffer for each mixer pipeline, this value is arbitrary chosen
-#define MIXER_BUFFER_SIZE (1024 * 4)
-
-struct ext_mixer_pipeline {
- int16_t buffer[MIXER_BUFFER_SIZE];
- unsigned int position;
-};
-
-struct ext_pcm {
- struct pcm *pcm;
- pthread_mutex_t lock;
- unsigned int ref_count;
- pthread_mutex_t mixer_lock;
- struct ext_mixer_pipeline mixer_pipeline;
- pthread_t mixer_thread;
- Hashmap *mixer_pipeline_map;
-};
-
-struct ext_pcm *ext_pcm_open(unsigned int card, unsigned int device,
- unsigned int flags, struct pcm_config *config);
-int ext_pcm_close(struct ext_pcm *ext_pcm);
-int ext_pcm_is_ready(struct ext_pcm *ext_pcm);
-int ext_pcm_write(struct ext_pcm *ext_pcm, const char *bus_address,
- const void *data, unsigned int count);
-const char *ext_pcm_get_error(struct ext_pcm *ext_pcm);
-unsigned int ext_pcm_frames_to_bytes(struct ext_pcm *ext_pcm,
- unsigned int frames);
-
-#endif // EXT_PCM_H
diff --git a/audio/io_thread.cpp b/audio/io_thread.cpp
new file mode 100644
index 0000000..e26299a
--- /dev/null
+++ b/audio/io_thread.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "io_thread.h"
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+
+bool IOThread::notify(const uint32_t mask) {
+ EventFlag *evf = getEventFlag();
+ return evf ? evf->wake(mask) : false;
+}
+
+bool IOThread::standby() {
+ return notify(STAND_BY_REQUEST);
+}
+
+void IOThread::requestExit() {
+ Thread::requestExit();
+ notify(EXIT_REQUEST);
+}
+
+} // namespace implementation
+} // namespace V6_0
+} // namespace audio
+} // namespace hardware
+} // namespace android
diff --git a/audio/io_thread.h b/audio/io_thread.h
new file mode 100644
index 0000000..355a29c
--- /dev/null
+++ b/audio/io_thread.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <utils/Thread.h>
+#include <fmq/EventFlag.h>
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+
+struct IOThread : public ::android::Thread {
+ static constexpr uint32_t STAND_BY_REQUEST = 1 << 20;
+ static constexpr uint32_t EXIT_REQUEST = 1 << 21;
+
+ IOThread() : Thread(false /*canCallJava*/) {}
+ virtual EventFlag *getEventFlag() = 0;
+ virtual bool notify(uint32_t mask);
+ virtual bool standby();
+ void requestExit() override;
+};
+
+} // namespace implementation
+} // namespace V6_0
+} // namespace audio
+} // namespace hardware
+} // namespace android
diff --git a/audio/policy/audio_policy_configuration.xml b/audio/policy/audio_policy_configuration.xml
new file mode 100644
index 0000000..f631bb0
--- /dev/null
+++ b/audio/policy/audio_policy_configuration.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <!-- version section contains a “version” tag in the form “major.minor” e.g version=”1.0” -->
+
+ <!-- Global configuration Decalaration -->
+ <globalConfiguration speaker_drc_enabled="false"/>
+
+ <modules>
+ <!-- Primary Audio HAL -->
+ <xi:include href="primary_audio_policy_configuration.xml"/>
+
+ </modules>
+ <!-- End of Modules section -->
+
+ <!-- Volume section:
+ IMPORTANT NOTE: Volume tables have been moved to engine configuration.
+ Keep it here for legacy.
+ Engine will fallback on these files if none are provided by engine.
+ -->
+
+ <xi:include href="audio_policy_volumes.xml"/>
+ <xi:include href="default_volume_tables.xml"/>
+
+ <!-- End of Volume section -->
+
+</audioPolicyConfiguration>
diff --git a/audio/policy/primary_audio_policy_configuration.xml b/audio/policy/primary_audio_policy_configuration.xml
new file mode 100644
index 0000000..eedc96b
--- /dev/null
+++ b/audio/policy/primary_audio_policy_configuration.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Default Primary Audio HAL Module Audio Policy Configuration include file -->
+<module name="primary" halVersion="2.0">
+ <attachedDevices>
+ <item>Speaker</item>
+ <item>Built-In Mic</item>
+ </attachedDevices>
+ <defaultOutputDevice>Speaker</defaultOutputDevice>
+ <mixPorts>
+ <mixPort name="primary output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="44100" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+ </mixPort>
+ <mixPort name="primary input" role="sink">
+ <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+ samplingRates="8000,16000" channelMasks="AUDIO_CHANNEL_IN_MONO"/>
+ </mixPort>
+ </mixPorts>
+ <devicePorts>
+ <devicePort tagName="Speaker" type="AUDIO_DEVICE_OUT_SPEAKER" role="sink">
+ </devicePort>
+
+ <devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
+ </devicePort>
+ </devicePorts>
+ <routes>
+ <route type="mix" sink="Speaker"
+ sources="primary output"/>
+ <route type="mix" sink="primary input"
+ sources="Built-In Mic"/>
+ </routes>
+</module>
diff --git a/audio/primary_device.cpp b/audio/primary_device.cpp
new file mode 100644
index 0000000..03b2791
--- /dev/null
+++ b/audio/primary_device.cpp
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2018 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 <log/log.h>
+#include <system/audio.h>
+#include "primary_device.h"
+#include "stream_in.h"
+#include "stream_out.h"
+#include "util.h"
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+
+constexpr size_t kInBufferDurationMs = 15;
+constexpr size_t kOutBufferDurationMs = 15;
+
+using ::android::hardware::Void;
+
+PrimaryDevice::PrimaryDevice()
+ : mMixer(talsa::mixerOpen(talsa::kPcmDevice)) {
+ if (mMixer) {
+ mMixerMasterVolumeCtl = mixer_get_ctl_by_name(mMixer.get(), "Master Playback Volume");
+ mMixerCaptureVolumeCtl = mixer_get_ctl_by_name(mMixer.get(), "Capture Volume");
+ mMixerMasterPaybackSwitchCtl = mixer_get_ctl_by_name(mMixer.get(), "Master Playback Switch");
+ mMixerCaptureSwitchCtl = mixer_get_ctl_by_name(mMixer.get(), "Capture Switch");
+
+ talsa::mixerSetPercentAll(mMixerMasterVolumeCtl, 100);
+ talsa::mixerSetPercentAll(mMixerCaptureVolumeCtl, 100);
+ talsa::mixerSetValueAll(mMixerMasterPaybackSwitchCtl, 1);
+ talsa::mixerSetValueAll(mMixerCaptureSwitchCtl, 1);
+ }
+}
+
+Return<Result> PrimaryDevice::initCheck() {
+ return mMixer ? Result::OK : Result::NOT_INITIALIZED;
+}
+
+Return<Result> PrimaryDevice::setMasterVolume(float volume) {
+ if (volume < 0 || volume > 1.0) {
+ return Result::INVALID_ARGUMENTS;
+ }
+
+ if (!mMixerMasterVolumeCtl) {
+ return Result::INVALID_STATE;
+ }
+
+ talsa::mixerSetPercentAll(mMixerMasterVolumeCtl, int(100 * volume));
+ return Result::OK;
+}
+
+Return<void> PrimaryDevice::getMasterVolume(getMasterVolume_cb _hidl_cb) {
+ if (mMixerMasterVolumeCtl) {
+ _hidl_cb(Result::OK,
+ mixer_ctl_get_percent(mMixerMasterVolumeCtl, 0) / 100.0);
+ } else {
+ _hidl_cb(Result::INVALID_STATE, 0);
+ }
+
+ return Void();
+}
+
+Return<Result> PrimaryDevice::PrimaryDevice::setMicMute(bool mute) {
+ if (mMixerCaptureSwitchCtl) {
+ talsa::mixerSetValueAll(mMixerCaptureSwitchCtl, mute ? 0 : 1);
+ return Result::OK;
+ } else {
+ return Result::INVALID_STATE;
+ }
+}
+
+Return<void> PrimaryDevice::getMicMute(getMicMute_cb _hidl_cb) {
+ if (mMixerCaptureSwitchCtl) {
+ const int value = mixer_ctl_get_value(mMixerCaptureSwitchCtl, 0);
+ _hidl_cb(Result::OK, value == 0);
+ } else {
+ _hidl_cb(Result::INVALID_STATE, 0);
+ }
+ return Void();
+}
+
+Return<Result> PrimaryDevice::setMasterMute(bool mute) {
+ if (mMixerMasterPaybackSwitchCtl) {
+ talsa::mixerSetValueAll(mMixerMasterPaybackSwitchCtl, mute ? 0 : 1);
+ return Result::OK;
+ } else {
+ return Result::INVALID_STATE;
+ }
+}
+
+Return<void> PrimaryDevice::getMasterMute(getMasterMute_cb _hidl_cb) {
+ if (mMixerMasterPaybackSwitchCtl) {
+ const int value = mixer_ctl_get_value(mMixerMasterPaybackSwitchCtl, 0);
+ _hidl_cb(Result::OK, value == 0);
+ } else {
+ _hidl_cb(Result::NOT_SUPPORTED, 0);
+ }
+
+ return Void();
+}
+
+Return<void> PrimaryDevice::getInputBufferSize(const AudioConfig& config,
+ getInputBufferSize_cb _hidl_cb) {
+ AudioConfig suggestedConfig;
+ if (util::checkAudioConfig(false, kInBufferDurationMs, config, suggestedConfig)) {
+ const size_t sz =
+ suggestedConfig.frameCount
+ * util::countChannels(suggestedConfig.channelMask)
+ * util::getBytesPerSample(suggestedConfig.format);
+
+ _hidl_cb(Result::OK, sz);
+ } else {
+ ALOGE("PrimaryDevice::%s:%d failed", __func__, __LINE__);
+ _hidl_cb(Result::INVALID_ARGUMENTS, 0);
+ }
+
+ return Void();
+}
+
+Return<void> PrimaryDevice::openOutputStream(int32_t ioHandle,
+ const DeviceAddress& device,
+ const AudioConfig& config,
+ hidl_bitfield<AudioOutputFlag> flags,
+ const SourceMetadata& sourceMetadata,
+ openOutputStream_cb _hidl_cb) {
+ AudioConfig suggestedConfig;
+ if (util::checkAudioConfig(true, kOutBufferDurationMs, config, suggestedConfig)) {
+ ++mNStreams;
+ _hidl_cb(Result::OK,
+ new StreamOut(this, &unrefDevice,
+ ioHandle, device, suggestedConfig, flags, sourceMetadata),
+ config);
+ } else {
+ ALOGE("PrimaryDevice::%s:%d failed", __func__, __LINE__);
+ _hidl_cb(Result::INVALID_ARGUMENTS, nullptr, suggestedConfig);
+ }
+
+ return Void();
+}
+
+Return<void> PrimaryDevice::openInputStream(int32_t ioHandle,
+ const DeviceAddress& device,
+ const AudioConfig& config,
+ hidl_bitfield<AudioInputFlag> flags,
+ const SinkMetadata& sinkMetadata,
+ openInputStream_cb _hidl_cb) {
+ AudioConfig suggestedConfig;
+ if (util::checkAudioConfig(false, kInBufferDurationMs, config, suggestedConfig)) {
+ ++mNStreams;
+ _hidl_cb(Result::OK,
+ new StreamIn(this, &unrefDevice,
+ ioHandle, device, suggestedConfig, flags, sinkMetadata),
+ config);
+ } else {
+ ALOGE("PrimaryDevice::%s:%d failed", __func__, __LINE__);
+ _hidl_cb(Result::INVALID_ARGUMENTS, nullptr, suggestedConfig);
+ }
+
+ return Void();
+}
+
+Return<bool> PrimaryDevice::supportsAudioPatches() {
+ return false;
+}
+
+Return<void> PrimaryDevice::createAudioPatch(const hidl_vec<AudioPortConfig>& sources,
+ const hidl_vec<AudioPortConfig>& sinks,
+ createAudioPatch_cb _hidl_cb) {
+ (void)sources;
+ (void)sinks;
+ _hidl_cb(Result::NOT_SUPPORTED, 0);
+ return Void();
+}
+
+Return<void> PrimaryDevice::updateAudioPatch(int32_t previousPatch,
+ const hidl_vec<AudioPortConfig>& sources,
+ const hidl_vec<AudioPortConfig>& sinks,
+ updateAudioPatch_cb _hidl_cb) {
+ (void)previousPatch;
+ (void)sources;
+ (void)sinks;
+ _hidl_cb(Result::NOT_SUPPORTED, 0);
+ return Void();
+}
+
+Return<Result> PrimaryDevice::releaseAudioPatch(int32_t patch) {
+ (void)patch;
+ return Result::NOT_SUPPORTED;
+}
+
+Return<void> PrimaryDevice::getAudioPort(const AudioPort& port, getAudioPort_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, port);
+ return Void();
+}
+
+Return<Result> PrimaryDevice::setAudioPortConfig(const AudioPortConfig& config) {
+ (void)config;
+ return Result::NOT_SUPPORTED;
+}
+
+Return<Result> PrimaryDevice::setScreenState(bool turnedOn) {
+ (void)turnedOn;
+ return Result::OK;
+}
+
+Return<void> PrimaryDevice::getHwAvSync(getHwAvSync_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, {});
+ return Void();
+}
+
+Return<void> PrimaryDevice::getParameters(const hidl_vec<ParameterValue>& context,
+ const hidl_vec<hidl_string>& keys,
+ getParameters_cb _hidl_cb) {
+ (void)context;
+ if (keys.size() == 0) {
+ _hidl_cb(Result::OK, {});
+ } else {
+ _hidl_cb(Result::NOT_SUPPORTED, {});
+ }
+ return Void();
+}
+
+Return<Result> PrimaryDevice::setParameters(const hidl_vec<ParameterValue>& context,
+ const hidl_vec<ParameterValue>& parameters) {
+ (void)context;
+ if (parameters.size() == 0) {
+ return Result::OK;
+ } else {
+ return Result::NOT_SUPPORTED;
+ }
+}
+
+Return<void> PrimaryDevice::getMicrophones(getMicrophones_cb _hidl_cb) {
+ _hidl_cb(Result::OK, {util::getMicrophoneInfo()});
+ return Void();
+}
+
+Return<Result> PrimaryDevice::setConnectedState(const DeviceAddress& dev_addr, bool connected) {
+ (void)dev_addr;
+ (void)connected;
+ return Result::NOT_SUPPORTED;
+}
+
+Return<Result> PrimaryDevice::close() {
+ if (mNStreams > 0) {
+ return Result::INVALID_STATE;
+ } else if (mMixer) {
+ mMixerMasterVolumeCtl = nullptr;
+ mMixerCaptureVolumeCtl = nullptr;
+ mMixerMasterPaybackSwitchCtl = nullptr;
+ mMixerCaptureSwitchCtl = nullptr;
+ mMixer.reset();
+ return Result::OK;
+ } else {
+ return Result::INVALID_STATE;
+ }
+}
+
+Return<Result> PrimaryDevice::addDeviceEffect(AudioPortHandle device, uint64_t effectId) {
+ (void)device;
+ (void)effectId;
+ return Result::NOT_SUPPORTED;
+}
+
+Return<Result> PrimaryDevice::removeDeviceEffect(AudioPortHandle device, uint64_t effectId) {
+ (void)device;
+ (void)effectId;
+ return Result::NOT_SUPPORTED;
+}
+
+Return<Result> PrimaryDevice::setVoiceVolume(float volume) {
+ return (volume >= 0 && volume <= 1.0) ? Result::OK : Result::INVALID_ARGUMENTS;
+}
+
+Return<Result> PrimaryDevice::setMode(AudioMode mode) {
+ switch (mode) {
+ case AudioMode::NORMAL:
+ case AudioMode::RINGTONE:
+ case AudioMode::IN_CALL:
+ case AudioMode::IN_COMMUNICATION:
+ return Result::OK;
+
+ default:
+ return Result::INVALID_ARGUMENTS;
+ }
+}
+
+Return<Result> PrimaryDevice::setBtScoHeadsetDebugName(const hidl_string& name) {
+ (void)name;
+ return Result::NOT_SUPPORTED;
+}
+
+Return<void> PrimaryDevice::getBtScoNrecEnabled(getBtScoNrecEnabled_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, false);
+ return Void();
+}
+
+Return<Result> PrimaryDevice::setBtScoNrecEnabled(bool enabled) {
+ (void)enabled;
+ return Result::NOT_SUPPORTED;
+}
+
+Return<void> PrimaryDevice::getBtScoWidebandEnabled(getBtScoWidebandEnabled_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, false);
+ return Void();
+}
+
+Return<Result> PrimaryDevice::setBtScoWidebandEnabled(bool enabled) {
+ (void)enabled;
+ return Result::NOT_SUPPORTED;
+}
+
+Return<void> PrimaryDevice::getTtyMode(getTtyMode_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, TtyMode::OFF);
+ return Void();
+}
+
+Return<Result> PrimaryDevice::setTtyMode(IPrimaryDevice::TtyMode mode) {
+ (void)mode;
+ return Result::NOT_SUPPORTED;
+}
+
+Return<void> PrimaryDevice::getHacEnabled(getHacEnabled_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, false);
+ return Void();
+}
+
+Return<Result> PrimaryDevice::setHacEnabled(bool enabled) {
+ (void)enabled;
+ return Result::NOT_SUPPORTED;
+}
+
+Return<void> PrimaryDevice::getBtHfpEnabled(getBtHfpEnabled_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, false);
+ return Void();
+}
+
+Return<Result> PrimaryDevice::setBtHfpEnabled(bool enabled) {
+ (void)enabled;
+ return Result::NOT_SUPPORTED;
+}
+
+Return<Result> PrimaryDevice::setBtHfpSampleRate(uint32_t sampleRateHz) {
+ (void)sampleRateHz;
+ return Result::NOT_SUPPORTED;
+}
+
+Return<Result> PrimaryDevice::setBtHfpVolume(float volume) {
+ (void)volume;
+ return Result::NOT_SUPPORTED;
+}
+
+Return<Result> PrimaryDevice::updateRotation(IPrimaryDevice::Rotation rotation) {
+ (void)rotation;
+ return Result::NOT_SUPPORTED;
+}
+
+void PrimaryDevice::unrefDevice(IDevice *dev) {
+ static_cast<PrimaryDevice *>(dev)->unrefDeviceImpl();
+}
+
+void PrimaryDevice::unrefDeviceImpl() {
+ LOG_ALWAYS_FATAL_IF(--mNStreams < 0);
+}
+
+} // namespace implementation
+} // namespace V6_0
+} // namespace audio
+} // namespace hardware
+} // namespace android
diff --git a/audio/primary_device.h b/audio/primary_device.h
new file mode 100644
index 0000000..72b5c75
--- /dev/null
+++ b/audio/primary_device.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <android/hardware/audio/6.0/IPrimaryDevice.h>
+#include <atomic>
+#include "talsa.h"
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_bitfield;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+
+using namespace ::android::hardware::audio::common::V6_0;
+using namespace ::android::hardware::audio::V6_0;
+
+struct PrimaryDevice : public IPrimaryDevice {
+ PrimaryDevice();
+
+ Return<Result> initCheck() override;
+ Return<Result> setMasterVolume(float volume) override;
+ Return<void> getMasterVolume(getMasterVolume_cb _hidl_cb) override;
+ Return<Result> setMicMute(bool mute) override;
+ Return<void> getMicMute(getMicMute_cb _hidl_cb) override;
+ Return<Result> setMasterMute(bool mute) override;
+ Return<void> getMasterMute(getMasterMute_cb _hidl_cb) override;
+ Return<void> getInputBufferSize(const AudioConfig& config,
+ getInputBufferSize_cb _hidl_cb) override;
+ Return<void> openOutputStream(int32_t ioHandle,
+ const DeviceAddress& device,
+ const AudioConfig& config,
+ hidl_bitfield<AudioOutputFlag> flags,
+ const SourceMetadata& sourceMetadata,
+ openOutputStream_cb _hidl_cb) override;
+ Return<void> openInputStream(int32_t ioHandle,
+ const DeviceAddress& device,
+ const AudioConfig& config,
+ hidl_bitfield<AudioInputFlag> flags,
+ const SinkMetadata& sinkMetadata,
+ openInputStream_cb _hidl_cb) override;
+ Return<bool> supportsAudioPatches() override;
+ Return<void> createAudioPatch(const hidl_vec<AudioPortConfig>& sources,
+ const hidl_vec<AudioPortConfig>& sinks,
+ createAudioPatch_cb _hidl_cb) override;
+ Return<void> updateAudioPatch(int32_t previousPatch,
+ const hidl_vec<AudioPortConfig>& sources,
+ const hidl_vec<AudioPortConfig>& sinks,
+ updateAudioPatch_cb _hidl_cb) override;
+ Return<Result> releaseAudioPatch(int32_t patch) override;
+ Return<void> getAudioPort(const AudioPort& port, getAudioPort_cb _hidl_cb) override;
+ Return<Result> setAudioPortConfig(const AudioPortConfig& config) override;
+ Return<Result> setScreenState(bool turnedOn) override;
+ Return<void> getHwAvSync(getHwAvSync_cb _hidl_cb) override;
+ Return<void> getParameters(const hidl_vec<ParameterValue>& context,
+ const hidl_vec<hidl_string>& keys,
+ getParameters_cb _hidl_cb) override;
+ Return<Result> setParameters(const hidl_vec<ParameterValue>& context,
+ const hidl_vec<ParameterValue>& parameters) override;
+ Return<void> getMicrophones(getMicrophones_cb _hidl_cb) override;
+ Return<Result> setConnectedState(const DeviceAddress& address, bool connected) override;
+ Return<Result> close() override;
+ Return<Result> addDeviceEffect(AudioPortHandle device, uint64_t effectId) override;
+ Return<Result> removeDeviceEffect(AudioPortHandle device, uint64_t effectId) override;
+ Return<Result> setVoiceVolume(float volume) override;
+ Return<Result> setMode(AudioMode mode) override;
+ Return<Result> setBtScoHeadsetDebugName(const hidl_string& name) override;
+ Return<void> getBtScoNrecEnabled(getBtScoNrecEnabled_cb _hidl_cb) override;
+ Return<Result> setBtScoNrecEnabled(bool enabled) override;
+ Return<void> getBtScoWidebandEnabled(getBtScoWidebandEnabled_cb _hidl_cb) override;
+ Return<Result> setBtScoWidebandEnabled(bool enabled) override;
+ Return<void> getTtyMode(getTtyMode_cb _hidl_cb) override;
+ Return<Result> setTtyMode(IPrimaryDevice::TtyMode mode) override;
+ Return<void> getHacEnabled(getHacEnabled_cb _hidl_cb) override;
+ Return<Result> setHacEnabled(bool enabled) override;
+ Return<void> getBtHfpEnabled(getBtHfpEnabled_cb _hidl_cb) override;
+ Return<Result> setBtHfpEnabled(bool enabled) override;
+ Return<Result> setBtHfpSampleRate(uint32_t sampleRateHz) override;
+ Return<Result> setBtHfpVolume(float volume) override;
+ Return<Result> updateRotation(IPrimaryDevice::Rotation rotation) override;
+
+private:
+ static void unrefDevice(IDevice*);
+ void unrefDeviceImpl();
+
+ talsa::MixerPtr mMixer;
+ talsa::mixer_ctl_t *mMixerMasterVolumeCtl = nullptr;
+ talsa::mixer_ctl_t *mMixerCaptureVolumeCtl = nullptr;
+ talsa::mixer_ctl_t *mMixerMasterPaybackSwitchCtl = nullptr;
+ talsa::mixer_ctl_t *mMixerCaptureSwitchCtl = nullptr;
+ std::atomic<int> mNStreams = 0;
+};
+
+} // namespace implementation
+} // namespace V6_0
+} // namespace audio
+} // namespace hardware
+} // namespace android
diff --git a/audio/stream_common.cpp b/audio/stream_common.cpp
new file mode 100644
index 0000000..3ac4da8
--- /dev/null
+++ b/audio/stream_common.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <log/log.h>
+#include "stream_common.h"
+#include "util.h"
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+
+using ::android::hardware::Void;
+
+StreamCommon::StreamCommon(int32_t ioHandle,
+ const DeviceAddress& device,
+ const AudioConfig& config,
+ hidl_bitfield<AudioInputFlag> flags)
+ : m_ioHandle(ioHandle)
+ , m_device(device)
+ , m_config(config)
+ , m_flags(flags)
+{}
+
+uint64_t StreamCommon::getFrameSize() const {
+ return util::countChannels(m_config.channelMask)
+ * util::getBytesPerSample(m_config.format);
+}
+
+uint64_t StreamCommon::getFrameCount() const {
+ return m_config.frameCount;
+}
+
+uint64_t StreamCommon::getBufferSize() const {
+ return getFrameSize() * getFrameCount();
+}
+
+uint32_t StreamCommon::getSampleRate() const {
+ return m_config.sampleRateHz;
+}
+
+void StreamCommon::getSupportedSampleRates(AudioFormat format,
+ IStream::getSupportedSampleRates_cb _hidl_cb) const {
+ if (m_config.format == format) {
+ _hidl_cb(Result::OK, {m_config.sampleRateHz});
+ } else {
+ _hidl_cb(Result::OK, {});
+ }
+}
+
+Result StreamCommon::setSampleRate(uint32_t sampleRateHz) const {
+ (void)sampleRateHz;
+ return Result::NOT_SUPPORTED;
+}
+
+hidl_bitfield<AudioChannelMask> StreamCommon::getChannelMask() const {
+ return m_config.channelMask;
+}
+
+void StreamCommon::getSupportedChannelMasks(AudioFormat format,
+ IStream::getSupportedChannelMasks_cb _hidl_cb) const {
+ if (m_config.format == format) {
+ _hidl_cb(Result::OK, {m_config.channelMask});
+ } else {
+ _hidl_cb(Result::OK, {});
+ }
+}
+
+Result StreamCommon::setChannelMask(hidl_bitfield<AudioChannelMask> mask) const {
+ (void)mask;
+ return Result::NOT_SUPPORTED;
+}
+
+AudioFormat StreamCommon::getFormat() const {
+ return m_config.format;
+}
+
+void StreamCommon::getSupportedFormats(IStream::getSupportedFormats_cb _hidl_cb) const {
+ _hidl_cb(Result::OK, {m_config.format});
+}
+
+Result StreamCommon::setFormat(AudioFormat format) const {
+ (void)format;
+ return Result::NOT_SUPPORTED;
+}
+
+void StreamCommon::getAudioProperties(IStream::getAudioProperties_cb _hidl_cb) const {
+ _hidl_cb(m_config.sampleRateHz, m_config.channelMask, m_config.format);
+}
+
+void StreamCommon::getDevices(IStream::getDevices_cb _hidl_cb) const {
+ _hidl_cb(Result::OK, {m_device});
+}
+
+Result StreamCommon::setDevices(const hidl_vec<DeviceAddress>& devices) const {
+ (void)devices;
+ return Result::NOT_SUPPORTED;
+}
+
+} // namespace implementation
+} // namespace V6_0
+} // namespace audio
+} // namespace hardware
+} // namespace android
diff --git a/audio/stream_common.h b/audio/stream_common.h
new file mode 100644
index 0000000..ecacee1
--- /dev/null
+++ b/audio/stream_common.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <android/hardware/audio/6.0/IStream.h>
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_bitfield;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using namespace ::android::hardware::audio::common::V6_0;
+using namespace ::android::hardware::audio::V6_0;
+
+struct StreamCommon {
+ StreamCommon(int32_t ioHandle,
+ const DeviceAddress& device,
+ const AudioConfig& config,
+ hidl_bitfield<AudioInputFlag> flags);
+
+ uint64_t getFrameSize() const;
+ uint64_t getFrameCount() const;
+ uint64_t getBufferSize() const;
+ uint32_t getSampleRate() const;
+ void getSupportedSampleRates(AudioFormat format,
+ IStream::getSupportedSampleRates_cb _hidl_cb) const;
+ Result setSampleRate(uint32_t sampleRateHz) const;
+ hidl_bitfield<AudioChannelMask> getChannelMask() const;
+ void getSupportedChannelMasks(AudioFormat format,
+ IStream::getSupportedChannelMasks_cb _hidl_cb) const;
+ Result setChannelMask(hidl_bitfield<AudioChannelMask> mask) const;
+ AudioFormat getFormat() const;
+ void getSupportedFormats(IStream::getSupportedFormats_cb _hidl_cb) const;
+ Result setFormat(AudioFormat format) const;
+ void getAudioProperties(IStream::getAudioProperties_cb _hidl_cb) const;
+ void getDevices(IStream::getDevices_cb _hidl_cb) const;
+ Result setDevices(const hidl_vec<DeviceAddress>& devices) const;
+
+ const int32_t m_ioHandle;
+ const DeviceAddress m_device;
+ const AudioConfig m_config;
+ const hidl_bitfield<AudioOutputFlag> m_flags;
+};
+
+} // namespace implementation
+} // namespace V6_0
+} // namespace audio
+} // namespace hardware
+} // namespace android
diff --git a/audio/stream_in.cpp b/audio/stream_in.cpp
new file mode 100644
index 0000000..0f87a4d
--- /dev/null
+++ b/audio/stream_in.cpp
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <log/log.h>
+#include <fmq/EventFlag.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include "stream_in.h"
+#include "deleters.h"
+#include "talsa.h"
+#include "util.h"
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+
+using ::android::hardware::Void;
+
+namespace {
+
+struct ReadThread : public IOThread {
+ typedef MessageQueue<IStreamIn::ReadParameters, kSynchronizedReadWrite> CommandMQ;
+ typedef MessageQueue<IStreamIn::ReadStatus, kSynchronizedReadWrite> StatusMQ;
+ typedef MessageQueue<uint8_t, kSynchronizedReadWrite> DataMQ;
+
+ ReadThread(StreamIn *stream,
+ const unsigned nChannels,
+ const size_t sampleRateHz,
+ const size_t frameCount,
+ const size_t bufferSize)
+ : mStream(stream)
+ , mNChannels(nChannels)
+ , mSampleRateHz(sampleRateHz)
+ , mFrameCount(frameCount)
+ , mCommandMQ(1)
+ , mStatusMQ(1)
+ , mDataMQ(bufferSize, true /* EventFlag */) {
+ if (!mCommandMQ.isValid()) {
+ ALOGE("ReadThread::%s:%d: mCommandMQ is invalid", __func__, __LINE__);
+ return;
+ }
+ if (!mDataMQ.isValid()) {
+ ALOGE("ReadThread::%s:%d: mDataMQ is invalid", __func__, __LINE__);
+ return;
+ }
+ if (!mStatusMQ.isValid()) {
+ ALOGE("ReadThread::%s:%d: mStatusMQ is invalid", __func__, __LINE__);
+ return;
+ }
+
+ status_t status;
+
+ EventFlag* rawEfGroup = nullptr;
+ status = EventFlag::createEventFlag(mDataMQ.getEventFlagWord(), &rawEfGroup);
+ if (status != OK || !rawEfGroup) {
+ ALOGE("ReadThread::%s:%d: rawEfGroup is invalid", __func__, __LINE__);
+ return;
+ } else {
+ mEfGroup.reset(rawEfGroup);
+ }
+
+ status = run("reader", PRIORITY_URGENT_AUDIO);
+ if (status != OK) {
+ ALOGE("ReadThread::%s:%d: failed to start the thread: %s",
+ __func__, __LINE__, strerror(-status));
+ }
+ }
+
+ ~ReadThread() {
+ requestExit();
+ LOG_ALWAYS_FATAL_IF(join() != OK);
+ }
+
+ EventFlag *getEventFlag() override {
+ return mEfGroup.get();
+ }
+
+ bool threadLoop() override {
+ while (!exitPending()) {
+ uint32_t efState = 0;
+ mEfGroup->wait(MessageQueueFlagBits::NOT_EMPTY | 0, &efState);
+ if (efState & EXIT_REQUEST) {
+ return false;
+ }
+
+ if (efState & STAND_BY_REQUEST) {
+ mPcm.reset();
+ mBuffer.reset();
+ }
+
+ if (efState & (MessageQueueFlagBits::NOT_EMPTY | 0)) {
+ if (!mPcm) {
+ mBuffer.reset(new uint8_t[mDataMQ.getQuantumCount()]);
+ LOG_ALWAYS_FATAL_IF(!mBuffer);
+
+ mPcm = talsa::pcmOpen(
+ talsa::kPcmCard, talsa::kPcmDevice,
+ mNChannels, mSampleRateHz, mFrameCount,
+ false /* isOut */);
+ LOG_ALWAYS_FATAL_IF(!mPcm);
+
+ mPos.reset();
+ }
+
+ processCommand();
+ }
+ }
+
+ return false; // do not restart threadLoop
+ }
+
+ void processCommand() {
+ IStreamIn::ReadParameters rParameters;
+
+ if (!mCommandMQ.read(&rParameters)) {
+ return; // Nothing to do.
+ }
+
+ IStreamIn::ReadStatus rStatus;
+ switch (rParameters.command) {
+ case IStreamIn::ReadCommand::READ:
+ rStatus = doRead(rParameters);
+ break;
+
+ case IStreamIn::ReadCommand::GET_CAPTURE_POSITION:
+ rStatus = doGetCapturePosition();
+ break;
+
+ default:
+ ALOGE("ReadThread::%s:%d: Unknown read thread command code %d",
+ __func__, __LINE__, rParameters.command);
+ rStatus.retval = Result::NOT_SUPPORTED;
+ break;
+ }
+
+ rStatus.replyTo = rParameters.command;
+
+ if (!mStatusMQ.write(&rStatus)) {
+ ALOGE("ReadThread::%s:%d: status message queue write failed", __func__, __LINE__);
+ }
+
+ mEfGroup->wake(MessageQueueFlagBits::NOT_FULL | 0);
+ }
+
+ IStreamIn::ReadStatus doRead(const IStreamIn::ReadParameters &rParameters) {
+ const size_t bytesToRead = std::min(mDataMQ.availableToWrite(),
+ static_cast<size_t>(rParameters.params.read));
+
+ IStreamIn::ReadStatus status;
+ size_t read = 0;
+ status.retval = doReadImpl(&mBuffer[0], bytesToRead, read);
+ if (status.retval == Result::OK) {
+ if (mDataMQ.write(&mBuffer[0], read)) {
+ ALOGE("ReadThread::%s:%d: mDataMQ.write failed", __func__, __LINE__);
+ }
+
+ mPos.addFrames(pcm_bytes_to_frames(mPcm.get(), read));
+ status.reply.read = read;
+ }
+
+ return status;
+ }
+
+ Result doReadImpl(uint8_t *const data, const size_t toRead, size_t &read) {
+ const int res = pcm_read(mPcm.get(), data, toRead);
+ if (res < 0) {
+ memset(data, 0, toRead);
+ read = toRead;
+
+ ALOGE("ReadThread::%s:%d pcm_read failed with %s",
+ __func__, __LINE__, strerror(-res));
+ } else if (res == 0) {
+ read = toRead;
+ } else {
+ read = res;
+ }
+
+ return Result::OK;
+ }
+
+ IStreamIn::ReadStatus doGetCapturePosition() {
+ IStreamIn::ReadStatus status;
+
+ status.retval = Result::OK;
+ nsecs_t t = 0;
+ mPos.now(mSampleRateHz, status.reply.capturePosition.frames, t);
+ status.reply.capturePosition.time = t;
+
+ return status;
+ }
+
+ StreamIn *const mStream;
+ const unsigned mNChannels;
+ const size_t mSampleRateHz;
+ const size_t mFrameCount;
+ CommandMQ mCommandMQ;
+ StatusMQ mStatusMQ;
+ DataMQ mDataMQ;
+ std::unique_ptr<EventFlag, deleters::forEventFlag> mEfGroup;
+ std::unique_ptr<uint8_t[]> mBuffer;
+ talsa::PcmPtr mPcm;
+ util::StreamPosition mPos;
+};
+
+} // namespace
+
+StreamIn::StreamIn(sp<IDevice> dev,
+ void (*unrefDevice)(IDevice*),
+ int32_t ioHandle,
+ const DeviceAddress& device,
+ const AudioConfig& config,
+ hidl_bitfield<AudioInputFlag> flags,
+ const SinkMetadata& sinkMetadata)
+ : mDev(std::move(dev))
+ , mUnrefDevice(unrefDevice)
+ , mCommon(ioHandle, device, config, flags)
+ , mSinkMetadata(sinkMetadata) {
+}
+
+StreamIn::~StreamIn() {
+ close();
+}
+
+Return<uint64_t> StreamIn::getFrameSize() {
+ return mCommon.getFrameSize();
+}
+
+Return<uint64_t> StreamIn::getFrameCount() {
+ return mCommon.getFrameCount();
+}
+
+Return<uint64_t> StreamIn::getBufferSize() {
+ return mCommon.getBufferSize();
+}
+
+Return<uint32_t> StreamIn::getSampleRate() {
+ return mCommon.getSampleRate();
+}
+
+Return<void> StreamIn::getSupportedSampleRates(AudioFormat format,
+ getSupportedSampleRates_cb _hidl_cb) {
+ mCommon.getSupportedSampleRates(format, _hidl_cb);
+ return Void();
+}
+
+Return<Result> StreamIn::setSampleRate(uint32_t sampleRateHz) {
+ return mCommon.setSampleRate(sampleRateHz);
+}
+
+Return<hidl_bitfield<AudioChannelMask>> StreamIn::getChannelMask() {
+ return mCommon.getChannelMask();
+}
+
+Return<void> StreamIn::getSupportedChannelMasks(AudioFormat format,
+ IStream::getSupportedChannelMasks_cb _hidl_cb) {
+ mCommon.getSupportedChannelMasks(format, _hidl_cb);
+ return Void();
+}
+
+Return<Result> StreamIn::setChannelMask(hidl_bitfield<AudioChannelMask> mask) {
+ return mCommon.setChannelMask(mask);
+}
+
+Return<AudioFormat> StreamIn::getFormat() {
+ return mCommon.getFormat();
+}
+
+Return<void> StreamIn::getSupportedFormats(getSupportedFormats_cb _hidl_cb) {
+ mCommon.getSupportedFormats(_hidl_cb);
+ return Void();
+}
+
+Return<Result> StreamIn::setFormat(AudioFormat format) {
+ return mCommon.setFormat(format);
+}
+
+Return<void> StreamIn::getAudioProperties(getAudioProperties_cb _hidl_cb) {
+ mCommon.getAudioProperties(_hidl_cb);
+ return Void();
+}
+
+Return<Result> StreamIn::addEffect(uint64_t effectId) {
+ (void)effectId;
+ return Result::INVALID_ARGUMENTS;
+}
+
+Return<Result> StreamIn::removeEffect(uint64_t effectId) {
+ (void)effectId;
+ return Result::INVALID_ARGUMENTS;
+}
+
+Return<Result> StreamIn::standby() {
+ if (mReadThread) {
+ LOG_ALWAYS_FATAL_IF(!mReadThread->standby());
+ }
+
+ return Result::OK;
+}
+
+Return<void> StreamIn::getDevices(getDevices_cb _hidl_cb) {
+ mCommon.getDevices(_hidl_cb);
+ return Void();
+}
+
+Return<Result> StreamIn::setDevices(const hidl_vec<DeviceAddress>& devices) {
+ return mCommon.setDevices(devices);
+}
+
+Return<void> StreamIn::getParameters(const hidl_vec<ParameterValue>& context,
+ const hidl_vec<hidl_string>& keys,
+ getParameters_cb _hidl_cb) {
+ (void)context;
+ _hidl_cb((keys.size() > 0) ? Result::NOT_SUPPORTED : Result::OK, {});
+ return Void();
+}
+
+Return<Result> StreamIn::setParameters(const hidl_vec<ParameterValue>& context,
+ const hidl_vec<ParameterValue>& parameters) {
+ (void)context;
+ return (parameters.size() > 0) ? Result::NOT_SUPPORTED : Result::OK;
+}
+
+Return<Result> StreamIn::setHwAvSync(uint32_t hwAvSync) {
+ (void)hwAvSync;
+ return Result::NOT_SUPPORTED;
+}
+
+Return<Result> StreamIn::start() {
+ return Result::NOT_SUPPORTED;
+}
+
+Return<Result> StreamIn::stop() {
+ return Result::NOT_SUPPORTED;
+}
+
+Return<void> StreamIn::createMmapBuffer(int32_t minSizeFrames,
+ createMmapBuffer_cb _hidl_cb) {
+ (void)minSizeFrames;
+ _hidl_cb(Result::NOT_SUPPORTED, {});
+ return Void();
+}
+
+Return<void> StreamIn::getMmapPosition(getMmapPosition_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, {});
+ return Void();
+}
+
+Return<Result> StreamIn::close() {
+ if (mDev) {
+ mReadThread.reset();
+ mUnrefDevice(mDev.get());
+ mDev = nullptr;
+ return Result::OK;
+ } else {
+ return Result::INVALID_STATE;
+ }
+}
+
+Return<void> StreamIn::getAudioSource(getAudioSource_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, {});
+ return Void();
+}
+
+Return<Result> StreamIn::setGain(float gain) {
+ (void)gain;
+ return Result::NOT_SUPPORTED;
+}
+
+Return<void> StreamIn::updateSinkMetadata(const SinkMetadata& sinkMetadata) {
+ (void)sinkMetadata;
+ return Void();
+}
+
+Return<void> StreamIn::prepareForReading(uint32_t frameSize,
+ uint32_t framesCount,
+ prepareForReading_cb _hidl_cb) {
+ if (!frameSize || !framesCount || frameSize > 256 || framesCount > (1u << 20)) {
+ _hidl_cb(Result::INVALID_ARGUMENTS, {}, {}, {}, {});
+ return Void();
+ }
+
+ if (mReadThread) { // INVALID_STATE if the method was already called.
+ _hidl_cb(Result::INVALID_STATE, {}, {}, {}, {});
+ return Void();
+ }
+
+ auto t = std::make_unique<ReadThread>(this,
+ util::countChannels(mCommon.getChannelMask()),
+ mCommon.getSampleRate(),
+ mCommon.getFrameCount(),
+ frameSize * framesCount);
+
+ if (t->isRunning()) {
+ _hidl_cb(Result::OK,
+ *(t->mCommandMQ.getDesc()),
+ *(t->mDataMQ.getDesc()),
+ *(t->mStatusMQ.getDesc()),
+ {.pid = getpid(), .tid = t->getTid()});
+
+ mReadThread = std::move(t);
+ } else {
+ _hidl_cb(Result::INVALID_ARGUMENTS, {}, {}, {}, {});
+ }
+
+ return Void();
+}
+
+Return<uint32_t> StreamIn::getInputFramesLost() {
+ return 0;
+}
+
+Return<void> StreamIn::getCapturePosition(getCapturePosition_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, 0, 0); // see ReadThread::doGetCapturePosition
+ return Void();
+}
+
+Return<void> StreamIn::getActiveMicrophones(getActiveMicrophones_cb _hidl_cb) {
+ _hidl_cb(Result::OK, {util::getMicrophoneInfo()});
+ return Void();
+}
+
+Return<Result> StreamIn::setMicrophoneDirection(MicrophoneDirection direction) {
+ (void)direction;
+ return Result::NOT_SUPPORTED;
+}
+
+Return<Result> StreamIn::setMicrophoneFieldDimension(float zoom) {
+ (void)zoom;
+ return Result::NOT_SUPPORTED;
+}
+
+} // namespace implementation
+} // namespace V6_0
+} // namespace audio
+} // namespace hardware
+} // namespace android
diff --git a/audio/stream_in.h b/audio/stream_in.h
new file mode 100644
index 0000000..13b28d8
--- /dev/null
+++ b/audio/stream_in.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <android/hardware/audio/6.0/IStreamIn.h>
+#include <android/hardware/audio/6.0/IDevice.h>
+#include "stream_common.h"
+#include "io_thread.h"
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_bitfield;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using namespace ::android::hardware::audio::common::V6_0;
+using namespace ::android::hardware::audio::V6_0;
+
+struct StreamIn : public IStreamIn {
+ StreamIn(sp<IDevice> dev,
+ void (*unrefDevice)(IDevice*),
+ int32_t ioHandle,
+ const DeviceAddress& device,
+ const AudioConfig& config,
+ hidl_bitfield<AudioInputFlag> flags,
+ const SinkMetadata& sinkMetadata);
+ ~StreamIn();
+
+ // IStream
+ Return<uint64_t> getFrameSize() override;
+ Return<uint64_t> getFrameCount() override;
+ Return<uint64_t> getBufferSize() override;
+ Return<uint32_t> getSampleRate() override;
+ Return<void> getSupportedSampleRates(AudioFormat format, getSupportedSampleRates_cb _hidl_cb) override;
+ Return<Result> setSampleRate(uint32_t sampleRateHz) override;
+ Return<hidl_bitfield<AudioChannelMask>> getChannelMask() override;
+ Return<void> getSupportedChannelMasks(AudioFormat format, getSupportedChannelMasks_cb _hidl_cb) override;
+ Return<Result> setChannelMask(hidl_bitfield<AudioChannelMask> mask) override;
+ Return<AudioFormat> getFormat() override;
+ Return<void> getSupportedFormats(getSupportedFormats_cb _hidl_cb) override;
+ Return<Result> setFormat(AudioFormat format) override;
+ Return<void> getAudioProperties(getAudioProperties_cb _hidl_cb) override;
+ Return<Result> addEffect(uint64_t effectId) override;
+ Return<Result> removeEffect(uint64_t effectId) override;
+ Return<Result> standby() override;
+ Return<void> getDevices(getDevices_cb _hidl_cb) override;
+ Return<Result> setDevices(const hidl_vec<DeviceAddress>& devices) override;
+ Return<Result> setHwAvSync(uint32_t hwAvSync) override;
+ Return<void> getParameters(const hidl_vec<ParameterValue>& context,
+ const hidl_vec<hidl_string>& keys,
+ getParameters_cb _hidl_cb) override;
+ Return<Result> setParameters(const hidl_vec<ParameterValue>& context,
+ const hidl_vec<ParameterValue>& parameters) override;
+ Return<Result> start() override;
+ Return<Result> stop() override;
+ Return<void> createMmapBuffer(int32_t minSizeFrames, createMmapBuffer_cb _hidl_cb) override;
+ Return<void> getMmapPosition(getMmapPosition_cb _hidl_cb) override;
+ Return<Result> close() override;
+
+ // IStreamIn
+ Return<void> getAudioSource(getAudioSource_cb _hidl_cb) override;
+ Return<Result> setGain(float gain) override;
+ Return<void> updateSinkMetadata(const SinkMetadata& sinkMetadata) override;
+ Return<void> prepareForReading(uint32_t frameSize, uint32_t framesCount,
+ prepareForReading_cb _hidl_cb) override;
+ Return<uint32_t> getInputFramesLost() override;
+ Return<void> getCapturePosition(getCapturePosition_cb _hidl_cb) override;
+ Return<void> getActiveMicrophones(getActiveMicrophones_cb _hidl_cb) override;
+ Return<Result> setMicrophoneDirection(MicrophoneDirection direction) override;
+ Return<Result> setMicrophoneFieldDimension(float zoom) override;
+
+private:
+ sp<IDevice> mDev;
+ void (* const mUnrefDevice)(IDevice*);
+ const StreamCommon mCommon;
+ const SinkMetadata mSinkMetadata;
+ std::unique_ptr<IOThread> mReadThread;
+};
+
+} // namespace implementation
+} // namespace V6_0
+} // namespace audio
+} // namespace hardware
+} // namespace android
diff --git a/audio/stream_out.cpp b/audio/stream_out.cpp
new file mode 100644
index 0000000..3e7cc80
--- /dev/null
+++ b/audio/stream_out.cpp
@@ -0,0 +1,539 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <log/log.h>
+#include <fmq/EventFlag.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <math.h>
+#include "stream_out.h"
+#include "talsa.h"
+#include "deleters.h"
+#include "util.h"
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+
+using ::android::hardware::Void;
+using namespace ::android::hardware::audio::common::V6_0;
+using namespace ::android::hardware::audio::V6_0;
+
+namespace {
+
+struct WriteThread : public IOThread {
+ typedef MessageQueue<IStreamOut::WriteCommand, kSynchronizedReadWrite> CommandMQ;
+ typedef MessageQueue<IStreamOut::WriteStatus, kSynchronizedReadWrite> StatusMQ;
+ typedef MessageQueue<uint8_t, kSynchronizedReadWrite> DataMQ;
+
+ WriteThread(StreamOut *stream,
+ const unsigned nChannels,
+ const size_t sampleRateHz,
+ const size_t frameCount,
+ const size_t mqBufferSize)
+ : mStream(stream)
+ , mNChannels(nChannels)
+ , mSampleRateHz(sampleRateHz)
+ , mFrameCount(frameCount)
+ , mCommandMQ(1)
+ , mStatusMQ(1)
+ , mDataMQ(mqBufferSize, true /* EventFlag */) {
+ if (!mCommandMQ.isValid()) {
+ ALOGE("WriteThread::%s:%d: mCommandMQ is invalid", __func__, __LINE__);
+ return;
+ }
+ if (!mDataMQ.isValid()) {
+ ALOGE("WriteThread::%s:%d: mDataMQ is invalid", __func__, __LINE__);
+ return;
+ }
+ if (!mStatusMQ.isValid()) {
+ ALOGE("WriteThread::%s:%d: mStatusMQ is invalid", __func__, __LINE__);
+ return;
+ }
+
+ status_t status;
+
+ EventFlag* rawEfGroup = nullptr;
+ status = EventFlag::createEventFlag(mDataMQ.getEventFlagWord(), &rawEfGroup);
+ if (status != OK || !rawEfGroup) {
+ ALOGE("WriteThread::%s:%d: rawEfGroup is invalid", __func__, __LINE__);
+ return;
+ } else {
+ mEfGroup.reset(rawEfGroup);
+ }
+
+ status = run("writer", PRIORITY_URGENT_AUDIO);
+ if (status != OK) {
+ ALOGE("WriteThread::%s:%d: failed to start the thread: %s",
+ __func__, __LINE__, strerror(-status));
+ }
+ }
+
+ ~WriteThread() {
+ requestExit();
+ LOG_ALWAYS_FATAL_IF(join() != OK);
+ }
+
+ EventFlag *getEventFlag() override {
+ return mEfGroup.get();
+ }
+
+ bool threadLoop() override {
+ while (!exitPending()) {
+ uint32_t efState = 0;
+ mEfGroup->wait(MessageQueueFlagBits::NOT_EMPTY | STAND_BY_REQUEST | EXIT_REQUEST,
+ &efState);
+ if (efState & EXIT_REQUEST) {
+ return false;
+ }
+
+ if (efState & STAND_BY_REQUEST) {
+ mPcm.reset();
+ mBuffer.reset();
+ }
+
+ if (efState & (MessageQueueFlagBits::NOT_EMPTY | 0)) {
+ if (!mPcm) {
+ mBuffer.reset(new uint8_t[mDataMQ.getQuantumCount()]);
+ LOG_ALWAYS_FATAL_IF(!mBuffer);
+
+ mPcm = talsa::pcmOpen(
+ talsa::kPcmCard, talsa::kPcmDevice,
+ mNChannels, mSampleRateHz, mFrameCount,
+ true /* isOut */);
+ LOG_ALWAYS_FATAL_IF(!mPcm);
+
+ mPos.reset();
+ }
+
+ processCommand();
+ }
+ }
+
+ return false; // do not restart threadLoop
+ }
+
+ void processCommand() {
+ IStreamOut::WriteCommand wCommand;
+
+ if (!mCommandMQ.read(&wCommand)) {
+ return; // Nothing to do.
+ }
+
+ IStreamOut::WriteStatus wStatus;
+ switch (wCommand) {
+ case IStreamOut::WriteCommand::WRITE:
+ wStatus = doWrite();
+ break;
+
+ case IStreamOut::WriteCommand::GET_PRESENTATION_POSITION:
+ wStatus = doGetPresentationPosition();
+ break;
+
+ case IStreamOut::WriteCommand::GET_LATENCY:
+ wStatus = doGetLatency();
+ break;
+
+ default:
+ ALOGE("WriteThread::%s:%d: Unknown write thread command code %d",
+ __func__, __LINE__, wCommand);
+ wStatus.retval = Result::NOT_SUPPORTED;
+ break;
+ }
+
+ wStatus.replyTo = wCommand;
+
+ if (!mStatusMQ.write(&wStatus)) {
+ ALOGE("status message queue write failed");
+ }
+
+ mEfGroup->wake(MessageQueueFlagBits::NOT_FULL | 0);
+ }
+
+ IStreamOut::WriteStatus doWrite() {
+ IStreamOut::WriteStatus status;
+
+ const size_t availToRead = mDataMQ.availableToRead();
+ size_t written = 0;
+ if (mDataMQ.read(&mBuffer[0], availToRead)) {
+ status.retval = doWriteImpl(&mBuffer[0], availToRead, written);
+
+ mPos.addFrames(pcm_bytes_to_frames(mPcm.get(), written));
+ status.reply.written = written;
+ } else {
+ ALOGE("WriteThread::%s:%d: mDataMQ.read failed", __func__, __LINE__);
+ status.retval = Result::OK;
+ }
+
+ return status;
+ }
+
+ IStreamOut::WriteStatus doGetPresentationPosition() {
+ IStreamOut::WriteStatus status;
+
+ status.retval = doGetPresentationPositionImpl(
+ status.reply.presentationPosition.frames,
+ status.reply.presentationPosition.timeStamp);
+
+ return status;
+ }
+
+ IStreamOut::WriteStatus doGetLatency() {
+ IStreamOut::WriteStatus status;
+
+ status.retval = Result::OK;
+ status.reply.latencyMs = mStream->getLatency();
+
+ return status;
+ }
+
+ Result doWriteImpl(const uint8_t *const data,
+ const size_t toWrite,
+ size_t &written) {
+ const int res = pcm_write(mPcm.get(), data, toWrite);
+ if (res) {
+ ALOGE("WriteThread::%s:%d: pcm_write failed with %s",
+ __func__, __LINE__, strerror(-res));
+ }
+
+ written = toWrite;
+ return Result::OK;
+ }
+
+ Result doGetPresentationPositionImpl(uint64_t &frames, TimeSpec &ts) {
+ nsecs_t t = 0;
+ mPos.now(mSampleRateHz, frames, t);
+ ts.tvSec = ns2s(t);
+ ts.tvNSec = t - s2ns(ts.tvSec);
+ return Result::OK;
+ }
+
+ StreamOut *const mStream;
+ const unsigned mNChannels;
+ const size_t mSampleRateHz;
+ const size_t mFrameCount;
+ CommandMQ mCommandMQ;
+ StatusMQ mStatusMQ;
+ DataMQ mDataMQ;
+ std::unique_ptr<EventFlag, deleters::forEventFlag> mEfGroup;
+ std::unique_ptr<uint8_t[]> mBuffer;
+ talsa::PcmPtr mPcm;
+ util::StreamPosition mPos;
+};
+
+} // namespace
+
+StreamOut::StreamOut(sp<IDevice> dev,
+ void (*unrefDevice)(IDevice*),
+ int32_t ioHandle,
+ const DeviceAddress& device,
+ const AudioConfig& config,
+ hidl_bitfield<AudioOutputFlag> flags,
+ const SourceMetadata& sourceMetadata)
+ : mDev(std::move(dev))
+ , mUnrefDevice(unrefDevice)
+ , mCommon(ioHandle, device, config, flags)
+ , mSourceMetadata(sourceMetadata) {
+}
+
+StreamOut::~StreamOut() {
+ close();
+}
+
+Return<uint64_t> StreamOut::getFrameSize() {
+ return mCommon.getFrameSize();
+}
+
+Return<uint64_t> StreamOut::getFrameCount() {
+ return mCommon.getFrameCount();
+}
+
+Return<uint64_t> StreamOut::getBufferSize() {
+ return mCommon.getBufferSize();
+}
+
+Return<uint32_t> StreamOut::getSampleRate() {
+ return mCommon.getSampleRate();
+}
+
+Return<void> StreamOut::getSupportedSampleRates(AudioFormat format,
+ getSupportedSampleRates_cb _hidl_cb) {
+ mCommon.getSupportedSampleRates(format, _hidl_cb);
+ return Void();
+}
+
+Return<Result> StreamOut::setSampleRate(uint32_t sampleRateHz) {
+ return mCommon.setSampleRate(sampleRateHz);
+}
+
+Return<hidl_bitfield<AudioChannelMask>> StreamOut::getChannelMask() {
+ return mCommon.getChannelMask();
+}
+
+Return<void> StreamOut::getSupportedChannelMasks(AudioFormat format,
+ IStream::getSupportedChannelMasks_cb _hidl_cb) {
+ mCommon.getSupportedChannelMasks(format, _hidl_cb);
+ return Void();
+}
+
+Return<Result> StreamOut::setChannelMask(hidl_bitfield<AudioChannelMask> mask) {
+ return mCommon.setChannelMask(mask);
+}
+
+Return<AudioFormat> StreamOut::getFormat() {
+ return mCommon.getFormat();
+}
+
+Return<void> StreamOut::getSupportedFormats(getSupportedFormats_cb _hidl_cb) {
+ mCommon.getSupportedFormats(_hidl_cb);
+ return Void();
+}
+
+Return<Result> StreamOut::setFormat(AudioFormat format) {
+ return mCommon.setFormat(format);
+}
+
+Return<void> StreamOut::getAudioProperties(getAudioProperties_cb _hidl_cb) {
+ mCommon.getAudioProperties(_hidl_cb);
+ return Void();
+}
+
+Return<Result> StreamOut::addEffect(uint64_t effectId) {
+ (void)effectId;
+ return Result::INVALID_ARGUMENTS;
+}
+
+Return<Result> StreamOut::removeEffect(uint64_t effectId) {
+ (void)effectId;
+ return Result::INVALID_ARGUMENTS;
+}
+
+Return<Result> StreamOut::standby() {
+ if (mWriteThread) {
+ LOG_ALWAYS_FATAL_IF(!mWriteThread->standby());
+ }
+
+ return Result::OK;
+}
+
+Return<void> StreamOut::getDevices(getDevices_cb _hidl_cb) {
+ mCommon.getDevices(_hidl_cb);
+ return Void();
+}
+
+Return<Result> StreamOut::setDevices(const hidl_vec<DeviceAddress>& devices) {
+ return mCommon.setDevices(devices);
+}
+
+Return<void> StreamOut::getParameters(const hidl_vec<ParameterValue>& context,
+ const hidl_vec<hidl_string>& keys,
+ getParameters_cb _hidl_cb) {
+ (void)context;
+ _hidl_cb((keys.size() > 0) ? Result::NOT_SUPPORTED : Result::OK, {});
+ return Void();
+}
+
+Return<Result> StreamOut::setParameters(const hidl_vec<ParameterValue>& context,
+ const hidl_vec<ParameterValue>& parameters) {
+ (void)context;
+ return (parameters.size() > 0) ? Result::NOT_SUPPORTED : Result::OK;
+}
+
+Return<Result> StreamOut::setHwAvSync(uint32_t hwAvSync) {
+ (void)hwAvSync;
+ return Result::NOT_SUPPORTED;
+}
+
+Return<Result> StreamOut::close() {
+ if (mDev) {
+ mWriteThread.reset();
+ mUnrefDevice(mDev.get());
+ mDev = nullptr;
+ return Result::OK;
+ } else {
+ return Result::INVALID_STATE;
+ }
+}
+
+Return<Result> StreamOut::start() {
+ return Result::NOT_SUPPORTED;
+}
+
+Return<Result> StreamOut::stop() {
+ return Result::NOT_SUPPORTED;
+}
+
+Return<void> StreamOut::createMmapBuffer(int32_t minSizeFrames,
+ createMmapBuffer_cb _hidl_cb) {
+ (void)minSizeFrames;
+ _hidl_cb(Result::NOT_SUPPORTED, {});
+ return Void();
+}
+
+Return<void> StreamOut::getMmapPosition(getMmapPosition_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, {});
+ return Void();
+}
+
+Return<uint32_t> StreamOut::getLatency() {
+ return mCommon.getFrameCount() * 1000 / mCommon.getSampleRate();
+}
+
+Return<Result> StreamOut::setVolume(float left, float right) {
+ (void)left;
+ (void)right;
+ return Result::NOT_SUPPORTED;
+}
+
+Return<void> StreamOut::updateSourceMetadata(const SourceMetadata& sourceMetadata) {
+ (void)sourceMetadata;
+ return Void();
+}
+
+Return<void> StreamOut::prepareForWriting(uint32_t frameSize,
+ uint32_t framesCount,
+ prepareForWriting_cb _hidl_cb) {
+ if (!frameSize || !framesCount || frameSize > 256 || framesCount > (1u << 20)) {
+ _hidl_cb(Result::INVALID_ARGUMENTS, {}, {}, {}, {});
+ return Void();
+ }
+
+ if (mWriteThread) { // INVALID_STATE if the method was already called.
+ _hidl_cb(Result::INVALID_STATE, {}, {}, {}, {});
+ return Void();
+ }
+
+ auto t = std::make_unique<WriteThread>(this,
+ util::countChannels(mCommon.getChannelMask()),
+ mCommon.getSampleRate(),
+ mCommon.getFrameCount(),
+ frameSize * framesCount);
+
+ if (t->isRunning()) {
+ _hidl_cb(Result::OK,
+ *(t->mCommandMQ.getDesc()),
+ *(t->mDataMQ.getDesc()),
+ *(t->mStatusMQ.getDesc()),
+ {.pid = getpid(), .tid = t->getTid()});
+
+ mWriteThread = std::move(t);
+ } else {
+ _hidl_cb(Result::INVALID_ARGUMENTS, {}, {}, {}, {});
+ }
+
+ return Void();
+}
+
+Return<void> StreamOut::getRenderPosition(getRenderPosition_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, 0);
+ return Void();
+}
+
+Return<void> StreamOut::getNextWriteTimestamp(getNextWriteTimestamp_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, 0);
+ return Void();
+}
+
+Return<Result> StreamOut::setCallback(const sp<IStreamOutCallback>& callback) {
+ (void)callback;
+ return Result::NOT_SUPPORTED;
+}
+
+Return<Result> StreamOut::clearCallback() {
+ return Result::NOT_SUPPORTED;
+}
+
+Return<Result> StreamOut::setEventCallback(const sp<IStreamOutEventCallback>& callback) {
+ (void)callback;
+ return Result::NOT_SUPPORTED;
+}
+
+Return<void> StreamOut::supportsPauseAndResume(supportsPauseAndResume_cb _hidl_cb) {
+ _hidl_cb(false, false);
+ return Void();
+}
+
+Return<Result> StreamOut::pause() {
+ return Result::NOT_SUPPORTED;
+}
+
+Return<Result> StreamOut::resume() {
+ return Result::NOT_SUPPORTED;
+}
+
+Return<bool> StreamOut::supportsDrain() {
+ return false;
+}
+
+Return<Result> StreamOut::drain(AudioDrain type) {
+ (void)type;
+ return Result::NOT_SUPPORTED;
+}
+
+Return<Result> StreamOut::flush() {
+ return Result::NOT_SUPPORTED;
+}
+
+Return<void> StreamOut::getPresentationPosition(getPresentationPosition_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, {}, {}); // see WriteThread::doGetPresentationPosition
+ return Void();
+}
+
+Return<Result> StreamOut::selectPresentation(int32_t presentationId,
+ int32_t programId) {
+ (void)presentationId;
+ (void)programId;
+ return Result::NOT_SUPPORTED;
+}
+
+Return<void> StreamOut::getDualMonoMode(getDualMonoMode_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, {});
+ return Void();
+}
+
+Return<Result> StreamOut::setDualMonoMode(DualMonoMode mode) {
+ (void)mode;
+ return Result::NOT_SUPPORTED;
+}
+
+Return<void> StreamOut::getAudioDescriptionMixLevel(getAudioDescriptionMixLevel_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, 0);
+ return Void();
+}
+
+Return<Result> StreamOut::setAudioDescriptionMixLevel(float leveldB) {
+ (void)leveldB;
+ return Result::NOT_SUPPORTED;
+}
+
+Return<void> StreamOut::getPlaybackRateParameters(getPlaybackRateParameters_cb _hidl_cb) {
+ _hidl_cb(Result::NOT_SUPPORTED, {});
+ return Void();
+}
+
+Return<Result> StreamOut::setPlaybackRateParameters(const PlaybackRate &playbackRate) {
+ (void)playbackRate;
+ return Result::NOT_SUPPORTED;
+}
+
+} // namespace implementation
+} // namespace V6_0
+} // namespace audio
+} // namespace hardware
+} // namespace android
diff --git a/audio/stream_out.h b/audio/stream_out.h
new file mode 100644
index 0000000..c55ad8c
--- /dev/null
+++ b/audio/stream_out.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <android/hardware/audio/6.0/IStreamOut.h>
+#include <android/hardware/audio/6.0/IDevice.h>
+#include "stream_common.h"
+#include "io_thread.h"
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::hidl_bitfield;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using namespace ::android::hardware::audio::common::V6_0;
+using namespace ::android::hardware::audio::V6_0;
+
+struct StreamOut : public IStreamOut {
+ StreamOut(sp<IDevice> dev,
+ void (*unrefDevice)(IDevice*),
+ int32_t ioHandle,
+ const DeviceAddress& device,
+ const AudioConfig& config,
+ hidl_bitfield<AudioOutputFlag> flags,
+ const SourceMetadata& sourceMetadata);
+ ~StreamOut();
+
+ // IStream
+ Return<uint64_t> getFrameSize() override;
+ Return<uint64_t> getFrameCount() override;
+ Return<uint64_t> getBufferSize() override;
+ Return<uint32_t> getSampleRate() override;
+ Return<void> getSupportedSampleRates(AudioFormat format, getSupportedSampleRates_cb _hidl_cb) override;
+ Return<Result> setSampleRate(uint32_t sampleRateHz) override;
+ Return<hidl_bitfield<AudioChannelMask>> getChannelMask() override;
+ Return<void> getSupportedChannelMasks(AudioFormat format, getSupportedChannelMasks_cb _hidl_cb) override;
+ Return<Result> setChannelMask(hidl_bitfield<AudioChannelMask> mask) override;
+ Return<AudioFormat> getFormat() override;
+ Return<void> getSupportedFormats(getSupportedFormats_cb _hidl_cb) override;
+ Return<Result> setFormat(AudioFormat format) override;
+ Return<void> getAudioProperties(getAudioProperties_cb _hidl_cb) override;
+ Return<Result> addEffect(uint64_t effectId) override;
+ Return<Result> removeEffect(uint64_t effectId) override;
+ Return<Result> standby() override;
+ Return<void> getDevices(getDevices_cb _hidl_cb) override;
+ Return<Result> setDevices(const hidl_vec<DeviceAddress>& devices) override;
+ Return<Result> setHwAvSync(uint32_t hwAvSync) override;
+ Return<void> getParameters(const hidl_vec<ParameterValue>& context,
+ const hidl_vec<hidl_string>& keys,
+ getParameters_cb _hidl_cb) override;
+ Return<Result> setParameters(const hidl_vec<ParameterValue>& context,
+ const hidl_vec<ParameterValue>& parameters) override;
+ Return<Result> start() override;
+ Return<Result> stop() override;
+ Return<void> createMmapBuffer(int32_t minSizeFrames, createMmapBuffer_cb _hidl_cb) override;
+ Return<void> getMmapPosition(getMmapPosition_cb _hidl_cb) override;
+ Return<Result> close() override;
+
+ // IStreamOut
+ Return<uint32_t> getLatency() override;
+ Return<Result> setVolume(float left, float right) override;
+ Return<void> updateSourceMetadata(const SourceMetadata& sourceMetadata) override;
+ Return<void> prepareForWriting(uint32_t frameSize, uint32_t framesCount,
+ prepareForWriting_cb _hidl_cb) override;
+ Return<void> getRenderPosition(getRenderPosition_cb _hidl_cb) override;
+ Return<void> getNextWriteTimestamp(getNextWriteTimestamp_cb _hidl_cb) override;
+ Return<Result> setCallback(const sp<IStreamOutCallback>& callback) override;
+ Return<Result> clearCallback() override;
+ Return<Result> setEventCallback(const sp<IStreamOutEventCallback>& callback) override;
+ Return<void> supportsPauseAndResume(supportsPauseAndResume_cb _hidl_cb) override;
+ Return<Result> pause() override;
+ Return<Result> resume() override;
+ Return<bool> supportsDrain() override;
+ Return<Result> drain(AudioDrain type) override;
+ Return<Result> flush() override;
+ Return<void> getPresentationPosition(getPresentationPosition_cb _hidl_cb) override;
+ Return<Result> selectPresentation(int32_t presentationId, int32_t programId) override;
+ Return<void> getDualMonoMode(getDualMonoMode_cb _hidl_cb) override;
+ Return<Result> setDualMonoMode(DualMonoMode mode) override;
+ Return<void> getAudioDescriptionMixLevel(getAudioDescriptionMixLevel_cb _hidl_cb) override;
+ Return<Result> setAudioDescriptionMixLevel(float leveldB) override;
+ Return<void> getPlaybackRateParameters(getPlaybackRateParameters_cb _hidl_cb) override;
+ Return<Result> setPlaybackRateParameters(const PlaybackRate &playbackRate) override;
+
+private:
+ sp<IDevice> mDev;
+ void (* const mUnrefDevice)(IDevice*);
+ const StreamCommon mCommon;
+ const SourceMetadata mSourceMetadata;
+ std::unique_ptr<IOThread> mWriteThread;
+};
+
+} // namespace implementation
+} // namespace V6_0
+} // namespace audio
+} // namespace hardware
+} // namespace android
diff --git a/audio/talsa.cpp b/audio/talsa.cpp
new file mode 100644
index 0000000..9fb7099
--- /dev/null
+++ b/audio/talsa.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "talsa.h"
+#include <log/log.h>
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+namespace talsa {
+
+void PcmDeleter::operator()(pcm_t *x) const {
+ LOG_ALWAYS_FATAL_IF(pcm_close(x) != 0);
+};
+
+void MixerDeleter::operator()(struct mixer *x) const {
+ mixer_close(x);
+}
+
+std::unique_ptr<pcm_t, PcmDeleter> pcmOpen(const unsigned int dev,
+ const unsigned int card,
+ const unsigned int nChannels,
+ const size_t sampleRateHz,
+ const size_t frameCount,
+ const bool isOut) {
+ struct pcm_config pcm_config;
+ memset(&pcm_config, 0, sizeof(pcm_config));
+
+ pcm_config.channels = nChannels;
+ pcm_config.rate = sampleRateHz;
+ pcm_config.period_size = frameCount; // Approx frames between interrupts
+ pcm_config.period_count = 4; // Approx interrupts per buffer
+ pcm_config.format = PCM_FORMAT_S16_LE;
+ pcm_config.start_threshold = 0;
+ pcm_config.stop_threshold = isOut ? 0 : INT_MAX;
+
+ PcmPtr pcm =
+ PcmPtr(::pcm_open(dev, card,
+ (isOut ? PCM_OUT : PCM_IN) | PCM_MONOTONIC,
+ &pcm_config));
+ if (::pcm_is_ready(pcm.get())) {
+ return pcm;
+ } else {
+ ALOGE("%s:%d pcm_open failed for sampleRateHz=%zu "
+ "frameCount=%zu with %s", __func__, __LINE__,
+ sampleRateHz, frameCount,
+ pcm_get_error(pcm.get()));
+ return nullptr;
+ }
+}
+
+MixerPtr mixerOpen(unsigned int card) {
+ return MixerPtr(::mixer_open(card));
+}
+
+void mixerSetValueAll(mixer_ctl_t *ctl, int value) {
+ const unsigned int n = mixer_ctl_get_num_values(ctl);
+ for (unsigned int i = 0; i < n; i++) {
+ mixer_ctl_set_value(ctl, i, value);
+ }
+}
+
+void mixerSetPercentAll(mixer_ctl_t *ctl, int percent) {
+ const unsigned int n = mixer_ctl_get_num_values(ctl);
+ for (unsigned int i = 0; i < n; i++) {
+ mixer_ctl_set_percent(ctl, i, percent);
+ }
+}
+
+
+} // namespace talsa
+} // namespace implementation
+} // namespace V6_0
+} // namespace audio
+} // namespace hardware
+} // namespace android
diff --git a/audio/talsa.h b/audio/talsa.h
new file mode 100644
index 0000000..0b05bfc
--- /dev/null
+++ b/audio/talsa.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <memory>
+#include <tinyalsa/asoundlib.h>
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+namespace talsa {
+
+constexpr unsigned int kPcmDevice = 0;
+constexpr unsigned int kPcmCard = 0;
+
+typedef struct pcm pcm_t;
+struct PcmDeleter { void operator()(pcm_t *x) const; };
+typedef std::unique_ptr<pcm_t, PcmDeleter> PcmPtr;
+PcmPtr pcmOpen(unsigned int dev, unsigned int card, unsigned int nChannels, size_t sampleRateHz, size_t frameCount, bool isOut);
+
+typedef struct mixer mixer_t;
+typedef struct mixer_ctl mixer_ctl_t;
+struct MixerDeleter { void operator()(struct mixer *m) const; };
+typedef std::unique_ptr<mixer_t, MixerDeleter> MixerPtr;
+MixerPtr mixerOpen(unsigned int card);
+void mixerSetValueAll(mixer_ctl_t *ctl, int value);
+void mixerSetPercentAll(mixer_ctl_t *ctl, int percent);
+
+} // namespace talsa
+} // namespace implementation
+} // namespace V6_0
+} // namespace audio
+} // namespace hardware
+} // namespace android
diff --git a/audio/util.cpp b/audio/util.cpp
new file mode 100644
index 0000000..746e37d
--- /dev/null
+++ b/audio/util.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <log/log.h>
+#include <cutils/bitops.h>
+#include <system/audio.h>
+#include "util.h"
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+namespace util {
+
+namespace {
+
+const std::array<uint32_t, 8> kSupportedRatesHz = {
+ 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+const std::array<hidl_bitfield<AudioChannelMask>, 4> kSupportedInChannelMask = {
+ AudioChannelMask::IN_LEFT | 0,
+ AudioChannelMask::IN_RIGHT | 0,
+ AudioChannelMask::IN_FRONT | 0,
+ AudioChannelMask::IN_STEREO | 0,
+};
+
+const std::array<hidl_bitfield<AudioChannelMask>, 4> kSupportedOutChannelMask = {
+ AudioChannelMask::OUT_FRONT_LEFT | 0,
+ AudioChannelMask::OUT_FRONT_RIGHT | 0,
+ AudioChannelMask::OUT_FRONT_CENTER | 0,
+ AudioChannelMask::OUT_STEREO | 0,
+};
+
+const std::array<AudioFormat, 1> kSupportedAudioFormats = {
+ AudioFormat::PCM_16_BIT,
+};
+
+bool checkSampleRateHz(uint32_t value, uint32_t &suggest) {
+ for (const uint32_t supported : kSupportedRatesHz) {
+ if (value <= supported) {
+ suggest = supported;
+ return (value == supported);
+ }
+ }
+
+ suggest = kSupportedRatesHz.back();
+ return false;
+}
+
+size_t getBufferSizeFrames(size_t duration_ms, uint32_t sample_rate) {
+ return sample_rate * duration_ms / 1000;
+}
+
+} // namespace
+
+MicrophoneInfo getMicrophoneInfo() {
+ MicrophoneInfo mic;
+
+ mic.deviceId = "mic_goldfish";
+ mic.group = 0;
+ mic.indexInTheGroup = 0;
+ mic.sensitivity = AUDIO_MICROPHONE_SENSITIVITY_UNKNOWN;
+ mic.maxSpl = AUDIO_MICROPHONE_SENSITIVITY_UNKNOWN;
+ mic.minSpl = AUDIO_MICROPHONE_SENSITIVITY_UNKNOWN;
+ mic.directionality = AudioMicrophoneDirectionality::UNKNOWN;
+ mic.position.x = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+ mic.position.y = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+ mic.position.z = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+ mic.orientation.x = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+ mic.orientation.y = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+ mic.orientation.z = AUDIO_MICROPHONE_COORDINATE_UNKNOWN;
+
+ return mic;
+}
+
+size_t countChannels(hidl_bitfield<AudioChannelMask> mask) {
+ return popcount(mask);
+}
+
+size_t getBytesPerSample(AudioFormat format) {
+ return audio_bytes_per_sample(static_cast<audio_format_t>(format));
+}
+
+bool checkAudioConfig(bool isOut,
+ size_t duration_ms,
+ const AudioConfig &cfg,
+ AudioConfig &suggested) {
+ bool valid = checkSampleRateHz(cfg.sampleRateHz, suggested.sampleRateHz);
+
+ if (isOut) {
+ if (std::find(kSupportedOutChannelMask.begin(),
+ kSupportedOutChannelMask.end(),
+ cfg.channelMask) == kSupportedOutChannelMask.end()) {
+ suggested.channelMask = AudioChannelMask::OUT_STEREO | 0;
+ valid = false;
+ } else {
+ suggested.channelMask = cfg.channelMask;
+ }
+ } else {
+ if (std::find(kSupportedInChannelMask.begin(),
+ kSupportedInChannelMask.end(),
+ cfg.channelMask) == kSupportedInChannelMask.end()) {
+ suggested.channelMask = AudioChannelMask::IN_STEREO | 0;
+ valid = false;
+ } else {
+ suggested.channelMask = cfg.channelMask;
+ }
+ }
+
+ if (std::find(kSupportedAudioFormats.begin(),
+ kSupportedAudioFormats.end(),
+ cfg.format) == kSupportedAudioFormats.end()) {
+ suggested.format = AudioFormat::PCM_16_BIT;
+ valid = false;
+ } else {
+ suggested.format = cfg.format;
+ }
+
+ suggested.offloadInfo = cfg.offloadInfo; // don't care
+
+ suggested.frameCount = (cfg.frameCount == 0)
+ ? getBufferSizeFrames(duration_ms, suggested.sampleRateHz)
+ : cfg.frameCount;
+
+ return valid;
+}
+
+StreamPosition::StreamPosition() : mTimestamp(systemTime(SYSTEM_TIME_MONOTONIC)) {}
+
+void StreamPosition::addFrames(uint64_t n) {
+ mTimestamp = systemTime(SYSTEM_TIME_MONOTONIC);
+ mFrames += n;
+}
+
+void StreamPosition::now(const size_t sampleRateHz,
+ uint64_t &frames,
+ nsecs_t ×tamp) const {
+ const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ const uint64_t deltaUs = ns2us(now - mTimestamp);
+
+ frames = mFrames + sampleRateHz * deltaUs / 1000000;
+ timestamp = now;
+}
+
+void StreamPosition::reset() {
+ *this = StreamPosition();
+}
+
+} // namespace util
+} // namespace implementation
+} // namespace V6_0
+} // namespace audio
+} // namespace hardware
+} // namespace android
diff --git a/audio/util.h b/audio/util.h
new file mode 100644
index 0000000..bfac69a
--- /dev/null
+++ b/audio/util.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+#include <array>
+#include <android/hardware/audio/common/6.0/types.h>
+#include <android/hardware/audio/6.0/types.h>
+#include <utils/Timers.h>
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V6_0 {
+namespace implementation {
+namespace util {
+
+using ::android::hardware::hidl_bitfield;
+using ::android::hardware::audio::common::V6_0::AudioFormat;
+using ::android::hardware::audio::common::V6_0::AudioChannelMask;
+using ::android::hardware::audio::common::V6_0::AudioConfig;
+using ::android::hardware::audio::V6_0::MicrophoneInfo;
+
+MicrophoneInfo getMicrophoneInfo();
+
+size_t countChannels(hidl_bitfield<AudioChannelMask>);
+size_t getBytesPerSample(AudioFormat);
+
+bool checkAudioConfig(bool isOut,
+ size_t duration_ms,
+ const AudioConfig &cfg,
+ AudioConfig &suggested);
+
+struct StreamPosition {
+ StreamPosition();
+ void addFrames(uint64_t n);
+ void now(size_t sampleRateHz, uint64_t &frames, nsecs_t ×tamp) const;
+ void reset();
+
+ uint64_t mFrames = 0;
+ nsecs_t mTimestamp;
+};
+
+} // namespace util
+} // namespace implementation
+} // namespace V6_0
+} // namespace audio
+} // namespace hardware
+} // namespace android
diff --git a/audio_policy.conf b/audio_policy.conf
deleted file mode 100644
index 0945c25..0000000
--- a/audio_policy.conf
+++ /dev/null
@@ -1,64 +0,0 @@
-#
-# Audio policy configuration for generic device builds (goldfish audio HAL - emulator)
-#
-
-# Global configuration section: lists input and output devices always present on the device
-# as well as the output device selected by default.
-# Devices are designated by a string that corresponds to the enum in audio.h
-
-global_configuration {
- attached_output_devices AUDIO_DEVICE_OUT_SPEAKER
- default_output_device AUDIO_DEVICE_OUT_SPEAKER
- attached_input_devices AUDIO_DEVICE_IN_BUILTIN_MIC|AUDIO_DEVICE_IN_REMOTE_SUBMIX
-}
-
-# audio hardware module section: contains descriptors for all audio hw modules present on the
-# device. Each hw module node is named after the corresponding hw module library base name.
-# For instance, "primary" corresponds to audio.primary.<device>.so.
-# The "primary" module is mandatory and must include at least one output with
-# AUDIO_OUTPUT_FLAG_PRIMARY flag.
-# Each module descriptor contains one or more output profile descriptors and zero or more
-# input profile descriptors. Each profile lists all the parameters supported by a given output
-# or input stream category.
-# The "channel_masks", "formats", "devices" and "flags" are specified using strings corresponding
-# to enums in audio.h and audio_policy.h. They are concatenated by use of "|" without space or "\n".
-
-audio_hw_modules {
- primary {
- outputs {
- primary {
- sampling_rates 8000|11025|16000|22050|24000|44100|48000
- channel_masks AUDIO_CHANNEL_OUT_MONO|AUDIO_CHANNEL_OUT_STEREO
- formats AUDIO_FORMAT_PCM_16_BIT
- devices AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_WIRED_HEADPHONE|AUDIO_DEVICE_OUT_WIRED_HEADSET
- flags AUDIO_OUTPUT_FLAG_PRIMARY
- }
- }
- inputs {
- primary {
- sampling_rates 8000|11025|16000|22050|44100|48000
- channel_masks AUDIO_CHANNEL_IN_MONO|AUDIO_CHANNEL_IN_STEREO
- formats AUDIO_FORMAT_PCM_16_BIT
- devices AUDIO_DEVICE_IN_BUILTIN_MIC|AUDIO_DEVICE_IN_WIRED_HEADSET
- }
- }
- }
- r_submix {
- outputs {
- submix {
- sampling_rates 48000
- channel_masks AUDIO_CHANNEL_OUT_STEREO
- formats AUDIO_FORMAT_PCM_16_BIT
- devices AUDIO_DEVICE_OUT_REMOTE_SUBMIX
- }
- }
- inputs {
- submix {
- sampling_rates 48000
- channel_masks AUDIO_CHANNEL_IN_STEREO
- formats AUDIO_FORMAT_PCM_16_BIT
- devices AUDIO_DEVICE_IN_REMOTE_SUBMIX
- }
- }
- }
-}
diff --git a/manifest.xml b/manifest.xml
index b54e735..8d8d572 100644
--- a/manifest.xml
+++ b/manifest.xml
@@ -60,15 +60,6 @@
</interface>
</hal>
<hal format="hidl">
- <name>android.hardware.audio</name>
- <transport>hwbinder</transport>
- <version>6.0</version>
- <interface>
- <name>IDevicesFactory</name>
- <instance>default</instance>
- </interface>
- </hal>
- <hal format="hidl">
<name>android.hardware.graphics.allocator</name>
<transport>hwbinder</transport>
<version>3.0</version>
diff --git a/vendor.mk b/vendor.mk
index 0a7dc2f..9a67b78 100644
--- a/vendor.mk
+++ b/vendor.mk
@@ -40,7 +40,6 @@
libgoldfish-rild \
libril-goldfish-fork \
qemu-props \
- audio.primary.goldfish \
stagefright \
fingerprint.ranchu \
android.hardware.graphics.composer@2.3-impl \
@@ -75,7 +74,7 @@
PRODUCT_PACKAGES += \
audio.r_submix.default \
android.hardware.audio.service \
- android.hardware.audio@6.0-impl:32 \
+ android.hardware.audio@6.0-impl.ranchu \
android.hardware.audio.effect@6.0-impl:32
PRODUCT_PACKAGES += \
@@ -286,13 +285,10 @@
frameworks/native/data/etc/android.software.autofill.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.autofill.xml \
frameworks/native/data/etc/android.software.verified_boot.xml:${TARGET_COPY_OUT_PRODUCT}/etc/permissions/android.software.verified_boot.xml \
frameworks/av/media/libeffects/data/audio_effects.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_effects.xml \
- device/generic/goldfish/audio_policy.conf:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy.conf \
- frameworks/av/services/audiopolicy/config/audio_policy_configuration_generic.xml:$(TARGET_COPY_OUT_VENDOR)/etc/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/r_submix_audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/r_submix_audio_policy_configuration.xml \
+ device/generic/goldfish/audio/policy/audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_configuration.xml \
+ device/generic/goldfish/audio/policy/primary_audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/primary_audio_policy_configuration.xml \
frameworks/av/services/audiopolicy/config/audio_policy_volumes.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_volumes.xml \
frameworks/av/services/audiopolicy/config/default_volume_tables.xml:$(TARGET_COPY_OUT_VENDOR)/etc/default_volume_tables.xml \
- frameworks/av/services/audiopolicy/config/surround_sound_configuration_5_0.xml:$(TARGET_COPY_OUT_VENDOR)/etc/surround_sound_configuration_5_0.xml \
device/generic/goldfish/data/etc/permissions/privapp-permissions-goldfish.xml:$(TARGET_COPY_OUT_PRODUCT)/etc/permissions/privapp-permissions-goldfish.xml \
hardware/google/camera/devices/EmulatedCamera/hwl/configs/emu_camera_back.json:$(TARGET_COPY_OUT_VENDOR)/etc/config/emu_camera_back.json \
hardware/google/camera/devices/EmulatedCamera/hwl/configs/emu_camera_front.json:$(TARGET_COPY_OUT_VENDOR)/etc/config/emu_camera_front.json \