Adds sound model update event, adds helper methods

Change-Id: Ifb03843a51a796e1fdb224d4671a52f84b4fceed
diff --git a/modules/soundtrigger/sound_trigger_hw.c b/modules/soundtrigger/sound_trigger_hw.c
index 8083b1f..972c520 100644
--- a/modules/soundtrigger/sound_trigger_hw.c
+++ b/modules/soundtrigger/sound_trigger_hw.c
@@ -25,6 +25,13 @@
  * model, you can also send $'\002' etc. $'\000' is the kill
  * signal for the trigger listening thread.
  *
+ * The default event type is a Recognition event, to specify a
+ * type of event, you can send another byte. $'\000' corresponds
+ * to a recognition event, $'\001' is a sound model event.
+ *
+ * adb forward tcp:14035 tcp:14035
+ * echo $'\001'$'\001' | nc -q -1 localhost 14035
+ *
  * To enable this file, you can make with command line parameter
  * SOUND_TRIGGER_USE_STUB_MODULE=1
  */
@@ -179,7 +186,7 @@
         event = (struct sound_trigger_phrase_recognition_event *)
                sound_trigger_event_alloc(model_handle, model_type, config);
         if (event) {
-            recognition_callback(&event->common, recognition_cookie);
+            recognition_callback(event, recognition_cookie);
             free(event);
         }
     } else if (model_type == SOUND_MODEL_TYPE_GENERIC) {
@@ -187,7 +194,7 @@
         event = (struct sound_trigger_generic_recognition_event *)
                 sound_trigger_event_alloc(model_handle, model_type, config);
         if (event) {
-            recognition_callback(&event->common, recognition_cookie);
+            recognition_callback(event, recognition_cookie);
             free(event);
         }
     } else {
@@ -195,6 +202,63 @@
     }
 }
 
+static void send_model_event(sound_model_handle_t model_handle,
+            sound_model_callback_t model_callback, void *model_cookie) {
+
+    if (model_callback == NULL) {
+        ALOGI("%s No matching callback for handle %d", __func__, model_handle);
+        return;
+    }
+
+    char *data;
+    data = (char *)calloc(1, sizeof(struct sound_trigger_model_event));
+    if (!data) {
+        ALOGW("%s Could not allocate event %d", __func__, model_handle);
+        return;
+    }
+
+    struct sound_trigger_model_event *event;
+    event = (struct sound_trigger_model_event *)data;
+    event->status = SOUND_MODEL_STATUS_UPDATED;
+    event->model = model_handle;
+
+    if (event) {
+        model_callback(&event, model_cookie);
+        free(event);
+    }
+}
+
+static bool recognition_callback_exists(struct stub_sound_trigger_device *stdev) {
+    bool callback_found = false;
+    if (stdev->root_model_context) {
+        struct recognition_context *current_model_context = stdev->root_model_context;
+        while(current_model_context) {
+            if (current_model_context->recognition_callback != NULL) {
+                callback_found = true;
+                break;
+            }
+            current_model_context = current_model_context->next;
+        }
+    }
+    return callback_found;
+}
+
+static struct recognition_context * get_model_context(struct stub_sound_trigger_device *stdev,
+            sound_model_handle_t handle) {
+    struct recognition_context *model_context = NULL;
+    if (stdev->root_model_context) {
+        struct recognition_context *current_model_context = stdev->root_model_context;
+        while(current_model_context) {
+            if (current_model_context->model_handle == handle) {
+                model_context = current_model_context;
+                break;
+            }
+            current_model_context = current_model_context->next;
+        }
+    }
+    return model_context;
+}
+
 static void *callback_thread_loop(void *context) {
     struct stub_sound_trigger_device *stdev = (struct stub_sound_trigger_device *)context;
     struct sockaddr_in incoming_info;
@@ -209,7 +273,7 @@
     bool exit = false;
     while(!exit) {
         int received_count;
-        int requested_count = 1;
+        int requested_count = 2;
         char buffer[requested_count];
         ALOGE("Opening socket");
         self_socket = socket(AF_INET, SOCK_STREAM, 0);
@@ -245,34 +309,61 @@
             }
             ALOGI("Connection from %s", inet_ntoa(incoming_info.sin_addr));
             received_count = recv(con_socket, buffer, requested_count, 0);
-            unsigned int index = buffer[0] - 1;
-            ALOGI("Received data");
             pthread_mutex_lock(&stdev->lock);
             if (received_count > 0) {
+                unsigned int index = buffer[0] - 1;
+                ALOGI("Received data");
                 if (buffer[0] == 0) {
                     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 index #%d", index );
+                    int TYPE_RECOGNITION = 0;
+                    int TYPE_MODEL_UPDATE = 1;
+                    int action = TYPE_RECOGNITION;
+
+                    if (received_count > 1) {
+                        // got a second instruction
+                        action = buffer[1];
+                        ALOGI("Got action instruction: %d", action);
+                    }
+
                     struct recognition_context *model_context = NULL;
-                    struct recognition_context *last_model_context = stdev->root_model_context;
+                    struct recognition_context *current_model_context = stdev->root_model_context;
                     int model_index = 0;
-                    while(last_model_context) {
+                    while(current_model_context) {
                         if (model_index == index) {
-                            model_context = last_model_context;
+                            model_context = current_model_context;
                             break;
                         }
-                        last_model_context = last_model_context->next;
+                        current_model_context = current_model_context->next;
                         model_index++;
                     }
-                    if (model_context) {
+
+                    if (!model_context) {
+                        ALOGI("Sound Model Does Not Exist at this Index: %d", index);
+                    } else if (action == TYPE_RECOGNITION) {
+                        ALOGI("Going to send trigger for model index #%d", index );
                         send_recognition_event(model_context->model_handle,
                                   model_context->model_type,
                                   model_context->recognition_callback,
                                   model_context->recognition_cookie,
                                   model_context->config);
+
+                        // After recognition callback, stop recognition
+                        free(model_context->config);
+                        model_context->config = NULL;
+                        model_context->recognition_callback = NULL;
+                        model_context->recognition_cookie = NULL;
+                        if (!recognition_callback_exists(stdev)) {
+                            exit = true;
+                        }
+                    } else if (action == TYPE_MODEL_UPDATE) {
+                        ALOGI("Going to send sound model update for model index #%d", index );
+                        send_model_event(model_context->model_handle,
+                                  model_context->model_callback,
+                                  model_context->model_cookie);
                     } else {
-                        ALOGI("Sound Model Does Not Exist at this Index: %d", index);
+                        ALOGI("Action/event type is not recognized: %d", action);
                     }
                 } else {
                     ALOGI("Data is not recognized: %d", index);
@@ -351,10 +442,10 @@
     // 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;
+        struct recognition_context *current_model_context = stdev->root_model_context;
         int model_count = 0;
-        while(last_model_context->next) {
-            last_model_context = last_model_context->next;
+        while(current_model_context->next) {
+            current_model_context = current_model_context->next;
             model_count++;
             if (model_count >= hw_properties.max_sound_models) {
                 ALOGW("Can't load model: reached max sound model limit");
@@ -363,7 +454,7 @@
                 return -ENOSYS;
             }
         }
-        last_model_context->next = model_context;
+        current_model_context->next = model_context;
     } else {
         stdev->root_model_context = model_context;
     }
@@ -387,23 +478,23 @@
 
 static int stdev_unload_sound_model(const struct sound_trigger_hw_device *dev,
                                     sound_model_handle_t handle) {
+    // If recognizing, stop_recognition must be called for a sound model before unload_sound_model
     struct stub_sound_trigger_device *stdev = (struct stub_sound_trigger_device *)dev;
     int status = 0;
     ALOGI("unload_sound_model");
     pthread_mutex_lock(&stdev->lock);
 
-
     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;
+        struct recognition_context *current_model_context = stdev->root_model_context;
+        while(current_model_context) {
+            if (current_model_context->model_handle == handle) {
+                model_context = current_model_context;
                 break;
             }
-            previous_model_context = last_model_context;
-            last_model_context = last_model_context->next;
+            previous_model_context = current_model_context;
+            current_model_context = current_model_context->next;
         }
     }
     if (!model_context) {
@@ -420,25 +511,7 @@
     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);
-        pthread_join(stdev->callback_thread, (void **)NULL);
-    } else {
-        pthread_mutex_unlock(&stdev->lock);
-    }
+    pthread_mutex_unlock(&stdev->lock);
 
     return status;
 }
@@ -452,17 +525,10 @@
     struct stub_sound_trigger_device *stdev = (struct stub_sound_trigger_device *)dev;
     pthread_mutex_lock(&stdev->lock);
 
-    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 other models running with callbacks, don't start trigger thread */
+    bool other_callbacks_found = recognition_callback_exists(stdev);
+
+    struct recognition_context *model_context = get_model_context(stdev, handle);
     if (!model_context) {
         ALOGW("Can't find sound model handle %d in registered list", handle);
         pthread_mutex_unlock(&stdev->lock);
@@ -482,31 +548,22 @@
     model_context->recognition_callback = callback;
     model_context->recognition_cookie = cookie;
 
-    pthread_create(&stdev->callback_thread, (const pthread_attr_t *) NULL,
-                        callback_thread_loop, stdev);
+    if (!other_callbacks_found) {
+        pthread_create(&stdev->callback_thread, (const pthread_attr_t *) NULL,
+                callback_thread_loop, stdev);
+    }
+
     pthread_mutex_unlock(&stdev->lock);
     return 0;
 }
 
 static int stdev_stop_recognition(const struct sound_trigger_hw_device *dev,
-                                 sound_model_handle_t handle) {
+            sound_model_handle_t handle) {
     struct stub_sound_trigger_device *stdev = (struct stub_sound_trigger_device *)dev;
     ALOGI("%s", __func__);
     pthread_mutex_lock(&stdev->lock);
 
-    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;
-        }
-    }
+    struct recognition_context *model_context = get_model_context(stdev, handle);
     if (!model_context) {
         ALOGW("Can't find sound model handle %d in registered list", handle);
         pthread_mutex_unlock(&stdev->lock);
@@ -519,7 +576,7 @@
     model_context->recognition_cookie = NULL;
 
     /* If no more models running with callbacks, stop trigger thread */
-    if (!other_callbacks_found) {
+    if (!recognition_callback_exists(stdev)) {
         send_loop_kill_signal();
         pthread_mutex_unlock(&stdev->lock);
         pthread_join(stdev->callback_thread, (void **)NULL);