1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 18/* This HAL simulates triggers from the DSP. 19 * To send a trigger from the command line you can type: 20 * 21 * adb forward tcp:14035 tcp:14035 22 * 23 * telnet localhost 14035 24 * 25 * Commands include: 26 * ls : Lists all models that have been loaded. 27 * trig <uuid> : Sends a recognition event for the model at the given uuid 28 * update <uuid> : Sends a model update event for the model at the given uuid. 29 * close : Closes the network connection. 30 * 31 * To enable this file, you can make with command line parameter 32 * SOUND_TRIGGER_USE_STUB_MODULE=1 33 */ 34 35#define LOG_TAG "sound_trigger_hw_default" 36#define LOG_NDEBUG 1 37#define PARSE_BUF_LEN 1024 // Length of the parsing buffer.S 38 39#define EVENT_RECOGNITION 1 40#define EVENT_SOUND_MODEL 2 41 42// The following commands work with the network port: 43#define COMMAND_LS "ls" 44#define COMMAND_RECOGNITION_TRIGGER "trig" // Argument: model index. 45#define COMMAND_RECOGNITION_ABORT "abort" // Argument: model index. 46#define COMMAND_RECOGNITION_FAILURE "fail" // Argument: model index. 47#define COMMAND_UPDATE "update" // Argument: model index. 48#define COMMAND_CLEAR "clear" // Removes all models from the list. 49#define COMMAND_CLOSE "close" // Close just closes the network port, keeps thread running. 50#define COMMAND_END "end" // Closes connection and stops the thread. 51 52#define ERROR_BAD_COMMAND "Bad command" 53 54#include <arpa/inet.h> 55#include <errno.h> 56#include <pthread.h> 57#include <netinet/in.h> 58#include <stdarg.h> 59#include <stdio.h> 60#include <stdlib.h> 61#include <string.h> 62#include <sys/prctl.h> 63#include <sys/types.h> 64#include <sys/socket.h> 65#include <unistd.h> 66 67#include <log/log.h> 68 69#include <hardware/hardware.h> 70#include <system/sound_trigger.h> 71#include <hardware/sound_trigger.h> 72 73static const struct sound_trigger_properties hw_properties = { 74 "The Android Open Source Project", // implementor 75 "Sound Trigger stub HAL", // description 76 1, // version 77 { 0xed7a7d60, 0xc65e, 0x11e3, 0x9be4, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } }, // uuid 78 4, // max_sound_models 79 1, // max_key_phrases 80 1, // max_users 81 RECOGNITION_MODE_VOICE_TRIGGER, // recognition_modes 82 false, // capture_transition 83 0, // max_buffer_ms 84 true, // concurrent_capture 85 false, // trigger_in_event 86 0 // power_consumption_mw 87}; 88 89struct recognition_context { 90 // Sound Model information, added in method load_sound_model 91 sound_model_handle_t model_handle; 92 sound_trigger_uuid_t model_uuid; 93 sound_trigger_sound_model_type_t model_type; 94 sound_model_callback_t model_callback; 95 void *model_cookie; 96 97 // Sound Model information, added in start_recognition 98 struct sound_trigger_recognition_config *config; 99 recognition_callback_t recognition_callback; 100 void *recognition_cookie; 101 102 bool model_started; 103 104 // Next recognition_context in the linked list 105 struct recognition_context *next; 106}; 107 108char tmp_write_buffer[PARSE_BUF_LEN]; 109 110struct stub_sound_trigger_device { 111 struct sound_trigger_hw_device device; 112 pthread_mutex_t lock; 113 114 // This thread opens a port that can be used to monitor and inject events 115 // into the stub HAL. 116 pthread_t control_thread; 117 118 // Recognition contexts are stored as a linked list 119 struct recognition_context *root_model_context; 120 121 int next_sound_model_id; 122}; 123 124static bool check_uuid_equality(sound_trigger_uuid_t uuid1, sound_trigger_uuid_t uuid2) { 125 if (uuid1.timeLow != uuid2.timeLow || 126 uuid1.timeMid != uuid2.timeMid || 127 uuid1.timeHiAndVersion != uuid2.timeHiAndVersion || 128 uuid1.clockSeq != uuid2.clockSeq) { 129 return false; 130 } 131 for (int i = 0; i < 6; i++) { 132 if(uuid1.node[i] != uuid2.node[i]) { 133 return false; 134 } 135 } 136 return true; 137} 138 139bool str_to_uuid(char* uuid_str, sound_trigger_uuid_t* uuid) { 140 if (uuid_str == NULL) { 141 ALOGI("Invalid str_to_uuid input."); 142 return false; 143 } 144 145 int tmp[10]; 146 if (sscanf(uuid_str, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", 147 tmp, tmp+1, tmp+2, tmp+3, tmp+4, tmp+5, tmp+6, tmp+7, tmp+8, tmp+9) < 10) { 148 ALOGI("Invalid UUID, got: %s", uuid_str); 149 return false; 150 } 151 uuid->timeLow = (unsigned int)tmp[0]; 152 uuid->timeMid = (unsigned short)tmp[1]; 153 uuid->timeHiAndVersion = (unsigned short)tmp[2]; 154 uuid->clockSeq = (unsigned short)tmp[3]; 155 uuid->node[0] = (unsigned char)tmp[4]; 156 uuid->node[1] = (unsigned char)tmp[5]; 157 uuid->node[2] = (unsigned char)tmp[6]; 158 uuid->node[3] = (unsigned char)tmp[7]; 159 uuid->node[4] = (unsigned char)tmp[8]; 160 uuid->node[5] = (unsigned char)tmp[9]; 161 return true; 162} 163 164void write_bad_command_error(int conn_socket, char* command) { 165 int num = snprintf(tmp_write_buffer, PARSE_BUF_LEN, "Bad command received: %s", command); 166 tmp_write_buffer[PARSE_BUF_LEN - 1] = '\0'; // Just to be sure. 167 tmp_write_buffer[PARSE_BUF_LEN - 2] = '\n'; 168 write(conn_socket, tmp_write_buffer, num); 169} 170 171void write_string(int conn_socket, char* str) { 172 int num = snprintf(tmp_write_buffer, PARSE_BUF_LEN, "%s", str); 173 tmp_write_buffer[PARSE_BUF_LEN - 1] = '\0'; 174 tmp_write_buffer[PARSE_BUF_LEN - 2] = '\n'; 175 write(conn_socket, tmp_write_buffer, num); 176} 177 178void write_vastr(int conn_socket, char* format, ...) { 179 va_list argptr; 180 va_start(argptr, format); 181 int num = vsnprintf(tmp_write_buffer, PARSE_BUF_LEN, format, argptr); 182 va_end(argptr); 183 tmp_write_buffer[PARSE_BUF_LEN - 1] = '\0'; 184 tmp_write_buffer[PARSE_BUF_LEN - 2] = '\n'; 185 write(conn_socket, tmp_write_buffer, num); 186} 187 188static void print_uuid(sound_trigger_uuid_t uuid) { 189 ALOGI("%s %08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", __func__, uuid.timeLow, uuid.timeMid, 190 uuid.timeHiAndVersion, uuid.clockSeq, uuid.node[0], uuid.node[1], uuid.node[2], 191 uuid.node[3], uuid.node[4], uuid.node[5]); 192} 193 194static void write_uuid(int conn_socket, sound_trigger_uuid_t uuid) { 195 write_vastr(conn_socket, "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x\n", uuid.timeLow, uuid.timeMid, 196 uuid.timeHiAndVersion, uuid.clockSeq, uuid.node[0], uuid.node[1], uuid.node[2], 197 uuid.node[3], uuid.node[4], uuid.node[5]); 198} 199 200// Returns model at the given index, null otherwise (error, doesn't exist, etc). 201// Note that here index starts from zero. 202struct recognition_context* fetch_model_with_handle( 203 struct stub_sound_trigger_device* stdev, sound_model_handle_t* model_handle) { 204 ALOGI("%s", __func__); 205 struct recognition_context *model_context = NULL; 206 struct recognition_context *last_model_context = stdev->root_model_context; 207 while(last_model_context) { 208 if (last_model_context->model_handle == *model_handle) { 209 model_context = last_model_context; 210 break; 211 } 212 last_model_context = last_model_context->next; 213 } 214 return model_context; 215} 216 217// Returns the first model that matches the sound model UUID. 218static sound_model_handle_t* get_model_handle_with_uuid(struct stub_sound_trigger_device* stdev, 219 sound_trigger_uuid_t uuid) { 220 sound_model_handle_t* model_handle_str = NULL; 221 struct recognition_context *last_model_context = stdev->root_model_context; 222 while(last_model_context) { 223 if (check_uuid_equality(last_model_context->model_uuid, uuid)) { 224 model_handle_str = &last_model_context->model_handle; 225 break; 226 } 227 last_model_context = last_model_context->next; 228 } 229 return model_handle_str; 230} 231 232/* Will reuse ids when overflow occurs */ 233static sound_model_handle_t generate_sound_model_handle(const struct sound_trigger_hw_device *dev) { 234 struct stub_sound_trigger_device *stdev = (struct stub_sound_trigger_device *)dev; 235 int new_id = stdev->next_sound_model_id; 236 ++stdev->next_sound_model_id; 237 if (stdev->next_sound_model_id == 0) { 238 stdev->next_sound_model_id = 1; 239 } 240 return (sound_model_handle_t) new_id; 241} 242 243bool parse_socket_data(int conn_socket, struct stub_sound_trigger_device* stdev); 244static void unload_all_sound_models(struct stub_sound_trigger_device *stdev); 245 246static char *sound_trigger_keyphrase_event_alloc(sound_model_handle_t handle, 247 struct sound_trigger_recognition_config *config, 248 int recognition_status) { 249 char *data; 250 struct sound_trigger_phrase_recognition_event *event; 251 data = (char *)calloc(1, sizeof(struct sound_trigger_phrase_recognition_event)); 252 if (!data) 253 return NULL; 254 event = (struct sound_trigger_phrase_recognition_event *)data; 255 event->common.status = recognition_status; 256 event->common.type = SOUND_MODEL_TYPE_KEYPHRASE; 257 event->common.model = handle; 258 259 if (config) { 260 unsigned int i; 261 262 event->num_phrases = config->num_phrases; 263 if (event->num_phrases > SOUND_TRIGGER_MAX_PHRASES) 264 event->num_phrases = SOUND_TRIGGER_MAX_PHRASES; 265 for (i=0; i < event->num_phrases; i++) 266 memcpy(&event->phrase_extras[i], 267 &config->phrases[i], 268 sizeof(struct sound_trigger_phrase_recognition_extra)); 269 } 270 271 event->num_phrases = 1; 272 event->phrase_extras[0].confidence_level = 100; 273 event->phrase_extras[0].num_levels = 1; 274 event->phrase_extras[0].levels[0].level = 100; 275 event->phrase_extras[0].levels[0].user_id = 0; 276 // Signify that all the data is comming through streaming, not through the buffer. 277 event->common.capture_available = true; 278 event->common.audio_config = AUDIO_CONFIG_INITIALIZER; 279 event->common.audio_config.sample_rate = 16000; 280 event->common.audio_config.channel_mask = AUDIO_CHANNEL_IN_MONO; 281 event->common.audio_config.format = AUDIO_FORMAT_PCM_16_BIT; 282 return data; 283} 284 285static char *sound_trigger_generic_event_alloc(sound_model_handle_t handle, 286 struct sound_trigger_recognition_config *config, 287 int recognition_status) { 288 char *data; 289 struct sound_trigger_generic_recognition_event *event; 290 data = (char *)calloc(1, sizeof(struct sound_trigger_generic_recognition_event)); 291 if (!data) 292 return NULL; 293 event = (struct sound_trigger_generic_recognition_event *)data; 294 event->common.status = recognition_status; 295 event->common.type = SOUND_MODEL_TYPE_GENERIC; 296 event->common.model = handle; 297 298 // Signify that all the data is comming through streaming, not through the buffer. 299 event->common.capture_available = true; 300 event->common.audio_config = AUDIO_CONFIG_INITIALIZER; 301 event->common.audio_config.sample_rate = 16000; 302 event->common.audio_config.channel_mask = AUDIO_CHANNEL_IN_MONO; 303 event->common.audio_config.format = AUDIO_FORMAT_PCM_16_BIT; 304 return data; 305} 306 307void send_event_with_handle(sound_model_handle_t* model_handle_str, 308 struct stub_sound_trigger_device* stdev, int event_type, 309 int status) { 310 ALOGI("%s", __func__); 311 struct recognition_context *model_context = fetch_model_with_handle(stdev, model_handle_str); 312 if (model_context) { 313 if (event_type == EVENT_RECOGNITION) { 314 if (model_context->recognition_callback == NULL) { 315 ALOGI("%s No matching callback", __func__); 316 return; 317 } 318 319 if (model_context->model_type == SOUND_MODEL_TYPE_KEYPHRASE) { 320 struct sound_trigger_phrase_recognition_event *event; 321 event = (struct sound_trigger_phrase_recognition_event *) 322 sound_trigger_keyphrase_event_alloc(model_context->model_handle, 323 model_context->config, status); 324 if (event) { 325 model_context->recognition_callback(event, model_context->recognition_cookie); 326 free(event); 327 } 328 } else if (model_context->model_type == SOUND_MODEL_TYPE_GENERIC) { 329 struct sound_trigger_generic_recognition_event *event; 330 event = (struct sound_trigger_generic_recognition_event *) 331 sound_trigger_generic_event_alloc(model_context->model_handle, 332 model_context->config, status); 333 if (event) { 334 model_context->recognition_callback(event, model_context->recognition_cookie); 335 free(event); 336 } 337 } else { 338 ALOGI("Unknown Sound Model Type, No Event to Send"); 339 } 340 } else if (event_type == EVENT_SOUND_MODEL) { 341 char *data; 342 data = (char *)calloc(1, sizeof(struct sound_trigger_model_event)); 343 if (!data) { 344 ALOGW("%s Could not allocate event", __func__); 345 return; 346 } 347 348 struct sound_trigger_model_event *event; 349 event = (struct sound_trigger_model_event *)data; 350 event->status = SOUND_MODEL_STATUS_UPDATED; 351 event->model = model_context->model_handle; 352 if (event) { 353 model_context->model_callback(&event, model_context->model_cookie); 354 free(event); 355 } 356 } 357 } else { 358 ALOGI("No model for this handle"); 359 } 360} 361 362static void send_event(int conn_socket, struct stub_sound_trigger_device* stdev, int event_type, 363 int status) { 364 char* model_uuid_str = strtok(NULL, " \r\n"); 365 sound_trigger_uuid_t model_uuid; 366 if (str_to_uuid(model_uuid_str, &model_uuid)) { 367 sound_model_handle_t* model_handle_str = get_model_handle_with_uuid(stdev, model_uuid); 368 if (model_handle_str == NULL) { 369 ALOGI("%s Bad sound model handle.", __func__); 370 write_string(conn_socket, "Bad sound model handle.\n"); 371 return; 372 } 373 send_event_with_handle(model_handle_str, stdev, event_type, status); 374 } else { 375 ALOGI("%s Not a valid UUID", __func__); 376 write_string(conn_socket, "Not a valid UUID.\n"); 377 } 378} 379 380static bool recognition_callback_exists(struct stub_sound_trigger_device *stdev) { 381 bool callback_found = false; 382 if (stdev->root_model_context) { 383 struct recognition_context *current_model_context = stdev->root_model_context; 384 while(current_model_context) { 385 if (current_model_context->recognition_callback != NULL) { 386 callback_found = true; 387 break; 388 } 389 current_model_context = current_model_context->next; 390 } 391 } 392 return callback_found; 393} 394 395static struct recognition_context * get_model_context(struct stub_sound_trigger_device *stdev, 396 sound_model_handle_t handle) { 397 struct recognition_context *model_context = NULL; 398 if (stdev->root_model_context) { 399 struct recognition_context *current_model_context = stdev->root_model_context; 400 while(current_model_context) { 401 if (current_model_context->model_handle == handle) { 402 model_context = current_model_context; 403 break; 404 } 405 current_model_context = current_model_context->next; 406 } 407 } 408 return model_context; 409} 410 411static void *control_thread_loop(void *context) { 412 struct stub_sound_trigger_device *stdev = (struct stub_sound_trigger_device *)context; 413 struct sockaddr_in incoming_info; 414 struct sockaddr_in self_info; 415 int self_socket; 416 socklen_t sock_size = sizeof(struct sockaddr_in); 417 memset(&self_info, 0, sizeof(self_info)); 418 self_info.sin_family = AF_INET; 419 self_info.sin_addr.s_addr = htonl(INADDR_ANY); 420 self_info.sin_port = htons(14035); 421 422 bool exit = false; 423 while(!exit) { 424 int received_count; 425 int requested_count = 2; 426 char buffer[requested_count]; 427 ALOGE("Opening socket"); 428 self_socket = socket(AF_INET, SOCK_STREAM, 0); 429 if (self_socket < 0) { 430 ALOGE("Error on socket creation: %s", strerror(errno)); 431 exit = true; 432 } else { 433 ALOGI("Socket created"); 434 } 435 436 int reuse = 1; 437 if (setsockopt(self_socket, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) < 0) { 438 ALOGE("setsockopt(SO_REUSEADDR) failed"); 439 } 440 441 int bind_result = bind(self_socket, (struct sockaddr *)&self_info, sizeof(struct sockaddr)); 442 if (bind_result < 0) { 443 ALOGE("Error on bind"); 444 exit = true; 445 } 446 447 int listen_result = listen(self_socket, 1); 448 if (listen_result < 0) { 449 ALOGE("Error on Listen"); 450 exit = true; 451 } 452 453 while(!exit) { 454 int con_socket = accept(self_socket, (struct sockaddr *)&incoming_info, &sock_size); 455 if (!con_socket) { 456 ALOGE("Lost socket, cannot send trigger"); 457 break; 458 } 459 ALOGI("Connection from %s", inet_ntoa(incoming_info.sin_addr)); 460 if (!parse_socket_data(con_socket, stdev)) { 461 ALOGI("Done processing commands over network. Stopping thread."); 462 exit = true; 463 } 464 close(con_socket); 465 } 466 ALOGE("Closing socket"); 467 close(self_socket); 468 } 469 470 return NULL; 471} 472 473void list_models(int conn_socket, char* buffer, 474 struct stub_sound_trigger_device* stdev) { 475 ALOGI("%s", __func__); 476 struct recognition_context *last_model_context = stdev->root_model_context; 477 unsigned int model_index = 0; 478 write_string(conn_socket, "-----------------------\n"); 479 if (!last_model_context) { 480 ALOGI("ZERO Models exist."); 481 write_string(conn_socket, "Zero models exist.\n"); 482 } 483 while (last_model_context) { 484 write_vastr(conn_socket, "Model Index: %d\n", model_index); 485 ALOGI("Model Index: %d", model_index); 486 write_vastr(conn_socket, "Model handle: %d\n", last_model_context->model_handle); 487 ALOGI("Model handle: %d", last_model_context->model_handle); 488 write_uuid(conn_socket, last_model_context->model_uuid); 489 print_uuid(last_model_context->model_uuid); 490 sound_trigger_sound_model_type_t model_type = last_model_context->model_type; 491 492 if (model_type == SOUND_MODEL_TYPE_KEYPHRASE) { 493 write_string(conn_socket, "Keyphrase sound Model.\n"); 494 ALOGI("Keyphrase sound Model."); 495 } else if (model_type == SOUND_MODEL_TYPE_GENERIC) { 496 write_string(conn_socket, "Generic sound Model.\n"); 497 ALOGI("Generic sound Model."); 498 } else { 499 write_vastr(conn_socket, "Unknown sound model type: %d\n", model_type); 500 ALOGI("Unknown sound model type: %d", model_type); 501 } 502 if (last_model_context->model_started) { 503 write_string(conn_socket, "Model started.\n"); 504 ALOGI("Model started.\n"); 505 } else { 506 write_string(conn_socket, "Model stopped.\n"); 507 ALOGI("Model stopped.\n"); 508 } 509 write_string(conn_socket, "-----------------------\n\n"); 510 ALOGI("----\n\n"); 511 last_model_context = last_model_context->next; 512 model_index++; 513 } 514} 515 516// Gets the next word from buffer, replaces '\n' or ' ' with '\0'. 517char* get_command(char* buffer) { 518 char* command = strtok(buffer, " "); 519 char* newline = strchr(command, '\n'); 520 if (newline != NULL) { 521 *newline = '\0'; 522 } 523 return command; 524} 525 526// Parses data coming in from the local socket, executes commands. Returns when 527// done. Return code indicates whether the server should continue listening or 528// abort (true if continue listening). 529bool parse_socket_data(int conn_socket, struct stub_sound_trigger_device* stdev) { 530 ALOGI("Calling parse_socket_data"); 531 bool input_done = false; 532 char buffer[PARSE_BUF_LEN]; 533 FILE* input_fp = fdopen(conn_socket, "r"); 534 bool continue_listening = true; 535 536 // Note: Since we acquire a lock inside this loop, do not use break or other 537 // exit methods without releasing this lock. 538 write_string(conn_socket, "\n>>> "); 539 while(!input_done) { 540 if (fgets(buffer, PARSE_BUF_LEN, input_fp) != NULL) { 541 pthread_mutex_lock(&stdev->lock); 542 char* command = strtok(buffer, " \r\n"); 543 if (command == NULL) { 544 write_bad_command_error(conn_socket, command); 545 } else if (strncmp(command, COMMAND_LS, 2) == 0) { 546 list_models(conn_socket, buffer, stdev); 547 } else if (strcmp(command, COMMAND_RECOGNITION_TRIGGER) == 0) { 548 send_event(conn_socket, stdev, EVENT_RECOGNITION, RECOGNITION_STATUS_SUCCESS); 549 } else if (strcmp(command, COMMAND_RECOGNITION_ABORT) == 0) { 550 send_event(conn_socket, stdev, EVENT_RECOGNITION, RECOGNITION_STATUS_ABORT); 551 } else if (strcmp(command, COMMAND_RECOGNITION_FAILURE) == 0) { 552 send_event(conn_socket, stdev, EVENT_RECOGNITION, RECOGNITION_STATUS_FAILURE); 553 } else if (strcmp(command, COMMAND_UPDATE) == 0) { 554 send_event(conn_socket, stdev, EVENT_SOUND_MODEL, SOUND_MODEL_STATUS_UPDATED); 555 } else if (strncmp(command, COMMAND_CLEAR, 5) == 0) { 556 unload_all_sound_models(stdev); 557 } else if (strncmp(command, COMMAND_CLOSE, 5) == 0) { 558 ALOGI("Closing this connection."); 559 write_string(conn_socket, "Closing this connection."); 560 input_done = true; 561 } else if (strncmp(command, COMMAND_END, 3) == 0) { 562 ALOGI("End command received."); 563 write_string(conn_socket, "End command received. Stopping connection."); 564 continue_listening = false; 565 input_done = true; 566 } else { 567 write_vastr(conn_socket, "\nBad command %s.\n\n", command); 568 } 569 pthread_mutex_unlock(&stdev->lock); 570 } else { 571 ALOGI("parse_socket_data done (got null)"); 572 input_done = true; // break. 573 } 574 write_string(conn_socket, "\n>>> "); 575 } 576 return continue_listening; 577} 578 579static void send_loop_kill_signal() { 580 ALOGI("Sending loop thread kill signal"); 581 int self_socket = socket(AF_INET, SOCK_STREAM, 0); 582 struct sockaddr_in remote_info; 583 memset(&remote_info, 0, sizeof(remote_info)); 584 remote_info.sin_family = AF_INET; 585 remote_info.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 586 remote_info.sin_port = htons(14035); 587 if (connect(self_socket, (struct sockaddr *)&remote_info, sizeof(struct sockaddr)) == 0) { 588 send(self_socket, COMMAND_END, 3, 0); 589 } else { 590 ALOGI("Could not connect"); 591 } 592 close(self_socket); 593 ALOGI("Sent loop thread kill signal"); 594} 595 596static int stdev_get_properties(const struct sound_trigger_hw_device *dev, 597 struct sound_trigger_properties *properties) { 598 struct stub_sound_trigger_device *stdev = (struct stub_sound_trigger_device *)dev; 599 600 ALOGI("%s", __func__); 601 if (properties == NULL) 602 return -EINVAL; 603 memcpy(properties, &hw_properties, sizeof(struct sound_trigger_properties)); 604 return 0; 605} 606 607static int stdev_load_sound_model(const struct sound_trigger_hw_device *dev, 608 struct sound_trigger_sound_model *sound_model, 609 sound_model_callback_t callback, 610 void *cookie, 611 sound_model_handle_t *handle) { 612 struct stub_sound_trigger_device *stdev = (struct stub_sound_trigger_device *)dev; 613 ALOGI("%s stdev %p", __func__, stdev); 614 int status = 0; 615 pthread_mutex_lock(&stdev->lock); 616 617 if (handle == NULL || sound_model == NULL) { 618 pthread_mutex_unlock(&stdev->lock); 619 return -EINVAL; 620 } 621 if (sound_model->data_size == 0 || 622 sound_model->data_offset < sizeof(struct sound_trigger_sound_model)) { 623 pthread_mutex_unlock(&stdev->lock); 624 return -EINVAL; 625 } 626 627 struct recognition_context *model_context; 628 model_context = malloc(sizeof(struct recognition_context)); 629 if(!model_context) { 630 ALOGW("Could not allocate recognition_context"); 631 pthread_mutex_unlock(&stdev->lock); 632 return -ENOSYS; 633 } 634 635 // Add the new model context to the recognition_context linked list 636 if (stdev->root_model_context) { 637 // Find the tail 638 struct recognition_context *current_model_context = stdev->root_model_context; 639 unsigned int model_count = 0; 640 while(current_model_context->next) { 641 current_model_context = current_model_context->next; 642 model_count++; 643 if (model_count >= hw_properties.max_sound_models) { 644 ALOGW("Can't load model: reached max sound model limit"); 645 free(model_context); 646 pthread_mutex_unlock(&stdev->lock); 647 return -ENOSYS; 648 } 649 } 650 current_model_context->next = model_context; 651 } else { 652 stdev->root_model_context = model_context; 653 } 654 655 model_context->model_handle = generate_sound_model_handle(dev); 656 *handle = model_context->model_handle; 657 model_context->model_type = sound_model->type; 658 659 char *data = (char *)sound_model + sound_model->data_offset; 660 ALOGI("%s data size %d data %d - %d", __func__, 661 sound_model->data_size, data[0], data[sound_model->data_size - 1]); 662 model_context->model_uuid = sound_model->uuid; 663 model_context->model_callback = callback; 664 model_context->model_cookie = cookie; 665 model_context->config = NULL; 666 model_context->recognition_callback = NULL; 667 model_context->recognition_cookie = NULL; 668 model_context->next = NULL; 669 model_context->model_started = false; 670 ALOGI("Sound model loaded: Handle %d ", *handle); 671 672 pthread_mutex_unlock(&stdev->lock); 673 return status; 674} 675 676static void unload_all_sound_models(struct stub_sound_trigger_device *stdev) { 677 ALOGI("%s", __func__); 678 struct recognition_context *model_context = stdev->root_model_context; 679 stdev->root_model_context = NULL; 680 pthread_mutex_lock(&stdev->lock); 681 while (model_context) { 682 ALOGI("Deleting model with handle: %d", model_context->model_handle); 683 struct recognition_context *temp = model_context; 684 model_context = model_context->next; 685 free(temp->config); 686 free(temp); 687 } 688 pthread_mutex_unlock(&stdev->lock); 689} 690 691static int stdev_unload_sound_model(const struct sound_trigger_hw_device *dev, 692 sound_model_handle_t handle) { 693 // If recognizing, stop_recognition must be called for a sound model before unload_sound_model 694 ALOGI("%s", __func__); 695 struct stub_sound_trigger_device *stdev = (struct stub_sound_trigger_device *)dev; 696 int status = 0; 697 ALOGI("unload_sound_model:%d", handle); 698 pthread_mutex_lock(&stdev->lock); 699 700 struct recognition_context *model_context = NULL; 701 struct recognition_context *previous_model_context = NULL; 702 if (stdev->root_model_context) { 703 struct recognition_context *current_model_context = stdev->root_model_context; 704 while(current_model_context) { 705 if (current_model_context->model_handle == handle) { 706 model_context = current_model_context; 707 break; 708 } 709 previous_model_context = current_model_context; 710 current_model_context = current_model_context->next; 711 } 712 } 713 if (!model_context) { 714 ALOGW("Can't find sound model handle %d in registered list", handle); 715 pthread_mutex_unlock(&stdev->lock); 716 return -ENOSYS; 717 } 718 if (previous_model_context) { 719 previous_model_context->next = model_context->next; 720 } else { 721 stdev->root_model_context = model_context->next; 722 } 723 free(model_context->config); 724 free(model_context); 725 pthread_mutex_unlock(&stdev->lock); 726 return status; 727} 728 729static int stdev_start_recognition(const struct sound_trigger_hw_device *dev, 730 sound_model_handle_t handle, 731 const struct sound_trigger_recognition_config *config, 732 recognition_callback_t callback, 733 void *cookie) { 734 ALOGI("%s", __func__); 735 struct stub_sound_trigger_device *stdev = (struct stub_sound_trigger_device *)dev; 736 pthread_mutex_lock(&stdev->lock); 737 738 /* If other models running with callbacks, don't start trigger thread */ 739 bool other_callbacks_found = recognition_callback_exists(stdev); 740 741 struct recognition_context *model_context = get_model_context(stdev, handle); 742 if (!model_context) { 743 ALOGW("Can't find sound model handle %d in registered list", handle); 744 pthread_mutex_unlock(&stdev->lock); 745 return -ENOSYS; 746 } 747 748 free(model_context->config); 749 model_context->config = NULL; 750 if (config) { 751 model_context->config = malloc(sizeof(*config)); 752 if (!model_context->config) { 753 pthread_mutex_unlock(&stdev->lock); 754 return -ENOMEM; 755 } 756 memcpy(model_context->config, config, sizeof(*config)); 757 } 758 model_context->recognition_callback = callback; 759 model_context->recognition_cookie = cookie; 760 model_context->model_started = true; 761 762 pthread_mutex_unlock(&stdev->lock); 763 ALOGI("%s done for handle %d", __func__, handle); 764 return 0; 765} 766 767static int stdev_stop_recognition(const struct sound_trigger_hw_device *dev, 768 sound_model_handle_t handle) { 769 struct stub_sound_trigger_device *stdev = (struct stub_sound_trigger_device *)dev; 770 ALOGI("%s", __func__); 771 pthread_mutex_lock(&stdev->lock); 772 773 struct recognition_context *model_context = get_model_context(stdev, handle); 774 if (!model_context) { 775 ALOGW("Can't find sound model handle %d in registered list", handle); 776 pthread_mutex_unlock(&stdev->lock); 777 return -ENOSYS; 778 } 779 780 free(model_context->config); 781 model_context->config = NULL; 782 model_context->recognition_callback = NULL; 783 model_context->recognition_cookie = NULL; 784 model_context->model_started = false; 785 786 pthread_mutex_unlock(&stdev->lock); 787 ALOGI("%s done for handle %d", __func__, handle); 788 789 return 0; 790} 791 792static int stdev_stop_all_recognitions(const struct sound_trigger_hw_device *dev) { 793 struct stub_sound_trigger_device *stdev = (struct stub_sound_trigger_device *)dev; 794 ALOGI("%s", __func__); 795 pthread_mutex_lock(&stdev->lock); 796 797 struct recognition_context *model_context = stdev->root_model_context; 798 while (model_context) { 799 free(model_context->config); 800 model_context->config = NULL; 801 model_context->recognition_callback = NULL; 802 model_context->recognition_cookie = NULL; 803 model_context->model_started = false; 804 ALOGI("%s stopped handle %d", __func__, model_context->model_handle); 805 806 model_context = model_context->next; 807 } 808 809 pthread_mutex_unlock(&stdev->lock); 810 811 return 0; 812} 813 814__attribute__ ((visibility ("default"))) 815int sound_trigger_open_for_streaming() { 816 int ret = 0; 817 return ret; 818} 819 820__attribute__ ((visibility ("default"))) 821size_t sound_trigger_read_samples(int audio_handle, void *buffer, size_t buffer_len) { 822 size_t ret = 0; 823 return ret; 824} 825 826__attribute__ ((visibility ("default"))) 827int sound_trigger_close_for_streaming(int audio_handle __unused) { 828 return 0; 829} 830 831static int stdev_close(hw_device_t *device) { 832 // TODO: Implement the ability to stop the control thread. Since this is a 833 // test hal, we have skipped implementing this for now. A possible method 834 // would register a signal handler for the control thread so that any 835 // blocking socket calls can be interrupted. We would send that signal here 836 // to interrupt and quit the thread. 837 free(device); 838 return 0; 839} 840 841static int stdev_open(const hw_module_t* module, const char* name, 842 hw_device_t** device) { 843 struct stub_sound_trigger_device *stdev; 844 int ret; 845 846 if (strcmp(name, SOUND_TRIGGER_HARDWARE_INTERFACE) != 0) 847 return -EINVAL; 848 849 stdev = calloc(1, sizeof(struct stub_sound_trigger_device)); 850 if (!stdev) 851 return -ENOMEM; 852 853 stdev->next_sound_model_id = 1; 854 stdev->root_model_context = NULL; 855 856 stdev->device.common.tag = HARDWARE_DEVICE_TAG; 857 stdev->device.common.version = SOUND_TRIGGER_DEVICE_API_VERSION_1_1; 858 stdev->device.common.module = (struct hw_module_t *) module; 859 stdev->device.common.close = stdev_close; 860 stdev->device.get_properties = stdev_get_properties; 861 stdev->device.load_sound_model = stdev_load_sound_model; 862 stdev->device.unload_sound_model = stdev_unload_sound_model; 863 stdev->device.start_recognition = stdev_start_recognition; 864 stdev->device.stop_recognition = stdev_stop_recognition; 865 stdev->device.stop_all_recognitions = stdev_stop_all_recognitions; 866 867 pthread_mutex_init(&stdev->lock, (const pthread_mutexattr_t *) NULL); 868 869 *device = &stdev->device.common; 870 871 pthread_create(&stdev->control_thread, (const pthread_attr_t *) NULL, 872 control_thread_loop, stdev); 873 ALOGI("Starting control thread for the stub hal."); 874 875 return 0; 876} 877 878static struct hw_module_methods_t hal_module_methods = { 879 .open = stdev_open, 880}; 881 882struct sound_trigger_module HAL_MODULE_INFO_SYM = { 883 .common = { 884 .tag = HARDWARE_MODULE_TAG, 885 .module_api_version = SOUND_TRIGGER_MODULE_API_VERSION_1_0, 886 .hal_api_version = HARDWARE_HAL_API_VERSION, 887 .id = SOUND_TRIGGER_HARDWARE_MODULE_ID, 888 .name = "Default sound trigger HAL", 889 .author = "The Android Open Source Project", 890 .methods = &hal_module_methods, 891 }, 892}; 893 894