Merge "Activity Recognition HAL."
diff --git a/include/hardware/bt_gatt_client.h b/include/hardware/bt_gatt_client.h
index baed4bd..abd2e86 100644
--- a/include/hardware/bt_gatt_client.h
+++ b/include/hardware/bt_gatt_client.h
@@ -195,7 +195,7 @@
bt_status_t (*unregister_client)(int client_if );
/** Start or stop LE device scanning */
- bt_status_t (*scan)( int client_if, bool start );
+ bt_status_t (*scan)( bool start );
/** Create a connection to a remote LE or dual-mode device */
bt_status_t (*connect)( int client_if, const bt_bdaddr_t *bd_addr,
@@ -304,6 +304,9 @@
/** Configure the MTU for a given connection */
bt_status_t (*configure_mtu)(int conn_id, int mtu);
+ /** Sets the LE scan interval and window in units of N*0.625 msec */
+ bt_status_t (*set_scan_parameters)(int scan_interval, int scan_window);
+
/** Test mode interface */
bt_status_t (*test_command)( int command, btgatt_test_params_t* params);
diff --git a/include/hardware/hdmi_cec.h b/include/hardware/hdmi_cec.h
index 1906153..0b724a1 100644
--- a/include/hardware/hdmi_cec.h
+++ b/include/hardware/hdmi_cec.h
@@ -91,7 +91,7 @@
CEC_MESSAGE_TIMER_CLEARED_STATUS = 0x043,
CEC_MESSAGE_USER_CONTROL_PRESSED = 0x44,
CEC_MESSAGE_USER_CONTROL_RELEASED = 0x45,
- CEC_MESSAGE_GET_OSD_NAME = 0x46,
+ CEC_MESSAGE_GIVE_OSD_NAME = 0x46,
CEC_MESSAGE_SET_OSD_NAME = 0x47,
CEC_MESSAGE_SET_OSD_STRING = 0x64,
CEC_MESSAGE_SET_TIMER_PROGRAM_TITLE = 0x67,
@@ -129,6 +129,12 @@
CEC_MESSAGE_VENDOR_COMMAND_WITH_ID = 0xA0,
CEC_MESSAGE_CLEAR_EXTERNAL_TIMER = 0xA1,
CEC_MESSAGE_SET_EXTERNAL_TIMER = 0xA2,
+ CEC_MESSAGE_INITIATE_ARC = 0xC0,
+ CEC_MESSAGE_REPORT_ARC_INITIATED = 0xC1,
+ CEC_MESSAGE_REPORT_ARC_TERMINATED = 0xC2,
+ CEC_MESSAGE_REQUEST_ARC_INITIATION = 0xC3,
+ CEC_MESSAGE_REQUEST_ARC_TERMINATION = 0xC4,
+ CEC_MESSAGE_TERMINATE_ARC = 0xC5,
CEC_MESSAGE_ABORT = 0xFF
};
diff --git a/modules/audio_remote_submix/audio_hw.cpp b/modules/audio_remote_submix/audio_hw.cpp
index bc60484..03f079f 100644
--- a/modules/audio_remote_submix/audio_hw.cpp
+++ b/modules/audio_remote_submix/audio_hw.cpp
@@ -23,6 +23,7 @@
#include <stdlib.h>
#include <sys/param.h>
#include <sys/time.h>
+#include <sys/limits.h>
#include <cutils/log.h>
#include <cutils/properties.h>
@@ -53,27 +54,71 @@
#define SUBMIX_ALOGE(...)
#endif // SUBMIX_VERBOSE_LOGGING
-#define MAX_PIPE_DEPTH_IN_FRAMES (1024*8)
+// NOTE: This value will be rounded up to the nearest power of 2 by MonoPipe().
+#define DEFAULT_PIPE_SIZE_IN_FRAMES (1024*8)
+// Value used to divide the MonoPipe() buffer into segments that are written to the source and
+// read from the sink. The maximum latency of the device is the size of the MonoPipe's buffer
+// the minimum latency is the MonoPipe buffer size divided by this value.
+#define DEFAULT_PIPE_PERIOD_COUNT 4
// The duration of MAX_READ_ATTEMPTS * READ_ATTEMPT_SLEEP_MS must be stricly inferior to
// the duration of a record buffer at the current record sample rate (of the device, not of
// the recording itself). Here we have:
// 3 * 5ms = 15ms < 1024 frames * 1000 / 48000 = 21.333ms
#define MAX_READ_ATTEMPTS 3
#define READ_ATTEMPT_SLEEP_MS 5 // 5ms between two read attempts when pipe is empty
-#define DEFAULT_RATE_HZ 48000 // default sample rate
+#define DEFAULT_SAMPLE_RATE_HZ 48000 // default sample rate
+// See NBAIO_Format frameworks/av/include/media/nbaio/NBAIO.h.
+#define DEFAULT_FORMAT AUDIO_FORMAT_PCM_16_BIT
+// A legacy user of this device does not close the input stream when it shuts down, which
+// results in the application opening a new input stream before closing the old input stream
+// handle it was previously using. Setting this value to 1 allows multiple clients to open
+// multiple input streams from this device. If this option is enabled, each input stream returned
+// is *the same stream* which means that readers will race to read data from these streams.
+#define ENABLE_LEGACY_INPUT_OPEN 1
+// Whether channel conversion (16-bit signed PCM mono->stereo, stereo->mono) is enabled.
+#define ENABLE_CHANNEL_CONVERSION 1
+// Common limits macros.
+#ifndef min
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#endif // min
+#ifndef max
+#define max(a, b) ((a) > (b) ? (a) : (b))
+#endif // max
+
+// Set *result_variable_ptr to true if value_to_find is present in the array array_to_search,
+// otherwise set *result_variable_ptr to false.
+#define SUBMIX_VALUE_IN_SET(value_to_find, array_to_search, result_variable_ptr) \
+ { \
+ size_t i; \
+ *(result_variable_ptr) = false; \
+ for (i = 0; i < sizeof(array_to_search) / sizeof((array_to_search)[0]); i++) { \
+ if ((value_to_find) == (array_to_search)[i]) { \
+ *(result_variable_ptr) = true; \
+ break; \
+ } \
+ } \
+ }
+
+// Configuration of the submix pipe.
struct submix_config {
- audio_format_t format;
- audio_channel_mask_t channel_mask;
- unsigned int rate; // sample rate for the device
- unsigned int period_size; // size of the audio pipe is period_size * period_count in frames
- unsigned int period_count;
+ // Channel mask field in this data structure is set to either input_channel_mask or
+ // output_channel_mask depending upon the last stream to be opened on this device.
+ struct audio_config common;
+ // Input stream and output stream channel masks. This is required since input and output
+ // channel bitfields are not equivalent.
+ audio_channel_mask_t input_channel_mask;
+ audio_channel_mask_t output_channel_mask;
+ size_t pipe_frame_size; // Number of bytes in each audio frame in the pipe.
+ size_t buffer_size_frames; // Size of the audio pipe in frames.
+ // Maximum number of frames buffered by the input and output streams.
+ size_t buffer_period_size_frames;
};
struct submix_audio_device {
struct audio_hw_device device;
- bool output_standby;
bool input_standby;
+ bool output_standby;
submix_config config;
// Pipe variables: they handle the ring buffer that "pipes" audio:
// - from the submix virtual audio output == what needs to be played
@@ -83,10 +128,16 @@
// A usecase example is one where the component capturing the audio is then sending it over
// Wifi for presentation on a remote Wifi Display device (e.g. a dongle attached to a TV, or a
// TV with Wifi Display capabilities), or to a wireless audio player.
- sp<MonoPipe> rsxSink;
+ sp<MonoPipe> rsxSink;
sp<MonoPipeReader> rsxSource;
- // device lock, also used to protect access to the audio pipe
+ // Pointers to the current input and output stream instances. rsxSink and rsxSource are
+ // destroyed if both and input and output streams are destroyed.
+ struct submix_stream_out *output;
+ struct submix_stream_in *input;
+
+ // Device lock, also used to protect access to submix_audio_device from the input and output
+ // streams.
pthread_mutex_t lock;
};
@@ -104,8 +155,76 @@
struct timespec record_start_time;
// how many frames have been requested to be read
int64_t read_counter_frames;
+
+#if ENABLE_LEGACY_INPUT_OPEN
+ // Number of references to this input stream.
+ volatile int32_t ref_count;
+#endif // ENABLE_LEGACY_INPUT_OPEN
};
+// Determine whether the specified sample rate is supported by the submix module.
+static bool sample_rate_supported(const uint32_t sample_rate)
+{
+ // Set of sample rates supported by Format_from_SR_C() frameworks/av/media/libnbaio/NAIO.cpp.
+ static const unsigned int supported_sample_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
+ };
+ bool return_value;
+ SUBMIX_VALUE_IN_SET(sample_rate, supported_sample_rates, &return_value);
+ return return_value;
+}
+
+// Determine whether the specified sample rate is supported, if it is return the specified sample
+// rate, otherwise return the default sample rate for the submix module.
+static uint32_t get_supported_sample_rate(uint32_t sample_rate)
+{
+ return sample_rate_supported(sample_rate) ? sample_rate : DEFAULT_SAMPLE_RATE_HZ;
+}
+
+// Determine whether the specified channel in mask is supported by the submix module.
+static bool channel_in_mask_supported(const audio_channel_mask_t channel_in_mask)
+{
+ // Set of channel in masks supported by Format_from_SR_C()
+ // frameworks/av/media/libnbaio/NAIO.cpp.
+ static const audio_channel_mask_t supported_channel_in_masks[] = {
+ AUDIO_CHANNEL_IN_MONO, AUDIO_CHANNEL_IN_STEREO,
+ };
+ bool return_value;
+ SUBMIX_VALUE_IN_SET(channel_in_mask, supported_channel_in_masks, &return_value);
+ return return_value;
+}
+
+// Determine whether the specified channel in mask is supported, if it is return the specified
+// channel in mask, otherwise return the default channel in mask for the submix module.
+static audio_channel_mask_t get_supported_channel_in_mask(
+ const audio_channel_mask_t channel_in_mask)
+{
+ return channel_in_mask_supported(channel_in_mask) ? channel_in_mask :
+ static_cast<audio_channel_mask_t>(AUDIO_CHANNEL_IN_STEREO);
+}
+
+// Determine whether the specified channel out mask is supported by the submix module.
+static bool channel_out_mask_supported(const audio_channel_mask_t channel_out_mask)
+{
+ // Set of channel out masks supported by Format_from_SR_C()
+ // frameworks/av/media/libnbaio/NAIO.cpp.
+ static const audio_channel_mask_t supported_channel_out_masks[] = {
+ AUDIO_CHANNEL_OUT_MONO, AUDIO_CHANNEL_OUT_STEREO,
+ };
+ bool return_value;
+ SUBMIX_VALUE_IN_SET(channel_out_mask, supported_channel_out_masks, &return_value);
+ return return_value;
+}
+
+// Determine whether the specified channel out mask is supported, if it is return the specified
+// channel out mask, otherwise return the default channel out mask for the submix module.
+static audio_channel_mask_t get_supported_channel_out_mask(
+ const audio_channel_mask_t channel_out_mask)
+{
+ return channel_out_mask_supported(channel_out_mask) ? channel_out_mask :
+ static_cast<audio_channel_mask_t>(AUDIO_CHANNEL_OUT_STEREO);
+}
+
// Get a pointer to submix_stream_out given an audio_stream_out that is embedded within the
// structure.
static struct submix_stream_out * audio_stream_out_get_submix_stream_out(
@@ -154,26 +273,232 @@
offsetof(struct submix_audio_device, device));
}
+// Get the number of channels referenced by the specified channel_mask. The channel_mask can
+// reference either input or output channels.
+uint32_t get_channel_count_from_mask(const audio_channel_mask_t channel_mask) {
+ if (audio_is_input_channel(channel_mask)) {
+ return popcount(channel_mask & AUDIO_CHANNEL_IN_ALL);
+ } else if (audio_is_output_channel(channel_mask)) {
+ return popcount(channel_mask & AUDIO_CHANNEL_OUT_ALL);
+ }
+ ALOGE("get_channel_count(): No channels specified in channel mask %x", channel_mask);
+ return 0;
+}
+
+// Compare an audio_config with input channel mask and an audio_config with output channel mask
+// returning false if they do *not* match, true otherwise.
+static bool audio_config_compare(const audio_config * const input_config,
+ const audio_config * const output_config)
+{
+#if !ENABLE_CHANNEL_CONVERSION
+ const uint32_t input_channels = get_channel_count_from_mask(input_config->channel_mask);
+ const uint32_t output_channels = get_channel_count_from_mask(output_config->channel_mask);
+ if (input_channels != output_channels) {
+ ALOGE("audio_config_compare() channel count mismatch input=%d vs. output=%d",
+ input_channels, output_channels);
+ return false;
+ }
+#endif // !ENABLE_CHANNEL_CONVERSION
+ if (input_config->sample_rate != output_config->sample_rate) {
+ ALOGE("audio_config_compare() sample rate mismatch %ul vs. %ul",
+ input_config->sample_rate, output_config->sample_rate);
+ return false;
+ }
+ if (input_config->format != output_config->format) {
+ ALOGE("audio_config_compare() format mismatch %x vs. %x",
+ input_config->format, output_config->format);
+ return false;
+ }
+ // This purposely ignores offload_info as it's not required for the submix device.
+ return true;
+}
+
+// If one doesn't exist, create a pipe for the submix audio device rsxadev of size
+// buffer_size_frames and optionally associate "in" or "out" with the submix audio device.
+static void submix_audio_device_create_pipe(struct submix_audio_device * const rsxadev,
+ const struct audio_config * const config,
+ const size_t buffer_size_frames,
+ const uint32_t buffer_period_count,
+ struct submix_stream_in * const in,
+ struct submix_stream_out * const out)
+{
+ ALOG_ASSERT(in || out);
+ ALOGV("submix_audio_device_create_pipe()");
+ pthread_mutex_lock(&rsxadev->lock);
+ // Save a reference to the specified input or output stream and the associated channel
+ // mask.
+ if (in) {
+ rsxadev->input = in;
+ rsxadev->config.input_channel_mask = config->channel_mask;
+ }
+ if (out) {
+ rsxadev->output = out;
+ rsxadev->config.output_channel_mask = config->channel_mask;
+ }
+ // If a pipe isn't associated with the device, create one.
+ if (rsxadev->rsxSink == NULL || rsxadev->rsxSource == NULL) {
+ struct submix_config * const device_config = &rsxadev->config;
+ const NBAIO_Format format = Format_from_SR_C(config->sample_rate,
+ get_channel_count_from_mask(config->channel_mask), config->format);
+ const NBAIO_Format offers[1] = {format};
+ size_t numCounterOffers = 0;
+ // Create a MonoPipe with optional blocking set to true.
+ MonoPipe* sink = new MonoPipe(buffer_size_frames, format, true /*writeCanBlock*/);
+ // Negotiation between the source and sink cannot fail as the device open operation
+ // creates both ends of the pipe using the same audio format.
+ ssize_t index = sink->negotiate(offers, 1, NULL, numCounterOffers);
+ ALOG_ASSERT(index == 0);
+ MonoPipeReader* source = new MonoPipeReader(sink);
+ numCounterOffers = 0;
+ index = source->negotiate(offers, 1, NULL, numCounterOffers);
+ ALOG_ASSERT(index == 0);
+ ALOGV("submix_audio_device_create_pipe(): created pipe");
+
+ // Save references to the source and sink.
+ ALOG_ASSERT(rsxadev->rsxSink == NULL);
+ ALOG_ASSERT(rsxadev->rsxSource == NULL);
+ rsxadev->rsxSink = sink;
+ rsxadev->rsxSource = source;
+ // Store the sanitized audio format in the device so that it's possible to determine
+ // the format of the pipe source when opening the input device.
+ memcpy(&device_config->common, config, sizeof(device_config->common));
+ device_config->buffer_size_frames = sink->maxFrames();
+ device_config->buffer_period_size_frames = device_config->buffer_size_frames /
+ buffer_period_count;
+ if (in) device_config->pipe_frame_size = audio_stream_frame_size(&in->stream.common);
+ if (out) device_config->pipe_frame_size = audio_stream_frame_size(&out->stream.common);
+ SUBMIX_ALOGV("submix_audio_device_create_pipe(): pipe frame size %zd, pipe size %zd, "
+ "period size %zd", device_config->pipe_frame_size,
+ device_config->buffer_size_frames, device_config->buffer_period_size_frames);
+ }
+ pthread_mutex_unlock(&rsxadev->lock);
+}
+
+// Release references to the sink and source. Input and output threads may maintain references
+// to these objects via StrongPointer (sp<MonoPipe> and sp<MonoPipeReader>) which they can use
+// before they shutdown.
+static void submix_audio_device_release_pipe(struct submix_audio_device * const rsxadev)
+{
+ ALOGV("submix_audio_device_release_pipe()");
+ rsxadev->rsxSink.clear();
+ rsxadev->rsxSource.clear();
+}
+
+// Remove references to the specified input and output streams. When the device no longer
+// references input and output streams destroy the associated pipe.
+static void submix_audio_device_destroy_pipe(struct submix_audio_device * const rsxadev,
+ const struct submix_stream_in * const in,
+ const struct submix_stream_out * const out)
+{
+ MonoPipe* sink;
+ pthread_mutex_lock(&rsxadev->lock);
+ ALOGV("submix_audio_device_destroy_pipe()");
+ ALOG_ASSERT(in == NULL || rsxadev->input == in);
+ ALOG_ASSERT(out == NULL || rsxadev->output == out);
+ if (in != NULL) {
+#if ENABLE_LEGACY_INPUT_OPEN
+ const_cast<struct submix_stream_in*>(in)->ref_count--;
+ if (in->ref_count == 0) {
+ rsxadev->input = NULL;
+ }
+ ALOGV("submix_audio_device_destroy_pipe(): input ref_count %d", in->ref_count);
+#else
+ rsxadev->input = NULL;
+#endif // ENABLE_LEGACY_INPUT_OPEN
+ }
+ if (out != NULL) rsxadev->output = NULL;
+ if (rsxadev->input != NULL && rsxadev->output != NULL) {
+ submix_audio_device_release_pipe(rsxadev);
+ ALOGV("submix_audio_device_destroy_pipe(): pipe destroyed");
+ }
+ pthread_mutex_unlock(&rsxadev->lock);
+}
+
+// Sanitize the user specified audio config for a submix input / output stream.
+static void submix_sanitize_config(struct audio_config * const config, const bool is_input_format)
+{
+ config->channel_mask = is_input_format ? get_supported_channel_in_mask(config->channel_mask) :
+ get_supported_channel_out_mask(config->channel_mask);
+ config->sample_rate = get_supported_sample_rate(config->sample_rate);
+ config->format = DEFAULT_FORMAT;
+}
+
+// Verify a submix input or output stream can be opened.
+static bool submix_open_validate(const struct submix_audio_device * const rsxadev,
+ pthread_mutex_t * const lock,
+ const struct audio_config * const config,
+ const bool opening_input)
+{
+ bool input_open;
+ bool output_open;
+ audio_config pipe_config;
+
+ // Query the device for the current audio config and whether input and output streams are open.
+ pthread_mutex_lock(lock);
+ output_open = rsxadev->output != NULL;
+ input_open = rsxadev->input != NULL;
+ memcpy(&pipe_config, &rsxadev->config.common, sizeof(pipe_config));
+ pthread_mutex_unlock(lock);
+
+ // If the stream is already open, don't open it again.
+ if (opening_input ? !ENABLE_LEGACY_INPUT_OPEN && input_open : output_open) {
+ ALOGE("submix_open_validate(): %s stream already open.", opening_input ? "Input" :
+ "Output");
+ return false;
+ }
+
+ SUBMIX_ALOGV("submix_open_validate(): sample rate=%d format=%x "
+ "%s_channel_mask=%x", config->sample_rate, config->format,
+ opening_input ? "in" : "out", config->channel_mask);
+
+ // If either stream is open, verify the existing audio config the pipe matches the user
+ // specified config.
+ if (input_open || output_open) {
+ const audio_config * const input_config = opening_input ? config : &pipe_config;
+ const audio_config * const output_config = opening_input ? &pipe_config : config;
+ // Get the channel mask of the open device.
+ pipe_config.channel_mask =
+ opening_input ? rsxadev->config.output_channel_mask :
+ rsxadev->config.input_channel_mask;
+ if (!audio_config_compare(input_config, output_config)) {
+ ALOGE("submix_open_validate(): Unsupported format.");
+ return false;
+ }
+ }
+ return true;
+}
+
+// Calculate the maximum size of the pipe buffer in frames for the specified stream.
+static size_t calculate_stream_pipe_size_in_frames(const struct audio_stream *stream,
+ const struct submix_config *config,
+ const size_t pipe_frames)
+{
+ const size_t stream_frame_size = audio_stream_frame_size(stream);
+ const size_t pipe_frame_size = config->pipe_frame_size;
+ const size_t max_frame_size = max(stream_frame_size, pipe_frame_size);
+ return (pipe_frames * config->pipe_frame_size) / max_frame_size;
+}
+
/* audio HAL functions */
static uint32_t out_get_sample_rate(const struct audio_stream *stream)
{
const struct submix_stream_out * const out = audio_stream_get_submix_stream_out(
const_cast<struct audio_stream *>(stream));
- uint32_t out_rate = out->dev->config.rate;
+ const uint32_t out_rate = out->dev->config.common.sample_rate;
SUBMIX_ALOGV("out_get_sample_rate() returns %u", out_rate);
return out_rate;
}
static int out_set_sample_rate(struct audio_stream *stream, uint32_t rate)
{
- if ((rate != 44100) && (rate != 48000)) {
+ if (!sample_rate_supported(rate)) {
ALOGE("out_set_sample_rate(rate=%u) rate unsupported", rate);
return -ENOSYS;
}
struct submix_stream_out * const out = audio_stream_get_submix_stream_out(stream);
SUBMIX_ALOGV("out_set_sample_rate(rate=%u)", rate);
- out->dev->config.rate = rate;
+ out->dev->config.common.sample_rate = rate;
return 0;
}
@@ -181,32 +506,37 @@
{
const struct submix_stream_out * const out = audio_stream_get_submix_stream_out(
const_cast<struct audio_stream *>(stream));
- const struct submix_config& config_out = out->dev->config;
- size_t buffer_size = config_out.period_size * popcount(config_out.channel_mask)
- * sizeof(int16_t); // only PCM 16bit
- SUBMIX_ALOGV("out_get_buffer_size() returns %u, period size=%u",
- buffer_size, config_out.period_size);
- return buffer_size;
+ const struct submix_config * const config = &out->dev->config;
+ const size_t buffer_size_frames = calculate_stream_pipe_size_in_frames(
+ stream, config, config->buffer_period_size_frames);
+ const size_t buffer_size_bytes = buffer_size_frames * audio_stream_frame_size(stream);
+ SUBMIX_ALOGV("out_get_buffer_size() returns %zu bytes, %zu frames",
+ buffer_size_bytes, buffer_size_frames);
+ return buffer_size_bytes;
}
static audio_channel_mask_t out_get_channels(const struct audio_stream *stream)
{
const struct submix_stream_out * const out = audio_stream_get_submix_stream_out(
const_cast<struct audio_stream *>(stream));
- uint32_t channels = out->dev->config.channel_mask;
- SUBMIX_ALOGV("out_get_channels() returns %08x", channels);
- return channels;
+ uint32_t channel_mask = out->dev->config.output_channel_mask;
+ SUBMIX_ALOGV("out_get_channels() returns %08x", channel_mask);
+ return channel_mask;
}
static audio_format_t out_get_format(const struct audio_stream *stream)
{
- return AUDIO_FORMAT_PCM_16_BIT;
+ const struct submix_stream_out * const out = audio_stream_get_submix_stream_out(
+ const_cast<struct audio_stream *>(stream));
+ const audio_format_t format = out->dev->config.common.format;
+ SUBMIX_ALOGV("out_get_format() returns %x", format);
+ return format;
}
static int out_set_format(struct audio_stream *stream, audio_format_t format)
{
- (void)stream;
- if (format != AUDIO_FORMAT_PCM_16_BIT) {
+ const struct submix_stream_out * const out = audio_stream_get_submix_stream_out(stream);
+ if (format != out->dev->config.common.format) {
ALOGE("out_set_format(format=%x) format unsupported", format);
return -ENOSYS;
}
@@ -240,6 +570,7 @@
int exiting = -1;
AudioParameter parms = AudioParameter(String8(kvpairs));
SUBMIX_ALOGV("out_set_parameters() kvpairs='%s'", kvpairs);
+
// FIXME this is using hard-coded strings but in the future, this functionality will be
// converted to use audio HAL extensions required to support tunneling
if ((parms.getInt(String8("exiting"), exiting) == NO_ERROR) && (exiting > 0)) {
@@ -247,7 +578,7 @@
audio_stream_get_submix_stream_out(stream)->dev;
pthread_mutex_lock(&rsxadev->lock);
{ // using the sink
- sp<MonoPipe> sink = rsxadev->rsxSink.get();
+ sp<MonoPipe> sink = rsxadev->rsxSink;
if (sink == NULL) {
pthread_mutex_unlock(&rsxadev->lock);
return 0;
@@ -272,10 +603,13 @@
{
const struct submix_stream_out * const out = audio_stream_out_get_submix_stream_out(
const_cast<struct audio_stream_out *>(stream));
- const struct submix_config * config_out = &(out->dev->config);
- uint32_t latency = (MAX_PIPE_DEPTH_IN_FRAMES * 1000) / config_out->rate;
- ALOGV("out_get_latency() returns %u", latency);
- return latency;
+ const struct submix_config * const config = &out->dev->config;
+ const size_t buffer_size_frames = calculate_stream_pipe_size_in_frames(
+ &stream->common, config, config->buffer_size_frames);
+ const uint32_t latency_ms = (buffer_size_frames * 1000) / config->common.sample_rate;
+ SUBMIX_ALOGV("out_get_latency() returns %u ms, size in frames %zu, sample rate %u",
+ latency_ms, buffer_size_frames, config->common.sample_rate);
+ return latency_ms;
}
static int out_set_volume(struct audio_stream_out *stream, float left,
@@ -293,15 +627,15 @@
SUBMIX_ALOGV("out_write(bytes=%zd)", bytes);
ssize_t written_frames = 0;
const size_t frame_size = audio_stream_frame_size(&stream->common);
- struct submix_audio_device * const rsxadev =
- audio_stream_out_get_submix_stream_out(stream)->dev;
+ struct submix_stream_out * const out = audio_stream_out_get_submix_stream_out(stream);
+ struct submix_audio_device * const rsxadev = out->dev;
const size_t frames = bytes / frame_size;
pthread_mutex_lock(&rsxadev->lock);
rsxadev->output_standby = false;
- sp<MonoPipe> sink = rsxadev->rsxSink.get();
+ sp<MonoPipe> sink = rsxadev->rsxSink;
if (sink != NULL) {
if (sink->isShutdown()) {
sink.clear();
@@ -319,6 +653,25 @@
return 0;
}
+ // If the write to the sink would block when no input stream is present, flush enough frames
+ // from the pipe to make space to write the most recent data.
+ {
+ const size_t availableToWrite = sink->availableToWrite();
+ sp<MonoPipeReader> source = rsxadev->rsxSource;
+ if (rsxadev->input == NULL && availableToWrite < frames) {
+ static uint8_t flush_buffer[64];
+ const size_t flushBufferSizeFrames = sizeof(flush_buffer) / frame_size;
+ size_t frames_to_flush_from_source = frames - availableToWrite;
+ SUBMIX_ALOGV("out_write(): flushing %d frames from the pipe to avoid blocking",
+ frames_to_flush_from_source);
+ while (frames_to_flush_from_source) {
+ const size_t flush_size = min(frames_to_flush_from_source, flushBufferSizeFrames);
+ frames_to_flush_from_source -= flush_size;
+ source->read(flush_buffer, flush_size, AudioBufferProvider::kInvalidPTS);
+ }
+ }
+ }
+
pthread_mutex_unlock(&rsxadev->lock);
written_frames = sink->write(buffer, frames);
@@ -388,38 +741,57 @@
{
const struct submix_stream_in * const in = audio_stream_get_submix_stream_in(
const_cast<struct audio_stream*>(stream));
- SUBMIX_ALOGV("in_get_sample_rate() returns %u", in->dev->config.sample_rate);
- return in->dev->config.rate;
+ SUBMIX_ALOGV("in_get_sample_rate() returns %u", in->dev->config.common.sample_rate);
+ return in->dev->config.common.sample_rate;
}
static int in_set_sample_rate(struct audio_stream *stream, uint32_t rate)
{
- return -ENOSYS;
+ const struct submix_stream_in * const in = audio_stream_get_submix_stream_in(stream);
+ if (!sample_rate_supported(rate)) {
+ ALOGE("in_set_sample_rate(rate=%u) rate unsupported", rate);
+ return -ENOSYS;
+ }
+ in->dev->config.common.sample_rate = rate;
+ SUBMIX_ALOGV("in_set_sample_rate() set %u", rate);
+ return 0;
}
static size_t in_get_buffer_size(const struct audio_stream *stream)
{
const struct submix_stream_in * const in = audio_stream_get_submix_stream_in(
const_cast<struct audio_stream*>(stream));
- ALOGV("in_get_buffer_size() returns %zu",
- in->dev->config.period_size * audio_stream_frame_size(stream));
- return in->dev->config.period_size * audio_stream_frame_size(stream);
+ const struct submix_config * const config = &in->dev->config;
+ const size_t buffer_size_frames = calculate_stream_pipe_size_in_frames(
+ stream, config, config->buffer_period_size_frames);
+ const size_t buffer_size_bytes = buffer_size_frames * audio_stream_frame_size(stream);
+ SUBMIX_ALOGV("in_get_buffer_size() returns %zu bytes, %zu frames", buffer_size_bytes,
+ buffer_size_frames);
+ return buffer_size_bytes;
}
static audio_channel_mask_t in_get_channels(const struct audio_stream *stream)
{
- (void)stream;
- return AUDIO_CHANNEL_IN_STEREO;
+ const struct submix_stream_in * const in = audio_stream_get_submix_stream_in(
+ const_cast<struct audio_stream*>(stream));
+ const audio_channel_mask_t channel_mask = in->dev->config.input_channel_mask;
+ SUBMIX_ALOGV("in_get_channels() returns %x", channel_mask);
+ return channel_mask;
}
static audio_format_t in_get_format(const struct audio_stream *stream)
{
- return AUDIO_FORMAT_PCM_16_BIT;
+ const struct submix_stream_in * const in = audio_stream_get_submix_stream_in(
+ const_cast<struct audio_stream*>(stream));
+ const audio_format_t format = in->dev->config.common.format;
+ SUBMIX_ALOGV("in_get_format() returns %x", format);
+ return format;
}
static int in_set_format(struct audio_stream *stream, audio_format_t format)
{
- if (format != AUDIO_FORMAT_PCM_16_BIT) {
+ const struct submix_stream_in * const in = audio_stream_get_submix_stream_in(stream);
+ if (format != in->dev->config.common.format) {
ALOGE("in_set_format(format=%x) format unsupported", format);
return -ENOSYS;
}
@@ -476,6 +848,7 @@
ssize_t frames_read = -1977;
struct submix_stream_in * const in = audio_stream_in_get_submix_stream_in(stream);
struct submix_audio_device * const rsxadev = in->dev;
+ struct audio_config *format;
const size_t frame_size = audio_stream_frame_size(&stream->common);
const size_t frames_to_read = bytes / frame_size;
@@ -504,7 +877,7 @@
if (source == NULL) {
ALOGE("no audio pipe yet we're trying to read!");
pthread_mutex_unlock(&rsxadev->lock);
- usleep((bytes / frame_size) * 1000000 / in_get_sample_rate(&stream->common));
+ usleep(frames_to_read * 1000000 / in_get_sample_rate(&stream->common));
memset(buffer, 0, bytes);
return bytes;
}
@@ -514,15 +887,73 @@
// read the data from the pipe (it's non blocking)
int attempts = 0;
char* buff = (char*)buffer;
+#if ENABLE_CHANNEL_CONVERSION
+ // Determine whether channel conversion is required.
+ const uint32_t input_channels = get_channel_count_from_mask(
+ rsxadev->config.input_channel_mask);
+ const uint32_t output_channels = get_channel_count_from_mask(
+ rsxadev->config.output_channel_mask);
+ if (input_channels != output_channels) {
+ SUBMIX_ALOGV("in_read(): %d output channels will be converted to %d "
+ "input channels", output_channels, input_channels);
+ // Only support 16-bit PCM channel conversion from mono to stereo or stereo to mono.
+ ALOG_ASSERT(rsxadev->config.common.format == AUDIO_FORMAT_PCM_16_BIT);
+ ALOG_ASSERT((input_channels == 1 && output_channels == 2) ||
+ (input_channels == 2 && output_channels == 1));
+ }
+#endif // ENABLE_CHANNEL_CONVERSION
+
while ((remaining_frames > 0) && (attempts < MAX_READ_ATTEMPTS)) {
- attempts++;
- frames_read = source->read(buff, remaining_frames, AudioBufferProvider::kInvalidPTS);
+ size_t read_frames = remaining_frames;
+#if ENABLE_CHANNEL_CONVERSION
+ if (output_channels == 1 && input_channels == 2) {
+ // Need to read half the requested frames since the converted output
+ // data will take twice the space (mono->stereo).
+ read_frames /= 2;
+ }
+#endif // ENABLE_CHANNEL_CONVERSION
+
+ SUBMIX_ALOGV("in_read(): frames available to read %zd", source->availableToRead());
+
+ frames_read = source->read(buff, read_frames, AudioBufferProvider::kInvalidPTS);
+
+ SUBMIX_ALOGV("in_read(): frames read %zd", frames_read);
+
+#if ENABLE_CHANNEL_CONVERSION
+ // Perform in-place channel conversion.
+ // NOTE: In the following "input stream" refers to the data returned by this function
+ // and "output stream" refers to the data read from the pipe.
+ if (input_channels != output_channels && frames_read > 0) {
+ int16_t *data = (int16_t*)buff;
+ if (output_channels == 2 && input_channels == 1) {
+ // Offset into the output stream data in samples.
+ ssize_t output_stream_offset = 0;
+ for (ssize_t input_stream_frame = 0; input_stream_frame < frames_read;
+ input_stream_frame++, output_stream_offset += 2) {
+ // Average the content from both channels.
+ data[input_stream_frame] = ((int32_t)data[output_stream_offset] +
+ (int32_t)data[output_stream_offset + 1]) / 2;
+ }
+ } else if (output_channels == 1 && input_channels == 2) {
+ // Offset into the input stream data in samples.
+ ssize_t input_stream_offset = (frames_read - 1) * 2;
+ for (ssize_t output_stream_frame = frames_read - 1; output_stream_frame >= 0;
+ output_stream_frame--, input_stream_offset -= 2) {
+ const short sample = data[output_stream_frame];
+ data[input_stream_offset] = sample;
+ data[input_stream_offset + 1] = sample;
+ }
+ }
+ }
+#endif // ENABLE_CHANNEL_CONVERSION
+
if (frames_read > 0) {
remaining_frames -= frames_read;
buff += frames_read * frame_size;
SUBMIX_ALOGV(" in_read (att=%d) got %zd frames, remaining=%zu",
attempts, frames_read, remaining_frames);
} else {
+ attempts++;
SUBMIX_ALOGE(" in_read read returned %zd", frames_read);
usleep(READ_ATTEMPT_SLEEP_MS * 1000);
}
@@ -534,9 +965,9 @@
}
if (remaining_frames > 0) {
+ const size_t remaining_bytes = remaining_frames * frame_size;
SUBMIX_ALOGV(" remaining_frames = %zu", remaining_frames);
- memset(((char*)buffer)+ bytes - (remaining_frames * frame_size), 0,
- remaining_frames * frame_size);
+ memset(((char*)buffer)+ bytes - remaining_bytes, 0, remaining_bytes);
}
// compute how much we need to sleep after reading the data by comparing the wall clock with
@@ -607,19 +1038,23 @@
struct submix_audio_device * const rsxadev = audio_hw_device_get_submix_audio_device(dev);
ALOGV("adev_open_output_stream()");
struct submix_stream_out *out;
- int ret;
(void)handle;
(void)devices;
(void)flags;
- out = (struct submix_stream_out *)calloc(1, sizeof(struct submix_stream_out));
- if (!out) {
- ret = -ENOMEM;
- goto err_open;
+ *stream_out = NULL;
+
+ // Make sure it's possible to open the device given the current audio config.
+ submix_sanitize_config(config, false);
+ if (!submix_open_validate(rsxadev, &rsxadev->lock, config, false)) {
+ ALOGE("adev_open_output_stream(): Unable to open output stream.");
+ return -EINVAL;
}
- pthread_mutex_lock(&rsxadev->lock);
+ out = (struct submix_stream_out *)calloc(1, sizeof(struct submix_stream_out));
+ if (!out) return -ENOMEM;
+ // Initialize the function pointer tables (v-tables).
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;
@@ -638,64 +1073,32 @@
out->stream.get_render_position = out_get_render_position;
out->stream.get_next_write_timestamp = out_get_next_write_timestamp;
- config->channel_mask = AUDIO_CHANNEL_OUT_STEREO;
- rsxadev->config.channel_mask = config->channel_mask;
-
- if ((config->sample_rate != 48000) && (config->sample_rate != 44100)) {
- config->sample_rate = DEFAULT_RATE_HZ;
+ // If the sink has been shutdown, delete the pipe so that it's recreated.
+ pthread_mutex_lock(&rsxadev->lock);
+ if (rsxadev->rsxSink != NULL && rsxadev->rsxSink->isShutdown()) {
+ submix_audio_device_release_pipe(rsxadev);
}
- rsxadev->config.rate = config->sample_rate;
-
- config->format = AUDIO_FORMAT_PCM_16_BIT;
- rsxadev->config.format = config->format;
-
- rsxadev->config.period_size = 1024;
- rsxadev->config.period_count = 4;
- out->dev = rsxadev;
-
- *stream_out = &out->stream;
-
- // initialize pipe
- {
- ALOGV(" initializing pipe");
- const NBAIO_Format format = Format_from_SR_C(config->sample_rate,
- popcount(config->channel_mask), config->format);
- const NBAIO_Format offers[1] = {format};
- size_t numCounterOffers = 0;
- // creating a MonoPipe with optional blocking set to true.
- MonoPipe* sink = new MonoPipe(MAX_PIPE_DEPTH_IN_FRAMES, format, true/*writeCanBlock*/);
- ssize_t index = sink->negotiate(offers, 1, NULL, numCounterOffers);
- ALOG_ASSERT(index == 0);
- MonoPipeReader* source = new MonoPipeReader(sink);
- numCounterOffers = 0;
- index = source->negotiate(offers, 1, NULL, numCounterOffers);
- ALOG_ASSERT(index == 0);
- rsxadev->rsxSink = sink;
- rsxadev->rsxSource = source;
- }
-
pthread_mutex_unlock(&rsxadev->lock);
- return 0;
+ // Store a pointer to the device from the output stream.
+ out->dev = rsxadev;
+ // Initialize the pipe.
+ ALOGV("adev_open_output_stream(): Initializing pipe");
+ submix_audio_device_create_pipe(rsxadev, config, DEFAULT_PIPE_SIZE_IN_FRAMES,
+ DEFAULT_PIPE_PERIOD_COUNT, NULL, out);
+ // Return the output stream.
+ *stream_out = &out->stream;
-err_open:
- *stream_out = NULL;
- return ret;
+ return 0;
}
static void adev_close_output_stream(struct audio_hw_device *dev,
struct audio_stream_out *stream)
{
- struct submix_audio_device *rsxadev = audio_hw_device_get_submix_audio_device(dev);
+ struct submix_stream_out * const out = audio_stream_out_get_submix_stream_out(stream);
ALOGV("adev_close_output_stream()");
-
- pthread_mutex_lock(&rsxadev->lock);
-
- rsxadev->rsxSink.clear();
- rsxadev->rsxSource.clear();
- free(stream);
-
- pthread_mutex_unlock(&rsxadev->lock);
+ submix_audio_device_destroy_pipe(audio_hw_device_get_submix_audio_device(dev), NULL, out);
+ free(out);
}
static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
@@ -779,10 +1182,18 @@
static size_t adev_get_input_buffer_size(const struct audio_hw_device *dev,
const struct audio_config *config)
{
- (void)dev;
- (void)config;
- //### TODO correlate this with pipe parameters
- return 4096;
+ if (audio_is_linear_pcm(config->format)) {
+ const size_t buffer_period_size_frames =
+ audio_hw_device_get_submix_audio_device(const_cast<struct audio_hw_device*>(dev))->
+ config.buffer_period_size_frames;
+ const size_t frame_size_in_bytes = get_channel_count_from_mask(config->channel_mask) *
+ audio_bytes_per_sample(config->format);
+ const size_t buffer_size = buffer_period_size_frames * frame_size_in_bytes;
+ SUBMIX_ALOGV("out_get_buffer_size() returns %zu bytes, %zu frames",
+ buffer_size, buffer_period_size_frames);
+ return buffer_size;
+ }
+ return 0;
}
static int adev_open_input_stream(struct audio_hw_device *dev,
@@ -791,84 +1202,83 @@
struct audio_config *config,
struct audio_stream_in **stream_in)
{
- ALOGI("adev_open_input_stream()");
struct submix_audio_device *rsxadev = audio_hw_device_get_submix_audio_device(dev);
struct submix_stream_in *in;
- int ret;
+ ALOGI("adev_open_input_stream()");
(void)handle;
(void)devices;
- in = (struct submix_stream_in *)calloc(1, sizeof(struct submix_stream_in));
- if (!in) {
- ret = -ENOMEM;
- goto err_open;
+ *stream_in = NULL;
+
+ // Make sure it's possible to open the device given the current audio config.
+ submix_sanitize_config(config, true);
+ if (!submix_open_validate(rsxadev, &rsxadev->lock, config, true)) {
+ ALOGE("adev_open_input_stream(): Unable to open input stream.");
+ return -EINVAL;
}
+#if ENABLE_LEGACY_INPUT_OPEN
pthread_mutex_lock(&rsxadev->lock);
-
- in->stream.common.get_sample_rate = in_get_sample_rate;
- in->stream.common.set_sample_rate = in_set_sample_rate;
- in->stream.common.get_buffer_size = in_get_buffer_size;
- in->stream.common.get_channels = in_get_channels;
- in->stream.common.get_format = in_get_format;
- in->stream.common.set_format = in_set_format;
- in->stream.common.standby = in_standby;
- in->stream.common.dump = in_dump;
- in->stream.common.set_parameters = in_set_parameters;
- in->stream.common.get_parameters = in_get_parameters;
- in->stream.common.add_audio_effect = in_add_audio_effect;
- in->stream.common.remove_audio_effect = in_remove_audio_effect;
- in->stream.set_gain = in_set_gain;
- in->stream.read = in_read;
- in->stream.get_input_frames_lost = in_get_input_frames_lost;
-
- config->channel_mask = AUDIO_CHANNEL_IN_STEREO;
- rsxadev->config.channel_mask = config->channel_mask;
-
- if ((config->sample_rate != 48000) && (config->sample_rate != 44100)) {
- config->sample_rate = DEFAULT_RATE_HZ;
+ in = rsxadev->input;
+ if (in) {
+ in->ref_count++;
+ sp<MonoPipe> sink = rsxadev->rsxSink;
+ ALOG_ASSERT(sink != NULL);
+ // If the sink has been shutdown, delete the pipe.
+ if (sink->isShutdown()) submix_audio_device_release_pipe(rsxadev);
}
- rsxadev->config.rate = config->sample_rate;
+ pthread_mutex_unlock(&rsxadev->lock);
+#else
+ in = NULL;
+#endif // ENABLE_LEGACY_INPUT_OPEN
- config->format = AUDIO_FORMAT_PCM_16_BIT;
- rsxadev->config.format = config->format;
+ if (!in) {
+ in = (struct submix_stream_in *)calloc(1, sizeof(struct submix_stream_in));
+ if (!in) return -ENOMEM;
+ in->ref_count = 1;
- rsxadev->config.period_size = 1024;
- rsxadev->config.period_count = 4;
+ // Initialize the function pointer tables (v-tables).
+ in->stream.common.get_sample_rate = in_get_sample_rate;
+ in->stream.common.set_sample_rate = in_set_sample_rate;
+ in->stream.common.get_buffer_size = in_get_buffer_size;
+ in->stream.common.get_channels = in_get_channels;
+ in->stream.common.get_format = in_get_format;
+ in->stream.common.set_format = in_set_format;
+ in->stream.common.standby = in_standby;
+ in->stream.common.dump = in_dump;
+ in->stream.common.set_parameters = in_set_parameters;
+ in->stream.common.get_parameters = in_get_parameters;
+ in->stream.common.add_audio_effect = in_add_audio_effect;
+ in->stream.common.remove_audio_effect = in_remove_audio_effect;
+ in->stream.set_gain = in_set_gain;
+ in->stream.read = in_read;
+ in->stream.get_input_frames_lost = in_get_input_frames_lost;
+ }
- *stream_in = &in->stream;
-
- in->dev = rsxadev;
-
+ // Initialize the input stream.
in->read_counter_frames = 0;
in->output_standby = rsxadev->output_standby;
-
- pthread_mutex_unlock(&rsxadev->lock);
+ in->dev = rsxadev;
+ // Initialize the pipe.
+ submix_audio_device_create_pipe(rsxadev, config, DEFAULT_PIPE_SIZE_IN_FRAMES,
+ DEFAULT_PIPE_PERIOD_COUNT, in, NULL);
+ // Return the input stream.
+ *stream_in = &in->stream;
return 0;
-
-err_open:
- *stream_in = NULL;
- return ret;
}
static void adev_close_input_stream(struct audio_hw_device *dev,
struct audio_stream_in *stream)
{
- struct submix_audio_device *rsxadev = audio_hw_device_get_submix_audio_device(dev);
+ struct submix_stream_in * const in = audio_stream_in_get_submix_stream_in(stream);
ALOGV("adev_close_input_stream()");
-
- pthread_mutex_lock(&rsxadev->lock);
-
- MonoPipe* sink = rsxadev->rsxSink.get();
- if (sink != NULL) {
- ALOGI("shutdown");
- sink->shutdown(true);
- }
-
- free(stream);
-
- pthread_mutex_unlock(&rsxadev->lock);
+ submix_audio_device_destroy_pipe(audio_hw_device_get_submix_audio_device(dev), in, NULL);
+#if ENABLE_LEGACY_INPUT_OPEN
+ if (in->ref_count == 0) free(in);
+#else
+ free(in);
+#endif // ENABLE_LEGACY_INPUT_OPEN
}
static int adev_dump(const audio_hw_device_t *device, int fd)
diff --git a/modules/sensors/multihal.cpp b/modules/sensors/multihal.cpp
index 135e740..5fd500a 100644
--- a/modules/sensors/multihal.cpp
+++ b/modules/sensors/multihal.cpp
@@ -36,6 +36,7 @@
static const char* CONFIG_FILENAME = "/system/etc/sensors/hals.conf";
static const char* LEGAL_SUBHAL_PATH_PREFIX = "/system/lib/hw/";
+static const char* LEGAL_SUBHAL_ALTERNATE_PATH_PREFIX = "/system/vendor/lib/";
static const int MAX_CONF_LINE_LENGTH = 1024;
static pthread_mutex_t init_modules_mutex = PTHREAD_MUTEX_INITIALIZER;
@@ -440,14 +441,15 @@
}
ALOGV("config file line #%d: '%s'", ++line_count, line);
char *real_path = realpath(line, NULL);
- if (starts_with(real_path, LEGAL_SUBHAL_PATH_PREFIX)) {
+ if (starts_with(real_path, LEGAL_SUBHAL_PATH_PREFIX) ||
+ starts_with(real_path, LEGAL_SUBHAL_ALTERNATE_PATH_PREFIX)) {
ALOGV("accepting valid path '%s'", real_path);
char* compact_line = new char[strlen(real_path) + 1];
strcpy(compact_line, real_path);
so_paths->push_back(compact_line);
} else {
- ALOGW("rejecting path '%s' because it does not start with '%s'",
- real_path, LEGAL_SUBHAL_PATH_PREFIX);
+ ALOGW("rejecting path '%s' because it does not start with '%s' or '%s'",
+ real_path, LEGAL_SUBHAL_PATH_PREFIX, LEGAL_SUBHAL_ALTERNATE_PATH_PREFIX);
}
free(real_path);
}