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);
     }