Merge "Camera: Fix tests per camera service interface change"
diff --git a/include/hardware/hw_auth_token.h b/include/hardware/hw_auth_token.h
new file mode 100644
index 0000000..154c1fd
--- /dev/null
+++ b/include/hardware/hw_auth_token.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 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 <stdint.h>
+
+#ifndef ANDROID_HARDWARE_HW_AUTH_TOKEN_H
+#define ANDROID_HARDWARE_HW_AUTH_TOKEN_H
+
+#ifndef __cplusplus
+extern "C" {
+#endif  // __cplusplus
+
+typedef enum {
+    HW_AUTH_NONE = 0,
+    HW_AUTH_PASSWORD = 1 << 1,
+    HW_AUTH_FINGERPRINT = 1 << 2,
+    // Additional entries should be powers of 2.
+    HW_AUTH_ANY = UINT32_MAX,
+} hw_authenticator_type_t;
+
+/**
+ * Data format for an authentication record used to prove successful authentication.
+ */
+typedef struct __attribute__((__packed__)) {
+    uint8_t version;  // Current version is 0
+    uint64_t challenge;
+    uint64_t user_id;             // secure user ID, not Android user ID
+    uint64_t authenticator_id;    // secure authenticator ID
+    uint32_t authenticator_type;  // hw_authenticator_type_t, in network order
+    uint32_t timestamp;           // in network order
+    uint8_t hmac[32];
+} hw_auth_token_t;
+
+#ifndef __cplusplus
+}  // extern "C"
+#endif  // __cplusplus
+
+#endif  // ANDROID_HARDWARE_HW_AUTH_TOKEN_H
diff --git a/include/hardware/keymaster_defs.h b/include/hardware/keymaster_defs.h
index a8f73ff..664d3a7 100644
--- a/include/hardware/keymaster_defs.h
+++ b/include/hardware/keymaster_defs.h
@@ -21,9 +21,9 @@
 #include <stdlib.h>
 #include <string.h>
 
-#if defined(__cplusplus)
+#ifndef __cplusplus
 extern "C" {
-#endif  // defined(__cplusplus)
+#endif  // __cplusplus
 
 /**
  * Authorization tags each have an associated type.  This enumeration facilitates tagging each with
@@ -128,6 +128,9 @@
     KM_TAG_NONCE = KM_BYTES | 1001,           /* Nonce or Initialization Vector */
     KM_TAG_CHUNK_LENGTH = KM_INT | 1002,      /* AEAD mode chunk size, in bytes.  0 means no limit,
                                                  which requires KM_TAG_RETURN_UNAUTHED. */
+    KM_TAG_AUTH_TOKEN = KM_BYTES | 1003,      /* Authentication token that proves secure user
+                                                 authentication has been performed.  Structure
+                                                 defined in hw_auth_token_t in hw_auth_token.h. */
 } keymaster_tag_t;
 
 /**
@@ -371,6 +374,11 @@
     KM_ERROR_SECURE_HW_BUSY = -48,
     KM_ERROR_SECURE_HW_COMMUNICATION_FAILED = -49,
     KM_ERROR_UNSUPPORTED_EC_FIELD = -50,
+    KM_ERROR_MISSING_NONCE = -51,
+    KM_ERROR_INVALID_NONCE = -52,
+    KM_ERROR_UNSUPPORTED_CHUNK_LENGTH = -53,
+    KM_ERROR_RESCOPABLE_KEY_NOT_USABLE = -54,
+
     KM_ERROR_UNIMPLEMENTED = -100,
     KM_ERROR_VERSION_MISMATCH = -101,
 
@@ -540,8 +548,8 @@
     }
 }
 
-#if defined(__cplusplus)
+#ifndef __cplusplus
 }  // extern "C"
-#endif  // defined(__cplusplus)
+#endif  // __cplusplus
 
 #endif  // ANDROID_HARDWARE_KEYMASTER_DEFS_H
diff --git a/include/hardware/radio.h b/include/hardware/radio.h
new file mode 100644
index 0000000..145deb5
--- /dev/null
+++ b/include/hardware/radio.h
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2015 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 <system/radio.h>
+#include <hardware/hardware.h>
+
+#ifndef ANDROID_RADIO_HAL_H
+#define ANDROID_RADIO_HAL_H
+
+
+__BEGIN_DECLS
+
+/**
+ * The id of this module
+ */
+#define RADIO_HARDWARE_MODULE_ID "radio"
+
+/**
+ * Name of the audio devices to open
+ */
+#define RADIO_HARDWARE_DEVICE "radio_hw_device"
+
+#define RADIO_MODULE_API_VERSION_1_0 HARDWARE_MODULE_API_VERSION(1, 0)
+#define RADIO_MODULE_API_VERSION_CURRENT RADIO_MODULE_API_VERSION_1_0
+
+
+#define RADIO_DEVICE_API_VERSION_1_0 HARDWARE_DEVICE_API_VERSION(1, 0)
+#define RADIO_DEVICE_API_VERSION_CURRENT RADIO_DEVICE_API_VERSION_1_0
+
+/**
+ * List of known radio HAL modules. This is the base name of the radio HAL
+ * library composed of the "radio." prefix, one of the base names below and
+ * a suffix specific to the device.
+ * E.g: radio.fm.default.so
+ */
+
+#define RADIO_HARDWARE_MODULE_ID_FM "fm" /* corresponds to RADIO_CLASS_AM_FM */
+#define RADIO_HARDWARE_MODULE_ID_SAT "sat" /* corresponds to RADIO_CLASS_SAT */
+#define RADIO_HARDWARE_MODULE_ID_DT "dt" /* corresponds to RADIO_CLASS_DT */
+
+
+/**
+ * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM
+ * and the fields of this data structure must begin with hw_module_t
+ * followed by module specific information.
+ */
+struct radio_module {
+    struct hw_module_t common;
+};
+
+/*
+ * Callback function called by the HAL when one of the following occurs:
+ * - event RADIO_EVENT_HW_FAILURE: radio chip of driver failure requiring
+ * closing and reopening of the tuner interface.
+ * - event RADIO_EVENT_CONFIG: new configuration applied in response to open_tuner(),
+ * or set_configuration(). The event status is 0 (no error) if the configuration has been applied,
+ * -EINVAL is not or -ETIMEDOUT in case of time out.
+ * - event RADIO_EVENT_TUNED: tune locked on new station/frequency following scan(),
+ * step(), tune() or auto AF switching. The event status is 0 (no error) if in tune,
+ * -EINVAL is not tuned and data in radio_program_info is not valid or -ETIMEDOUT if scan()
+ * timed out.
+ * - event RADIO_EVENT_TA: at the beginning and end of traffic announcement if current
+ * configuration enables TA.
+ * - event RADIO_EVENT_AF: after automatic switching to alternate frequency if current
+ * configuration enables AF switching.
+ * - event RADIO_EVENT_ANTENNA: when the antenna is connected or disconnected.
+ * - event RADIO_EVENT_METADATA: when new meta data are received from the tuned station.
+ * The callback MUST NOT be called synchronously while executing a HAL function but from
+ * a separate thread.
+ */
+typedef void (*radio_callback_t)(radio_hal_event_t *event, void *cookie);
+
+/* control interface for a radio tuner */
+struct radio_tuner {
+    /*
+     * Apply current radio band configuration (band, range, channel spacing ...).
+     *
+     * arguments:
+     * - config: the band configuration to apply
+     *
+     * returns:
+     *  0 if configuration could be applied
+     *  -EINVAL if configuration requested is invalid
+     *
+     * Automatically cancels pending scan, step or tune.
+     *
+     * Callback function with event RADIO_EVENT_CONFIG MUST be called once the
+     * configuration is applied or a failure occurs or after a time out.
+     */
+    int (*set_configuration)(const struct radio_tuner *tuner,
+                             const radio_hal_band_config_t *config);
+
+    /*
+     * Retrieve current radio band configuration.
+     *
+     * arguments:
+     * - config: where to return the band configuration
+     *
+     * returns:
+     *  0 if valid configuration is returned
+     *  -EINVAL if invalid arguments are passed
+     */
+    int (*get_configuration)(const struct radio_tuner *tuner,
+                             radio_hal_band_config_t *config);
+
+    /*
+     * Start scanning up to next valid station.
+     * Must be called when a valid configuration has been applied.
+     *
+     * arguments:
+     * - direction: RADIO_DIRECTION_UP or RADIO_DIRECTION_DOWN
+     * - skip_sub_channel: valid for HD radio or digital radios only: ignore sub channels
+     *  (e.g SPS for HD radio).
+     *
+     * returns:
+     *  0 if scan successfully started
+     *  -ENOSYS if called out of sequence
+     *  -ENODEV if another error occurs
+     *
+     * Automatically cancels pending scan, step or tune.
+     *
+     *  Callback function with event RADIO_EVENT_TUNED MUST be called once
+     *  locked on a station or after a time out or full frequency scan if
+     *  no station found. The event status should indicate if a valid station
+     *  is tuned or not.
+     */
+    int (*scan)(const struct radio_tuner *tuner,
+                radio_direction_t direction, bool skip_sub_channel);
+
+    /*
+     * Move one channel spacing up or down.
+     * Must be called when a valid configuration has been applied.
+     *
+     * arguments:
+     * - direction: RADIO_DIRECTION_UP or RADIO_DIRECTION_DOWN
+     * - skip_sub_channel: valid for HD radio or digital radios only: ignore sub channels
+     *  (e.g SPS for HD radio).
+     *
+     * returns:
+     *  0 if step successfully started
+     *  -ENOSYS if called out of sequence
+     *  -ENODEV if another error occurs
+     *
+     * Automatically cancels pending scan, step or tune.
+     *
+     * Callback function with event RADIO_EVENT_TUNED MUST be called once
+     * step completed or after a time out. The event status should indicate
+     * if a valid station is tuned or not.
+     */
+    int (*step)(const struct radio_tuner *tuner,
+                radio_direction_t direction, bool skip_sub_channel);
+
+    /*
+     * Tune to specified frequency.
+     * Must be called when a valid configuration has been applied.
+     *
+     * arguments:
+     * - channel: channel to tune to. A frequency in kHz for AM/FM/HD Radio bands.
+     * - sub_channel: valid for HD radio or digital radios only: (e.g SPS number for HD radio).
+     *
+     * returns:
+     *  0 if tune successfully started
+     *  -ENOSYS if called out of sequence
+     *  -EINVAL if invalid arguments are passed
+     *  -ENODEV if another error occurs
+     *
+     * Automatically cancels pending scan, step or tune.
+     *
+     * Callback function with event RADIO_EVENT_TUNED MUST be called once
+     * tuned or after a time out. The event status should indicate
+     * if a valid station is tuned or not.
+     */
+    int (*tune)(const struct radio_tuner *tuner,
+                unsigned int channel, unsigned int sub_channel);
+
+    /*
+     * Cancel a scan, step or tune operation.
+     * Must be called while a scan, step or tune operation is pending
+     * (callback not yet sent).
+     *
+     * returns:
+     *  0 if successful
+     *  -ENOSYS if called out of sequence
+     *  -ENODEV if another error occurs
+     *
+     * The callback is not sent.
+     */
+    int (*cancel)(const struct radio_tuner *tuner);
+
+    /*
+     * Retrieve current station information.
+     *
+     * arguments:
+     * - info: where to return the program info.
+     * If info->metadata is NULL. no meta data should be returned.
+     * If meta data must be returned, they should be added to or cloned to
+     * info->metadata, not passed from a newly created meta data buffer.
+     *
+     * returns:
+     *  0 if tuned and information available
+     *  -EINVAL if invalid arguments are passed
+     *  -ENODEV if another error occurs
+     */
+    int (*get_program_information)(const struct radio_tuner *tuner,
+                                   radio_program_info_t *info);
+};
+
+struct radio_hw_device {
+    struct hw_device_t common;
+
+    /*
+     * Retrieve implementation properties.
+     *
+     * arguments:
+     * - properties: where to return the module properties
+     *
+     * returns:
+     *  0 if no error
+     *  -EINVAL if invalid arguments are passed
+     */
+    int (*get_properties)(const struct radio_hw_device *dev,
+                          radio_hal_properties_t *properties);
+
+    /*
+     * Open a tuner interface for the requested configuration.
+     * If no other tuner is opened, this will activate the radio module.
+     *
+     * arguments:
+     * - config: the band configuration to apply
+     * - audio: this tuner will be used for live radio listening and should be connected to
+     * the radio audio source.
+     * - callback: the event callback
+     * - cookie: the cookie to pass when calling the callback
+     * - tuner: where to return the tuner interface
+     *
+     * returns:
+     *  0 if HW was powered up and configuration could be applied
+     *  -EINVAL if configuration requested is invalid
+     *  -ENOSYS if called out of sequence
+     *
+     * Callback function with event RADIO_EVENT_CONFIG MUST be called once the
+     * configuration is applied or a failure occurs or after a time out.
+     */
+    int (*open_tuner)(const struct radio_hw_device *dev,
+                    const radio_hal_band_config_t *config,
+                    bool audio,
+                    radio_callback_t callback,
+                    void *cookie,
+                    const struct radio_tuner **tuner);
+
+    /*
+     * Close a tuner interface.
+     * If the last tuner is closed, the radio module is deactivated.
+     *
+     * arguments:
+     * - tuner: the tuner interface to close
+     *
+     * returns:
+     *  0 if powered down successfully.
+     *  -EINVAL if an invalid argument is passed
+     *  -ENOSYS if called out of sequence
+     */
+    int (*close_tuner)(const struct radio_hw_device *dev, const struct radio_tuner *tuner);
+
+};
+
+typedef struct  radio_hw_device  radio_hw_device_t;
+
+/** convenience API for opening and closing a supported device */
+
+static inline int radio_hw_device_open(const struct hw_module_t* module,
+                                       struct radio_hw_device** device)
+{
+    return module->methods->open(module, RADIO_HARDWARE_DEVICE,
+                                 (struct hw_device_t**)device);
+}
+
+static inline int radio_hw_device_close(const struct radio_hw_device* device)
+{
+    return device->common.close((struct hw_device_t *)&device->common);
+}
+
+__END_DECLS
+
+#endif  // ANDROID_RADIO_HAL_H
diff --git a/modules/radio/Android.mk b/modules/radio/Android.mk
new file mode 100644
index 0000000..f433c85
--- /dev/null
+++ b/modules/radio/Android.mk
@@ -0,0 +1,27 @@
+# Copyright (C) 2015 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.
+
+LOCAL_PATH := $(call my-dir)
+
+# Stub radio HAL module, used for tests
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := radio.fm.default
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_SRC_FILES := radio_hw.c
+LOCAL_SHARED_LIBRARIES := liblog libcutils libradio_metadata
+LOCAL_MODULE_TAGS := optional
+LOCAL_32_BIT_ONLY := true
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/modules/radio/radio_hw.c b/modules/radio/radio_hw.c
new file mode 100644
index 0000000..b1a4d26
--- /dev/null
+++ b/modules/radio/radio_hw.c
@@ -0,0 +1,726 @@
+/*
+ * Copyright (C) 2015 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 "radio_hw_stub"
+#define LOG_NDEBUG 0
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <pthread.h>
+#include <sys/prctl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <cutils/log.h>
+#include <cutils/list.h>
+#include <system/radio.h>
+#include <system/radio_metadata.h>
+#include <hardware/hardware.h>
+#include <hardware/radio.h>
+
+static const radio_hal_properties_t hw_properties = {
+    .class_id = RADIO_CLASS_AM_FM,
+    .implementor = "The Android Open Source Project",
+    .product = "Radio stub HAL",
+    .version = "0.1",
+    .serial = "0123456789",
+    .num_tuners = 1,
+    .num_audio_sources = 1,
+    .supports_capture = false,
+    .num_bands = 2,
+    .bands = {
+        {
+            .type = RADIO_BAND_FM,
+            .antenna_connected = false,
+            .lower_limit = 87900,
+            .upper_limit = 107900,
+            .num_spacings = 1,
+            .spacings = { 200 },
+            .fm = {
+                .deemphasis = RADIO_DEEMPHASIS_75,
+                .stereo = true,
+                .rds = RADIO_RDS_US,
+                .ta = false,
+                .af = false,
+            }
+        },
+        {
+            .type = RADIO_BAND_AM,
+            .antenna_connected = true,
+            .lower_limit = 540,
+            .upper_limit = 1610,
+            .num_spacings = 1,
+            .spacings = { 10 },
+            .am = {
+                .stereo = true,
+            }
+        }
+    }
+};
+
+struct stub_radio_tuner {
+    struct radio_tuner interface;
+    struct stub_radio_device *dev;
+    radio_callback_t callback;
+    void *cookie;
+    radio_hal_band_config_t config;
+    radio_program_info_t program;
+    bool audio;
+    pthread_t callback_thread;
+    pthread_mutex_t lock;
+    pthread_cond_t  cond;
+    struct listnode command_list;
+};
+
+struct stub_radio_device {
+    struct radio_hw_device device;
+    struct stub_radio_tuner *tuner;
+    pthread_mutex_t lock;
+};
+
+
+typedef enum {
+    CMD_EXIT,
+    CMD_CONFIG,
+    CMD_STEP,
+    CMD_SCAN,
+    CMD_TUNE,
+    CMD_CANCEL,
+    CMD_METADATA,
+} thread_cmd_type_t;
+
+struct thread_command {
+    struct listnode node;
+    thread_cmd_type_t type;
+    struct timespec ts;
+    union {
+        unsigned int param;
+        radio_hal_band_config_t config;
+    };
+};
+
+/* must be called with out->lock locked */
+static int send_command_l(struct stub_radio_tuner *tuner,
+                          thread_cmd_type_t type,
+                          unsigned int delay_ms,
+                          void *param)
+{
+    struct thread_command *cmd = (struct thread_command *)calloc(1, sizeof(struct thread_command));
+    struct timespec ts;
+
+    if (cmd == NULL)
+        return -ENOMEM;
+
+    ALOGV("%s %d delay_ms %d", __func__, type, delay_ms);
+
+    cmd->type = type;
+    if (param != NULL) {
+        if (cmd->type == CMD_CONFIG) {
+            cmd->config = *(radio_hal_band_config_t *)param;
+            ALOGV("%s CMD_CONFIG type %d", __func__, cmd->config.type);
+        } else
+            cmd->param = *(unsigned int *)param;
+    }
+
+    clock_gettime(CLOCK_REALTIME, &ts);
+
+    ts.tv_sec  += delay_ms/1000;
+    ts.tv_nsec += (delay_ms%1000) * 1000000;
+    if (ts.tv_nsec >= 1000000000) {
+        ts.tv_nsec -= 1000000000;
+        ts.tv_sec  += 1;
+    }
+    cmd->ts = ts;
+    list_add_tail(&tuner->command_list, &cmd->node);
+    pthread_cond_signal(&tuner->cond);
+    return 0;
+}
+
+#define BITMAP_FILE_PATH "/data/misc/media/android.png"
+
+static int add_bitmap_metadata(radio_metadata_t **metadata, radio_metadata_key_t key,
+                               const char *source)
+{
+    int fd;
+    ssize_t ret = 0;
+    struct stat info;
+    void *data = NULL;
+    size_t size;
+
+    fd = open(source, O_RDONLY);
+    if (fd < 0)
+        return -EPIPE;
+
+    fstat(fd, &info);
+    size = info.st_size;
+    data = malloc(size);
+    if (data == NULL) {
+        ret = -ENOMEM;
+        goto exit;
+    }
+    ret = read(fd, data, size);
+    if (ret < 0)
+        goto exit;
+    ret = radio_metadata_add_raw(metadata, key, (const unsigned char *)data, size);
+
+exit:
+    close(fd);
+    free(data);
+    ALOGE_IF(ret != 0, "%s error %d", __func__, ret);
+    return (int)ret;
+}
+
+static int prepare_metadata(struct stub_radio_tuner *tuner,
+                            radio_metadata_t **metadata, bool program)
+{
+    int ret = 0;
+    char text[RADIO_STRING_LEN_MAX];
+    struct timespec ts;
+
+    if (metadata == NULL)
+        return -EINVAL;
+
+    if (*metadata != NULL)
+        radio_metadata_deallocate(*metadata);
+
+    *metadata = NULL;
+
+    ret = radio_metadata_allocate(metadata, tuner->program.channel, 0);
+    if (ret != 0)
+        return ret;
+
+    if (program) {
+        ret = radio_metadata_add_int(metadata, RADIO_METADATA_KEY_RBDS_PTY, 5);
+        if (ret != 0)
+            goto exit;
+        ret = radio_metadata_add_text(metadata, RADIO_METADATA_KEY_RDS_PS, "RockBand");
+        if (ret != 0)
+            goto exit;
+        ret = add_bitmap_metadata(metadata, RADIO_METADATA_KEY_ICON, BITMAP_FILE_PATH);
+        if (ret != 0)
+            goto exit;
+    } else {
+        ret = add_bitmap_metadata(metadata, RADIO_METADATA_KEY_ART, BITMAP_FILE_PATH);
+        if (ret != 0)
+            goto exit;
+    }
+
+    clock_gettime(CLOCK_REALTIME, &ts);
+    snprintf(text, RADIO_STRING_LEN_MAX, "Artist %ld", ts.tv_sec % 10);
+    ret = radio_metadata_add_text(metadata, RADIO_METADATA_KEY_ARTIST, text);
+    if (ret != 0)
+        goto exit;
+
+    snprintf(text, RADIO_STRING_LEN_MAX, "Song %ld", ts.tv_nsec % 10);
+    ret = radio_metadata_add_text(metadata, RADIO_METADATA_KEY_TITLE, text);
+    if (ret != 0)
+        goto exit;
+
+    return 0;
+
+exit:
+    radio_metadata_deallocate(*metadata);
+    *metadata = NULL;
+    return ret;
+}
+
+static void *callback_thread_loop(void *context)
+{
+    struct stub_radio_tuner *tuner = (struct stub_radio_tuner *)context;
+    struct timespec ts = {0, 0};
+
+    ALOGI("%s", __func__);
+
+    prctl(PR_SET_NAME, (unsigned long)"sound trigger callback", 0, 0, 0);
+
+    pthread_mutex_lock(&tuner->lock);
+
+    while (true) {
+        struct thread_command *cmd = NULL;
+        struct listnode *item;
+        struct listnode *tmp;
+        struct timespec cur_ts;
+
+        if (list_empty(&tuner->command_list) || ts.tv_sec != 0) {
+            ALOGV("%s SLEEPING", __func__);
+            if (ts.tv_sec != 0) {
+                ALOGV("%s SLEEPING with timeout", __func__);
+                pthread_cond_timedwait(&tuner->cond, &tuner->lock, &ts);
+            } else {
+                ALOGV("%s SLEEPING forever", __func__);
+                pthread_cond_wait(&tuner->cond, &tuner->lock);
+            }
+            ts.tv_sec = 0;
+            ALOGV("%s RUNNING", __func__);
+        }
+
+        clock_gettime(CLOCK_REALTIME, &cur_ts);
+
+        list_for_each_safe(item, tmp, &tuner->command_list) {
+            cmd = node_to_item(item, struct thread_command, node);
+
+            if ((cmd->ts.tv_sec < cur_ts.tv_sec) ||
+                    ((cmd->ts.tv_sec == cur_ts.tv_sec) && (cmd->ts.tv_nsec < cur_ts.tv_nsec))) {
+                radio_hal_event_t event;
+
+                event.type = RADIO_EVENT_HW_FAILURE;
+                list_remove(item);
+
+                ALOGV("%s processing command %d time %ld.%ld", __func__, cmd->type, cmd->ts.tv_sec,
+                      cmd->ts.tv_nsec);
+
+                switch (cmd->type) {
+                default:
+                case CMD_EXIT:
+                    free(cmd);
+                    goto exit;
+
+                case CMD_CONFIG: {
+                    tuner->config = cmd->config;
+                    event.type = RADIO_EVENT_CONFIG;
+                    event.config = tuner->config;
+                    ALOGV("%s CMD_CONFIG type %d low %d up %d",
+                          __func__, tuner->config.type,
+                          tuner->config.lower_limit, tuner->config.upper_limit);
+                    if (tuner->config.type == RADIO_BAND_FM) {
+                        ALOGV("  - stereo %d\n  - rds %d\n  - ta %d\n  - af %d",
+                              tuner->config.fm.stereo, tuner->config.fm.rds,
+                              tuner->config.fm.ta, tuner->config.fm.af);
+                    } else {
+                        ALOGV("  - stereo %d", tuner->config.am.stereo);
+                    }
+                } break;
+
+                case CMD_STEP: {
+                    int frequency;
+                    frequency = tuner->program.channel;
+                    if (cmd->param == RADIO_DIRECTION_UP) {
+                        frequency += tuner->config.spacings[0];
+                    } else {
+                        frequency -= tuner->config.spacings[0];
+                    }
+                    if (frequency > (int)tuner->config.upper_limit) {
+                        frequency = tuner->config.lower_limit;
+                    }
+                    if (frequency < (int)tuner->config.lower_limit) {
+                        frequency = tuner->config.upper_limit;
+                    }
+                    tuner->program.channel = frequency;
+                    tuner->program.tuned  = (frequency / (tuner->config.spacings[0] * 5)) % 2;
+                    tuner->program.signal_strength = 20;
+                    if (tuner->config.type == RADIO_BAND_FM)
+                        tuner->program.stereo = false;
+                    else
+                        tuner->program.stereo = false;
+
+                    event.type = RADIO_EVENT_TUNED;
+                    event.info = tuner->program;
+                } break;
+
+                case CMD_SCAN: {
+                    int frequency;
+                    frequency = tuner->program.channel;
+                    if (cmd->param == RADIO_DIRECTION_UP) {
+                        frequency += tuner->config.spacings[0] * 25;
+                    } else {
+                        frequency -= tuner->config.spacings[0] * 25;
+                    }
+                    if (frequency > (int)tuner->config.upper_limit) {
+                        frequency = tuner->config.lower_limit;
+                    }
+                    if (frequency < (int)tuner->config.lower_limit) {
+                        frequency = tuner->config.upper_limit;
+                    }
+                    tuner->program.channel = (unsigned int)frequency;
+                    tuner->program.tuned  = true;
+                    if (tuner->config.type == RADIO_BAND_FM)
+                        tuner->program.stereo = tuner->config.fm.stereo;
+                    else
+                        tuner->program.stereo = tuner->config.am.stereo;
+                    tuner->program.signal_strength = 50;
+
+                    event.type = RADIO_EVENT_TUNED;
+                    event.info = tuner->program;
+                    if (tuner->program.metadata != NULL)
+                        radio_metadata_deallocate(tuner->program.metadata);
+                    tuner->program.metadata = NULL;
+                    send_command_l(tuner, CMD_METADATA, 2000, NULL);
+                } break;
+
+                case CMD_TUNE: {
+                    tuner->program.channel = cmd->param;
+                    tuner->program.tuned  = (tuner->program.channel /
+                                                (tuner->config.spacings[0] * 5)) % 2;
+
+                    if (tuner->program.tuned) {
+                        prepare_metadata(tuner, &tuner->program.metadata, true);
+                        send_command_l(tuner, CMD_METADATA, 5000, NULL);
+                    } else {
+                        if (tuner->program.metadata != NULL)
+                            radio_metadata_deallocate(tuner->program.metadata);
+                        tuner->program.metadata = NULL;
+                    }
+                    tuner->program.signal_strength = 100;
+                    if (tuner->config.type == RADIO_BAND_FM)
+                        tuner->program.stereo =
+                                tuner->program.tuned ? tuner->config.fm.stereo : false;
+                    else
+                        tuner->program.stereo =
+                            tuner->program.tuned ? tuner->config.am.stereo : false;
+                    event.type = RADIO_EVENT_TUNED;
+                    event.info = tuner->program;
+                } break;
+
+                case CMD_METADATA: {
+                    prepare_metadata(tuner, &tuner->program.metadata, false);
+                    event.type = RADIO_EVENT_METADATA;
+                    event.metadata = tuner->program.metadata;
+                } break;
+
+                case CMD_CANCEL: {
+                    struct listnode *tmp2;
+                    list_for_each_safe(item, tmp2, &tuner->command_list) {
+                        cmd = node_to_item(item, struct thread_command, node);
+                        if (cmd->type == CMD_STEP || cmd->type == CMD_SCAN ||
+                                cmd->type == CMD_TUNE || cmd->type == CMD_METADATA) {
+                            list_remove(item);
+                            free(cmd);
+                        }
+                    }
+                } break;
+
+                }
+                if (event.type != RADIO_EVENT_HW_FAILURE && tuner->callback != NULL) {
+                    pthread_mutex_unlock(&tuner->lock);
+                    tuner->callback(&event, tuner->cookie);
+                    pthread_mutex_lock(&tuner->lock);
+                }
+                ALOGV("%s processed command %d", __func__, cmd->type);
+                free(cmd);
+            } else {
+                if ((ts.tv_sec == 0) ||
+                        (cmd->ts.tv_sec < ts.tv_sec) ||
+                        ((cmd->ts.tv_sec == ts.tv_sec) && (cmd->ts.tv_nsec < ts.tv_nsec))) {
+                    ts.tv_sec = cmd->ts.tv_sec;
+                    ts.tv_nsec = cmd->ts.tv_nsec;
+                }
+            }
+        }
+    }
+
+exit:
+    pthread_mutex_unlock(&tuner->lock);
+
+    ALOGV("%s Exiting", __func__);
+
+    return NULL;
+}
+
+
+static int tuner_set_configuration(const struct radio_tuner *tuner,
+                         const radio_hal_band_config_t *config)
+{
+    struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
+    int status = 0;
+
+    ALOGI("%s stub_tuner %p", __func__, stub_tuner);
+    pthread_mutex_lock(&stub_tuner->lock);
+    if (config == NULL) {
+        status = -EINVAL;
+        goto exit;
+    }
+    send_command_l(stub_tuner, CMD_CANCEL, 0, NULL);
+    send_command_l(stub_tuner, CMD_CONFIG, 500, (void *)config);
+
+exit:
+    pthread_mutex_unlock(&stub_tuner->lock);
+    return status;
+}
+
+static int tuner_get_configuration(const struct radio_tuner *tuner,
+                         radio_hal_band_config_t *config)
+{
+    struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
+    int status = 0;
+    struct listnode *item;
+    radio_hal_band_config_t *src_config;
+
+    ALOGI("%s stub_tuner %p", __func__, stub_tuner);
+    pthread_mutex_lock(&stub_tuner->lock);
+    src_config = &stub_tuner->config;
+
+    if (config == NULL) {
+        status = -EINVAL;
+        goto exit;
+    }
+    list_for_each(item, &stub_tuner->command_list) {
+        struct thread_command *cmd = node_to_item(item, struct thread_command, node);
+        if (cmd->type == CMD_CONFIG) {
+            src_config = &cmd->config;
+        }
+    }
+    *config = *src_config;
+
+exit:
+    pthread_mutex_unlock(&stub_tuner->lock);
+    return status;
+}
+
+static int tuner_step(const struct radio_tuner *tuner,
+                     radio_direction_t direction, bool skip_sub_channel)
+{
+    struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
+
+    ALOGI("%s stub_tuner %p direction %d, skip_sub_channel %d",
+          __func__, stub_tuner, direction, skip_sub_channel);
+
+    pthread_mutex_lock(&stub_tuner->lock);
+    send_command_l(stub_tuner, CMD_STEP, 20, &direction);
+    pthread_mutex_unlock(&stub_tuner->lock);
+    return 0;
+}
+
+static int tuner_scan(const struct radio_tuner *tuner,
+                     radio_direction_t direction, bool skip_sub_channel)
+{
+    struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
+
+    ALOGI("%s stub_tuner %p direction %d, skip_sub_channel %d",
+          __func__, stub_tuner, direction, skip_sub_channel);
+
+    pthread_mutex_lock(&stub_tuner->lock);
+    send_command_l(stub_tuner, CMD_SCAN, 200, &direction);
+    pthread_mutex_unlock(&stub_tuner->lock);
+    return 0;
+}
+
+static int tuner_tune(const struct radio_tuner *tuner,
+                     unsigned int channel, unsigned int sub_channel)
+{
+    struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
+
+    ALOGI("%s stub_tuner %p channel %d, sub_channel %d",
+          __func__, stub_tuner, channel, sub_channel);
+
+    pthread_mutex_lock(&stub_tuner->lock);
+    if (channel < stub_tuner->config.lower_limit || channel > stub_tuner->config.upper_limit) {
+        pthread_mutex_unlock(&stub_tuner->lock);
+        ALOGI("%s channel out of range", __func__);
+        return -EINVAL;
+    }
+    send_command_l(stub_tuner, CMD_TUNE, 100, &channel);
+    pthread_mutex_unlock(&stub_tuner->lock);
+    return 0;
+}
+
+static int tuner_cancel(const struct radio_tuner *tuner)
+{
+    struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
+
+    ALOGI("%s stub_tuner %p", __func__, stub_tuner);
+
+    pthread_mutex_lock(&stub_tuner->lock);
+    send_command_l(stub_tuner, CMD_CANCEL, 0, NULL);
+    pthread_mutex_unlock(&stub_tuner->lock);
+    return 0;
+}
+
+static int tuner_get_program_information(const struct radio_tuner *tuner,
+                                        radio_program_info_t *info)
+{
+    struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
+    int status = 0;
+    radio_metadata_t *metadata;
+
+    ALOGI("%s stub_tuner %p", __func__, stub_tuner);
+    pthread_mutex_lock(&stub_tuner->lock);
+    if (info == NULL) {
+        status = -EINVAL;
+        goto exit;
+    }
+    metadata = info->metadata;
+    *info = stub_tuner->program;
+    info->metadata = metadata;
+    if (metadata != NULL)
+        radio_metadata_add_metadata(&info->metadata, stub_tuner->program.metadata);
+
+exit:
+    pthread_mutex_unlock(&stub_tuner->lock);
+    return status;
+}
+
+static int rdev_get_properties(const struct radio_hw_device *dev,
+                                radio_hal_properties_t *properties)
+{
+    struct stub_radio_device *rdev = (struct stub_radio_device *)dev;
+
+    ALOGI("%s", __func__);
+    if (properties == NULL)
+        return -EINVAL;
+    memcpy(properties, &hw_properties, sizeof(radio_hal_properties_t));
+    return 0;
+}
+
+static int rdev_open_tuner(const struct radio_hw_device *dev,
+                          const radio_hal_band_config_t *config,
+                          bool audio,
+                          radio_callback_t callback,
+                          void *cookie,
+                          const struct radio_tuner **tuner)
+{
+    struct stub_radio_device *rdev = (struct stub_radio_device *)dev;
+    int status = 0;
+
+    ALOGI("%s rdev %p", __func__, rdev);
+    pthread_mutex_lock(&rdev->lock);
+
+    if (rdev->tuner != NULL) {
+        status = -ENOSYS;
+        goto exit;
+    }
+
+    if (config == NULL || callback == NULL || tuner == NULL) {
+        status = -EINVAL;
+        goto exit;
+    }
+
+    rdev->tuner = (struct stub_radio_tuner *)calloc(1, sizeof(struct stub_radio_tuner));
+    if (rdev->tuner == NULL) {
+        status = -ENOMEM;
+        goto exit;
+    }
+
+    rdev->tuner->interface.set_configuration = tuner_set_configuration;
+    rdev->tuner->interface.get_configuration = tuner_get_configuration;
+    rdev->tuner->interface.scan = tuner_scan;
+    rdev->tuner->interface.step = tuner_step;
+    rdev->tuner->interface.tune = tuner_tune;
+    rdev->tuner->interface.cancel = tuner_cancel;
+    rdev->tuner->interface.get_program_information = tuner_get_program_information;
+
+    rdev->tuner->audio = audio;
+    rdev->tuner->callback = callback;
+    rdev->tuner->cookie = cookie;
+
+    rdev->tuner->dev = rdev;
+
+    pthread_mutex_init(&rdev->tuner->lock, (const pthread_mutexattr_t *) NULL);
+    pthread_cond_init(&rdev->tuner->cond, (const pthread_condattr_t *) NULL);
+    pthread_create(&rdev->tuner->callback_thread, (const pthread_attr_t *) NULL,
+                        callback_thread_loop, rdev->tuner);
+    list_init(&rdev->tuner->command_list);
+
+    pthread_mutex_lock(&rdev->tuner->lock);
+    send_command_l(rdev->tuner, CMD_CONFIG, 500, (void *)config);
+    pthread_mutex_unlock(&rdev->tuner->lock);
+
+    *tuner = &rdev->tuner->interface;
+
+exit:
+    pthread_mutex_unlock(&rdev->lock);
+    ALOGI("%s DONE", __func__);
+    return status;
+}
+
+static int rdev_close_tuner(const struct radio_hw_device *dev,
+                            const struct radio_tuner *tuner)
+{
+    struct stub_radio_device *rdev = (struct stub_radio_device *)dev;
+    struct stub_radio_tuner *stub_tuner = (struct stub_radio_tuner *)tuner;
+    int status = 0;
+
+    ALOGI("%s tuner %p", __func__, tuner);
+    pthread_mutex_lock(&rdev->lock);
+
+    if (tuner == NULL) {
+        status = -EINVAL;
+        goto exit;
+    }
+
+    pthread_mutex_lock(&stub_tuner->lock);
+    stub_tuner->callback = NULL;
+    send_command_l(stub_tuner, CMD_EXIT, 0, NULL);
+    pthread_mutex_unlock(&stub_tuner->lock);
+    pthread_join(stub_tuner->callback_thread, (void **) NULL);
+
+    if (stub_tuner->program.metadata != NULL)
+        radio_metadata_deallocate(stub_tuner->program.metadata);
+
+    free(stub_tuner);
+    rdev->tuner = NULL;
+
+exit:
+    pthread_mutex_unlock(&rdev->lock);
+    return status;
+}
+
+static int rdev_close(hw_device_t *device)
+{
+    struct stub_radio_device *rdev = (struct stub_radio_device *)device;
+    if (rdev != NULL) {
+        free(rdev->tuner);
+    }
+    free(rdev);
+    return 0;
+}
+
+static int rdev_open(const hw_module_t* module, const char* name,
+                     hw_device_t** device)
+{
+    struct stub_radio_device *rdev;
+    int ret;
+
+    if (strcmp(name, RADIO_HARDWARE_DEVICE) != 0)
+        return -EINVAL;
+
+    rdev = calloc(1, sizeof(struct stub_radio_device));
+    if (!rdev)
+        return -ENOMEM;
+
+    rdev->device.common.tag = HARDWARE_DEVICE_TAG;
+    rdev->device.common.version = RADIO_DEVICE_API_VERSION_1_0;
+    rdev->device.common.module = (struct hw_module_t *) module;
+    rdev->device.common.close = rdev_close;
+    rdev->device.get_properties = rdev_get_properties;
+    rdev->device.open_tuner = rdev_open_tuner;
+    rdev->device.close_tuner = rdev_close_tuner;
+
+    pthread_mutex_init(&rdev->lock, (const pthread_mutexattr_t *) NULL);
+
+    *device = &rdev->device.common;
+
+    return 0;
+}
+
+
+static struct hw_module_methods_t hal_module_methods = {
+    .open = rdev_open,
+};
+
+struct radio_module HAL_MODULE_INFO_SYM = {
+    .common = {
+        .tag = HARDWARE_MODULE_TAG,
+        .module_api_version = RADIO_MODULE_API_VERSION_1_0,
+        .hal_api_version = HARDWARE_HAL_API_VERSION,
+        .id = RADIO_HARDWARE_MODULE_ID,
+        .name = "Stub radio HAL",
+        .author = "The Android Open Source Project",
+        .methods = &hal_module_methods,
+    },
+};