Adds GenericSoundModel Event Support to Stub HAL

Change-Id: I2a021cbd12a4e17954dcf84528c80731054f2fd3
diff --git a/modules/soundtrigger/sound_trigger_hw.c b/modules/soundtrigger/sound_trigger_hw.c
index 2c1cc9c..8083b1f 100644
--- a/modules/soundtrigger/sound_trigger_hw.c
+++ b/modules/soundtrigger/sound_trigger_hw.c
@@ -30,7 +30,7 @@
  */
 
 #define LOG_TAG "sound_trigger_hw_default"
-/*#define LOG_NDEBUG 0*/
+#define LOG_NDEBUG 1
 
 #include <errno.h>
 #include <stdio.h>
@@ -51,10 +51,6 @@
 #include <system/sound_trigger.h>
 #include <hardware/sound_trigger.h>
 
-/* Although max_sound_models is specified in sound_trigger_properties, having a maximum maximum
-allows a dramatic simplification of data structures in this file */
-#define MAX_MAX_SOUND_MODELS 5
-
 static const struct sound_trigger_properties hw_properties = {
         "The Android Open Source Project", // implementor
         "Sound Trigger stub HAL", // description
@@ -72,15 +68,19 @@
 };
 
 struct recognition_context {
-      /* Sound Model Information Modified On Load */
-    sound_model_handle_t loaded_sound_model;
-    sound_model_callback_t sound_model_callback;
-    void *sound_model_cookie;
+    // Sound Model information, added in method load_sound_model
+    sound_model_handle_t model_handle;
+    sound_trigger_sound_model_type_t model_type;
+    sound_model_callback_t model_callback;
+    void *model_cookie;
 
-    /* Sound Model Information Modified On Recognition Start */
+    // Sound Model information, added in start_recognition
     struct sound_trigger_recognition_config *config;
     recognition_callback_t recognition_callback;
     void *recognition_cookie;
+
+    // Next recognition_context in the linked list
+    struct recognition_context *next;
 };
 
 struct stub_sound_trigger_device {
@@ -88,7 +88,8 @@
     pthread_mutex_t lock;
     pthread_t callback_thread;
 
-    struct recognition_context model_context[MAX_MAX_SOUND_MODELS];
+    // Recognition contexts are stored as a linked list
+    struct recognition_context *root_model_context;
 
     int next_sound_model_id;
 };
@@ -104,57 +105,94 @@
     return new_id;
 }
 
-static char *sound_trigger_event_alloc(struct stub_sound_trigger_device *stdev,
-                                       sound_model_handle_t handle) {
-    struct sound_trigger_phrase_recognition_event *event;
+static char *sound_trigger_event_alloc(sound_model_handle_t handle,
+            sound_trigger_sound_model_type_t model_type,
+            struct sound_trigger_recognition_config *config) {
     char *data;
-    data = (char *)calloc(1, sizeof(struct sound_trigger_phrase_recognition_event));
-    if (!data)
-        return NULL;
+    if (model_type == SOUND_MODEL_TYPE_KEYPHRASE) {
+        struct sound_trigger_phrase_recognition_event *event;
+        data = (char *)calloc(1, sizeof(struct sound_trigger_phrase_recognition_event));
+        if (!data)
+            return NULL;
+        event = (struct sound_trigger_phrase_recognition_event *)data;
+        event->common.status = RECOGNITION_STATUS_SUCCESS;
+        event->common.type = SOUND_MODEL_TYPE_KEYPHRASE;
+        event->common.model = handle;
 
-    unsigned int model_index;
-    bool found = false;
-    for(model_index = 0; model_index < hw_properties.max_sound_models; model_index++) {
-        if (stdev->model_context[model_index].loaded_sound_model == handle) {
-            found = true;
-            break;
+        if (config) {
+            unsigned int i;
+
+            event->num_phrases = config->num_phrases;
+            if (event->num_phrases > SOUND_TRIGGER_MAX_PHRASES)
+                event->num_phrases = SOUND_TRIGGER_MAX_PHRASES;
+            for (i=0; i < event->num_phrases; i++)
+                memcpy(&event->phrase_extras[i],
+                       &config->phrases[i],
+                       sizeof(struct sound_trigger_phrase_recognition_extra));
         }
-    }
-    if (found == false) {
-        ALOGW("Can't find model");
+
+        event->num_phrases = 1;
+        event->phrase_extras[0].confidence_level = 100;
+        event->phrase_extras[0].num_levels = 1;
+        event->phrase_extras[0].levels[0].level = 100;
+        event->phrase_extras[0].levels[0].user_id = 0;
+        // Signify that all the data is comming through streaming, not through the buffer.
+        event->common.capture_available = true;
+        event->common.audio_config = AUDIO_CONFIG_INITIALIZER;
+        event->common.audio_config.sample_rate = 16000;
+        event->common.audio_config.channel_mask = AUDIO_CHANNEL_IN_MONO;
+        event->common.audio_config.format = AUDIO_FORMAT_PCM_16_BIT;
+    } else if (model_type == SOUND_MODEL_TYPE_GENERIC) {
+        struct sound_trigger_generic_recognition_event *event;
+        data = (char *)calloc(1, sizeof(struct sound_trigger_generic_recognition_event));
+        if (!data)
+            return NULL;
+        event = (struct sound_trigger_generic_recognition_event *)data;
+        event->common.status = RECOGNITION_STATUS_SUCCESS;
+        event->common.type = SOUND_MODEL_TYPE_GENERIC;
+        event->common.model = handle;
+
+        // Signify that all the data is comming through streaming, not through the buffer.
+        event->common.capture_available = true;
+        event->common.audio_config = AUDIO_CONFIG_INITIALIZER;
+        event->common.audio_config.sample_rate = 16000;
+        event->common.audio_config.channel_mask = AUDIO_CHANNEL_IN_MONO;
+        event->common.audio_config.format = AUDIO_FORMAT_PCM_16_BIT;
+    } else {
+        ALOGW("No Valid Event Type Known");
         return NULL;
     }
+    return data;
+}
 
-    event = (struct sound_trigger_phrase_recognition_event *)data;
-    event->common.status = RECOGNITION_STATUS_SUCCESS;
-    event->common.type = SOUND_MODEL_TYPE_KEYPHRASE;
-    event->common.model = handle;
-
-    if (stdev->model_context[model_index].config) {
-        unsigned int i;
-
-        event->num_phrases = stdev->model_context[model_index].config->num_phrases;
-        if (event->num_phrases > SOUND_TRIGGER_MAX_PHRASES)
-            event->num_phrases = SOUND_TRIGGER_MAX_PHRASES;
-        for (i=0; i < event->num_phrases; i++)
-            memcpy(&event->phrase_extras[i], &stdev->model_context[model_index].config->phrases[i],
-                   sizeof(struct sound_trigger_phrase_recognition_extra));
+static void send_recognition_event(sound_model_handle_t model_handle,
+            sound_trigger_sound_model_type_t model_type,
+            recognition_callback_t recognition_callback, void *recognition_cookie,
+            struct sound_trigger_recognition_config *config) {
+    if (recognition_callback == NULL) {
+        ALOGI("%s No matching callback for handle %d", __func__, model_handle);
+        return;
     }
 
-    event->num_phrases = 1;
-    event->phrase_extras[0].confidence_level = 100;
-    event->phrase_extras[0].num_levels = 1;
-    event->phrase_extras[0].levels[0].level = 100;
-    event->phrase_extras[0].levels[0].user_id = 0;
-    // Signify that all the data is comming through streaming, not through the buffer.
-    event->common.capture_available = true;
-
-    event->common.audio_config = AUDIO_CONFIG_INITIALIZER;
-    event->common.audio_config.sample_rate = 16000;
-    event->common.audio_config.channel_mask = AUDIO_CHANNEL_IN_MONO;
-    event->common.audio_config.format = AUDIO_FORMAT_PCM_16_BIT;
-
-    return data;
+    if (model_type == SOUND_MODEL_TYPE_KEYPHRASE) {
+        struct sound_trigger_phrase_recognition_event *event;
+        event = (struct sound_trigger_phrase_recognition_event *)
+               sound_trigger_event_alloc(model_handle, model_type, config);
+        if (event) {
+            recognition_callback(&event->common, recognition_cookie);
+            free(event);
+        }
+    } else if (model_type == SOUND_MODEL_TYPE_GENERIC) {
+        struct sound_trigger_generic_recognition_event *event;
+        event = (struct sound_trigger_generic_recognition_event *)
+                sound_trigger_event_alloc(model_handle, model_type, config);
+        if (event) {
+            recognition_callback(&event->common, recognition_cookie);
+            free(event);
+        }
+    } else {
+        ALOGI("Unknown Sound Model Type, No Event to Send");
+    }
 }
 
 static void *callback_thread_loop(void *context) {
@@ -215,26 +253,26 @@
                     ALOGI("Received kill signal: stop listening to incoming server messages");
                     exit = true;
                 } else if (index < hw_properties.max_sound_models) {
-                    ALOGI("Going to send trigger for model #%d", index );
-                    if (stdev->model_context[index].recognition_callback != NULL) {
-                        sound_model_handle_t handle = stdev->model_context[index].loaded_sound_model;
-                        if (handle == 0) {
-                            ALOGW("This trigger is not loaded");
-                        } else {
-                            struct sound_trigger_phrase_recognition_event *event;
-                            event = (struct sound_trigger_phrase_recognition_event *)
-                                   sound_trigger_event_alloc(stdev, handle);
-                            if (event) {
-                               ALOGI("%s send callback model %d", __func__, index);
-                               stdev->model_context[index].recognition_callback(&event->common,
-                                                     stdev->model_context[index].recognition_cookie);
-                               free(event);
-                               stdev->model_context[index].recognition_callback = NULL;
-                            }
-                            exit = true;
+                    ALOGI("Going to send trigger for model index #%d", index );
+                    struct recognition_context *model_context = NULL;
+                    struct recognition_context *last_model_context = stdev->root_model_context;
+                    int model_index = 0;
+                    while(last_model_context) {
+                        if (model_index == index) {
+                            model_context = last_model_context;
+                            break;
                         }
+                        last_model_context = last_model_context->next;
+                        model_index++;
+                    }
+                    if (model_context) {
+                        send_recognition_event(model_context->model_handle,
+                                  model_context->model_type,
+                                  model_context->recognition_callback,
+                                  model_context->recognition_cookie,
+                                  model_context->config);
                     } else {
-                       ALOGI("%s No matching callback for %d", __func__, index);
+                        ALOGI("Sound Model Does Not Exist at this Index: %d", index);
                     }
                 } else {
                     ALOGI("Data is not recognized: %d", index);
@@ -302,29 +340,46 @@
         return -EINVAL;
     }
 
-    /* Find if there is space for this sound model */
-    unsigned int model_index;
-    bool found = false;
-    for(model_index = 0; model_index < hw_properties.max_sound_models; model_index++) {
-        if (stdev->model_context[model_index].loaded_sound_model == 0) {
-            found = true;
-            break;
-        }
-    }
-    if (found == false) {
-        ALOGW("Can't load model: reached max sound model limit");
+    struct recognition_context *model_context;
+    model_context = malloc(sizeof(struct recognition_context));
+    if(!model_context) {
+        ALOGW("Could not allocate recognition_context");
         pthread_mutex_unlock(&stdev->lock);
         return -ENOSYS;
     }
 
-    stdev->model_context[model_index].loaded_sound_model = generate_sound_model_id(dev);
-    *handle = stdev->model_context[model_index].loaded_sound_model;
+    // Add the new model context to the recognition_context linked list
+    if (stdev->root_model_context) {
+        // Find the tail
+        struct recognition_context *last_model_context = stdev->root_model_context;
+        int model_count = 0;
+        while(last_model_context->next) {
+            last_model_context = last_model_context->next;
+            model_count++;
+            if (model_count >= hw_properties.max_sound_models) {
+                ALOGW("Can't load model: reached max sound model limit");
+                free(model_context);
+                pthread_mutex_unlock(&stdev->lock);
+                return -ENOSYS;
+            }
+        }
+        last_model_context->next = model_context;
+    } else {
+        stdev->root_model_context = model_context;
+    }
+
+    model_context->model_handle = generate_sound_model_id(dev);
+    *handle = model_context->model_handle;
+    model_context->model_type = sound_model->type;
 
     char *data = (char *)sound_model + sound_model->data_offset;
     ALOGI("%s data size %d data %d - %d", __func__,
           sound_model->data_size, data[0], data[sound_model->data_size - 1]);
-    stdev->model_context[model_index].sound_model_callback = callback;
-    stdev->model_context[model_index].sound_model_cookie = cookie;
+    model_context->model_callback = callback;
+    model_context->model_cookie = cookie;
+    model_context->config = NULL;
+    model_context->recognition_callback = NULL;
+    model_context->recognition_cookie = NULL;
 
     pthread_mutex_unlock(&stdev->lock);
     return status;
@@ -337,35 +392,46 @@
     ALOGI("unload_sound_model");
     pthread_mutex_lock(&stdev->lock);
 
-    unsigned int i;
-    unsigned int model_index;
-    bool found = false;
-    bool other_callbacks_found = false;
-    for(i = 0; i < hw_properties.max_sound_models; i++) {
-        if (stdev->model_context[i].loaded_sound_model == handle) {
-            found = true;
-            model_index = i;
-            break;
-        } else if (stdev->model_context[i].recognition_callback != NULL) {
-            other_callbacks_found = true;
+
+    struct recognition_context *model_context = NULL;
+    struct recognition_context *previous_model_context = NULL;
+    if (stdev->root_model_context) {
+        struct recognition_context *last_model_context = stdev->root_model_context;
+        while(last_model_context) {
+            if (last_model_context->model_handle == handle) {
+                model_context = last_model_context;
+                break;
+            }
+            previous_model_context = last_model_context;
+            last_model_context = last_model_context->next;
         }
     }
-    if (found == false) {
-        ALOGW("Can't sound model %d in registered list", handle);
+    if (!model_context) {
+        ALOGW("Can't find sound model handle %d in registered list", handle);
         pthread_mutex_unlock(&stdev->lock);
         return -ENOSYS;
     }
 
-    stdev->model_context[i].loaded_sound_model = 0;
-    stdev->model_context[i].sound_model_callback = NULL;
-    stdev->model_context[i].sound_model_cookie = NULL;
-
-    free(stdev->model_context[i].config);
-    stdev->model_context[i].config = NULL;
-    stdev->model_context[i].recognition_callback = NULL;
-    stdev->model_context[i].recognition_cookie = NULL;
+    if(previous_model_context) {
+        previous_model_context->next = model_context->next;
+    } else {
+        stdev->root_model_context = model_context->next;
+    }
+    free(model_context->config);
+    free(model_context);
 
     /* If no more models running with callbacks, stop trigger thread */
+    bool other_callbacks_found = false;
+    if (stdev->root_model_context) {
+        struct recognition_context *last_model_context = stdev->root_model_context;
+        while(last_model_context) {
+            if (last_model_context->recognition_callback != NULL) {
+                other_callbacks_found = true;
+                break;
+            }
+            last_model_context = last_model_context->next;
+        }
+    }
     if (!other_callbacks_found) {
         send_loop_kill_signal();
         pthread_mutex_unlock(&stdev->lock);
@@ -385,32 +451,36 @@
     ALOGI("%s", __func__);
     struct stub_sound_trigger_device *stdev = (struct stub_sound_trigger_device *)dev;
     pthread_mutex_lock(&stdev->lock);
-    unsigned int i;
-    bool found = false;
-    for(i = 0; i < hw_properties.max_sound_models; i++) {
-        if (stdev->model_context[i].loaded_sound_model == handle) {
-            found = true;
-            break;
+
+    struct recognition_context *model_context = NULL;
+    if (stdev->root_model_context) {
+        struct recognition_context *last_model_context = stdev->root_model_context;
+        while(last_model_context) {
+            if (last_model_context->model_handle == handle) {
+                model_context = last_model_context;
+                break;
+            }
+            last_model_context = last_model_context->next;
         }
     }
-    if (found == false) {
-        ALOGW("Can't sound model %d in registered list", handle);
+    if (!model_context) {
+        ALOGW("Can't find sound model handle %d in registered list", handle);
         pthread_mutex_unlock(&stdev->lock);
         return -ENOSYS;
     }
 
-    free(stdev->model_context[i].config);
-    stdev->model_context[i].config = NULL;
+    free(model_context->config);
+    model_context->config = NULL;
     if (config) {
-        stdev->model_context[i].config = malloc(sizeof(*config));
-        if (!stdev->model_context[i].config) {
+        model_context->config = malloc(sizeof(*config));
+        if (!model_context->config) {
             pthread_mutex_unlock(&stdev->lock);
             return -ENOMEM;
         }
-        memcpy(stdev->model_context[i].config, config, sizeof(*config));
+        memcpy(model_context->config, config, sizeof(*config));
     }
-    stdev->model_context[i].recognition_callback = callback;
-    stdev->model_context[i].recognition_cookie = cookie;
+    model_context->recognition_callback = callback;
+    model_context->recognition_cookie = cookie;
 
     pthread_create(&stdev->callback_thread, (const pthread_attr_t *) NULL,
                         callback_thread_loop, stdev);
@@ -424,48 +494,56 @@
     ALOGI("%s", __func__);
     pthread_mutex_lock(&stdev->lock);
 
-    unsigned int i;
-    bool found = false;
-    for(i = 0; i < hw_properties.max_sound_models; i++) {
-        if (stdev->model_context[i].loaded_sound_model == handle) {
-            found = true;
-            break;
+    struct recognition_context *model_context = NULL;
+    bool other_callbacks_found = false;
+    if (stdev->root_model_context) {
+        struct recognition_context *last_model_context = stdev->root_model_context;
+        while(last_model_context) {
+            if (last_model_context->model_handle == handle) {
+                model_context = last_model_context;
+            } else if (last_model_context->recognition_callback != NULL) {
+                other_callbacks_found = true;
+            }
+            last_model_context = last_model_context->next;
         }
     }
-    if (found == false) {
-        ALOGW("Can't sound model %d in registered list", handle);
+    if (!model_context) {
+        ALOGW("Can't find sound model handle %d in registered list", handle);
         pthread_mutex_unlock(&stdev->lock);
         return -ENOSYS;
     }
 
-    free(stdev->model_context[i].config);
-    stdev->model_context[i].config = NULL;
-    stdev->model_context[i].recognition_callback = NULL;
-    stdev->model_context[i].recognition_cookie = NULL;
+    free(model_context->config);
+    model_context->config = NULL;
+    model_context->recognition_callback = NULL;
+    model_context->recognition_cookie = NULL;
 
-    send_loop_kill_signal();
-    pthread_mutex_unlock(&stdev->lock);
-    pthread_join(stdev->callback_thread, (void **) NULL);
+    /* If no more models running with callbacks, stop trigger thread */
+    if (!other_callbacks_found) {
+        send_loop_kill_signal();
+        pthread_mutex_unlock(&stdev->lock);
+        pthread_join(stdev->callback_thread, (void **)NULL);
+    } else {
+        pthread_mutex_unlock(&stdev->lock);
+    }
+
     return 0;
 }
 
 __attribute__ ((visibility ("default")))
-int sound_trigger_open_for_streaming()
-{
+int sound_trigger_open_for_streaming() {
     int ret = 0;
     return ret;
 }
 
 __attribute__ ((visibility ("default")))
-size_t sound_trigger_read_samples(int audio_handle, void *buffer, size_t  buffer_len)
-{
+size_t sound_trigger_read_samples(int audio_handle, void *buffer, size_t  buffer_len) {
     size_t ret = 0;
     return ret;
 }
 
 __attribute__ ((visibility ("default")))
-int sound_trigger_close_for_streaming(int audio_handle __unused)
-{
+int sound_trigger_close_for_streaming(int audio_handle __unused) {
     return 0;
 }
 
@@ -486,21 +564,7 @@
     if (!stdev)
         return -ENOMEM;
 
-    if (MAX_MAX_SOUND_MODELS < hw_properties.max_sound_models) {
-        ALOGW("max_sound_models is greater than the allowed %d", MAX_MAX_SOUND_MODELS);
-        return -EINVAL;
-    }
-
     stdev->next_sound_model_id = 1;
-    unsigned int i;
-    for(i = 0; i < hw_properties.max_sound_models; i++) {
-        stdev->model_context[i].loaded_sound_model = 0;
-        stdev->model_context[i].sound_model_callback = NULL;
-        stdev->model_context[i].sound_model_cookie = NULL;
-        stdev->model_context[i].config = NULL;
-        stdev->model_context[i].recognition_callback = NULL;
-        stdev->model_context[i].recognition_cookie = NULL;
-    }
 
     stdev->device.common.tag = HARDWARE_DEVICE_TAG;
     stdev->device.common.version = SOUND_TRIGGER_DEVICE_API_VERSION_1_0;
@@ -534,3 +598,4 @@
         .methods = &hal_module_methods,
     },
 };
+