1 /* 2 ** 3 ** Copyright 2014, The Android Open Source Project 4 ** 5 ** Licensed under the Apache License, Version 2.0 (the "License"); 6 ** you may not use this file except in compliance with the License. 7 ** You may obtain a copy of the License at 8 ** 9 ** http://www.apache.org/licenses/LICENSE-2.0 10 ** 11 ** Unless required by applicable law or agreed to in writing, software 12 ** distributed under the License is distributed on an "AS IS" BASIS, 13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 ** See the License for the specific language governing permissions and 15 ** limitations under the License. 16 */ 17 18 //#define LOG_NDEBUG 0 19 #define LOG_TAG "SoundTrigger-JNI" 20 #include <utils/Log.h> 21 22 #include "jni.h" 23 #include "JNIHelp.h" 24 #include "android_runtime/AndroidRuntime.h" 25 #include <system/sound_trigger.h> 26 #include <soundtrigger/SoundTriggerCallback.h> 27 #include <soundtrigger/SoundTrigger.h> 28 #include <utils/RefBase.h> 29 #include <utils/Vector.h> 30 #include <binder/IMemory.h> 31 #include <binder/MemoryDealer.h> 32 #include "android_media_AudioFormat.h" 33 34 using namespace android; 35 36 static jclass gArrayListClass; 37 static struct { 38 jmethodID add; 39 } gArrayListMethods; 40 41 static jclass gUUIDClass; 42 static struct { 43 jmethodID toString; 44 } gUUIDMethods; 45 46 static const char* const kSoundTriggerClassPathName = "android/hardware/soundtrigger/SoundTrigger"; 47 static jclass gSoundTriggerClass; 48 49 static const char* const kModuleClassPathName = "android/hardware/soundtrigger/SoundTriggerModule"; 50 static jclass gModuleClass; 51 static struct { 52 jfieldID mNativeContext; 53 jfieldID mId; 54 } gModuleFields; 55 static jmethodID gPostEventFromNative; 56 57 static const char* const kModulePropertiesClassPathName = 58 "android/hardware/soundtrigger/SoundTrigger$ModuleProperties"; 59 static jclass gModulePropertiesClass; 60 static jmethodID gModulePropertiesCstor; 61 62 static const char* const kSoundModelClassPathName = 63 "android/hardware/soundtrigger/SoundTrigger$SoundModel"; 64 static jclass gSoundModelClass; 65 static struct { 66 jfieldID uuid; 67 jfieldID vendorUuid; 68 jfieldID data; 69 } gSoundModelFields; 70 71 static const char* const kKeyphraseClassPathName = 72 "android/hardware/soundtrigger/SoundTrigger$Keyphrase"; 73 static jclass gKeyphraseClass; 74 static struct { 75 jfieldID id; 76 jfieldID recognitionModes; 77 jfieldID locale; 78 jfieldID text; 79 jfieldID users; 80 } gKeyphraseFields; 81 82 static const char* const kKeyphraseSoundModelClassPathName = 83 "android/hardware/soundtrigger/SoundTrigger$KeyphraseSoundModel"; 84 static jclass gKeyphraseSoundModelClass; 85 static struct { 86 jfieldID keyphrases; 87 } gKeyphraseSoundModelFields; 88 89 static const char* const kRecognitionConfigClassPathName = 90 "android/hardware/soundtrigger/SoundTrigger$RecognitionConfig"; 91 static jclass gRecognitionConfigClass; 92 static struct { 93 jfieldID captureRequested; 94 jfieldID keyphrases; 95 jfieldID data; 96 } gRecognitionConfigFields; 97 98 static const char* const kRecognitionEventClassPathName = 99 "android/hardware/soundtrigger/SoundTrigger$RecognitionEvent"; 100 static jclass gRecognitionEventClass; 101 static jmethodID gRecognitionEventCstor; 102 103 static const char* const kKeyphraseRecognitionEventClassPathName = 104 "android/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionEvent"; 105 static jclass gKeyphraseRecognitionEventClass; 106 static jmethodID gKeyphraseRecognitionEventCstor; 107 108 static const char* const kKeyphraseRecognitionExtraClassPathName = 109 "android/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra"; 110 static jclass gKeyphraseRecognitionExtraClass; 111 static jmethodID gKeyphraseRecognitionExtraCstor; 112 static struct { 113 jfieldID id; 114 jfieldID recognitionModes; 115 jfieldID coarseConfidenceLevel; 116 jfieldID confidenceLevels; 117 } gKeyphraseRecognitionExtraFields; 118 119 static const char* const kConfidenceLevelClassPathName = 120 "android/hardware/soundtrigger/SoundTrigger$ConfidenceLevel"; 121 static jclass gConfidenceLevelClass; 122 static jmethodID gConfidenceLevelCstor; 123 static struct { 124 jfieldID userId; 125 jfieldID confidenceLevel; 126 } gConfidenceLevelFields; 127 128 static const char* const kAudioFormatClassPathName = 129 "android/media/AudioFormat"; 130 static jclass gAudioFormatClass; 131 static jmethodID gAudioFormatCstor; 132 133 static const char* const kSoundModelEventClassPathName = 134 "android/hardware/soundtrigger/SoundTrigger$SoundModelEvent"; 135 static jclass gSoundModelEventClass; 136 static jmethodID gSoundModelEventCstor; 137 138 static Mutex gLock; 139 140 enum { 141 SOUNDTRIGGER_STATUS_OK = 0, 142 SOUNDTRIGGER_STATUS_ERROR = INT_MIN, 143 SOUNDTRIGGER_PERMISSION_DENIED = -1, 144 SOUNDTRIGGER_STATUS_NO_INIT = -19, 145 SOUNDTRIGGER_STATUS_BAD_VALUE = -22, 146 SOUNDTRIGGER_STATUS_DEAD_OBJECT = -32, 147 SOUNDTRIGGER_INVALID_OPERATION = -38, 148 }; 149 150 enum { 151 SOUNDTRIGGER_EVENT_RECOGNITION = 1, 152 SOUNDTRIGGER_EVENT_SERVICE_DIED = 2, 153 SOUNDTRIGGER_EVENT_SOUNDMODEL = 3, 154 SOUNDTRIGGER_EVENT_SERVICE_STATE_CHANGE = 4, 155 }; 156 157 // ---------------------------------------------------------------------------- 158 // ref-counted object for callbacks 159 class JNISoundTriggerCallback: public SoundTriggerCallback 160 { 161 public: 162 JNISoundTriggerCallback(JNIEnv* env, jobject thiz, jobject weak_thiz); 163 ~JNISoundTriggerCallback(); 164 165 virtual void onRecognitionEvent(struct sound_trigger_recognition_event *event); 166 virtual void onSoundModelEvent(struct sound_trigger_model_event *event); 167 virtual void onServiceStateChange(sound_trigger_service_state_t state); 168 virtual void onServiceDied(); 169 170 private: 171 jclass mClass; // Reference to SoundTrigger class 172 jobject mObject; // Weak ref to SoundTrigger Java object to call on 173 }; 174 175 JNISoundTriggerCallback::JNISoundTriggerCallback(JNIEnv* env, jobject thiz, jobject weak_thiz) 176 { 177 178 // Hold onto the SoundTriggerModule class for use in calling the static method 179 // that posts events to the application thread. 180 jclass clazz = env->GetObjectClass(thiz); 181 if (clazz == NULL) { 182 ALOGE("Can't find class %s", kModuleClassPathName); 183 return; 184 } 185 mClass = (jclass)env->NewGlobalRef(clazz); 186 187 // We use a weak reference so the SoundTriggerModule object can be garbage collected. 188 // The reference is only used as a proxy for callbacks. 189 mObject = env->NewGlobalRef(weak_thiz); 190 } 191 192 JNISoundTriggerCallback::~JNISoundTriggerCallback() 193 { 194 // remove global references 195 JNIEnv *env = AndroidRuntime::getJNIEnv(); 196 env->DeleteGlobalRef(mObject); 197 env->DeleteGlobalRef(mClass); 198 } 199 200 void JNISoundTriggerCallback::onRecognitionEvent(struct sound_trigger_recognition_event *event) 201 { 202 JNIEnv *env = AndroidRuntime::getJNIEnv(); 203 jobject jEvent = NULL; 204 jbyteArray jData = NULL; 205 206 if (event->data_size) { 207 jData = env->NewByteArray(event->data_size); 208 jbyte *nData = env->GetByteArrayElements(jData, NULL); 209 memcpy(nData, (char *)event + event->data_offset, event->data_size); 210 env->ReleaseByteArrayElements(jData, nData, 0); 211 } 212 213 jobject jAudioFormat = NULL; 214 if (event->trigger_in_data || event->capture_available) { 215 jAudioFormat = env->NewObject(gAudioFormatClass, 216 gAudioFormatCstor, 217 audioFormatFromNative(event->audio_config.format), 218 event->audio_config.sample_rate, 219 inChannelMaskFromNative(event->audio_config.channel_mask)); 220 221 } 222 if (event->type == SOUND_MODEL_TYPE_KEYPHRASE) { 223 struct sound_trigger_phrase_recognition_event *phraseEvent = 224 (struct sound_trigger_phrase_recognition_event *)event; 225 226 jobjectArray jExtras = env->NewObjectArray(phraseEvent->num_phrases, 227 gKeyphraseRecognitionExtraClass, NULL); 228 if (jExtras == NULL) { 229 return; 230 } 231 232 for (size_t i = 0; i < phraseEvent->num_phrases; i++) { 233 jobjectArray jConfidenceLevels = env->NewObjectArray( 234 phraseEvent->phrase_extras[i].num_levels, 235 gConfidenceLevelClass, NULL); 236 237 if (jConfidenceLevels == NULL) { 238 return; 239 } 240 for (size_t j = 0; j < phraseEvent->phrase_extras[i].num_levels; j++) { 241 jobject jConfidenceLevel = env->NewObject(gConfidenceLevelClass, 242 gConfidenceLevelCstor, 243 phraseEvent->phrase_extras[i].levels[j].user_id, 244 phraseEvent->phrase_extras[i].levels[j].level); 245 env->SetObjectArrayElement(jConfidenceLevels, j, jConfidenceLevel); 246 env->DeleteLocalRef(jConfidenceLevel); 247 } 248 249 jobject jNewExtra = env->NewObject(gKeyphraseRecognitionExtraClass, 250 gKeyphraseRecognitionExtraCstor, 251 phraseEvent->phrase_extras[i].id, 252 phraseEvent->phrase_extras[i].recognition_modes, 253 phraseEvent->phrase_extras[i].confidence_level, 254 jConfidenceLevels); 255 256 if (jNewExtra == NULL) { 257 return; 258 } 259 env->SetObjectArrayElement(jExtras, i, jNewExtra); 260 env->DeleteLocalRef(jNewExtra); 261 env->DeleteLocalRef(jConfidenceLevels); 262 } 263 jEvent = env->NewObject(gKeyphraseRecognitionEventClass, gKeyphraseRecognitionEventCstor, 264 event->status, event->model, event->capture_available, 265 event->capture_session, event->capture_delay_ms, 266 event->capture_preamble_ms, event->trigger_in_data, 267 jAudioFormat, jData, jExtras); 268 env->DeleteLocalRef(jAudioFormat); 269 env->DeleteLocalRef(jData); 270 } else { 271 jEvent = env->NewObject(gRecognitionEventClass, gRecognitionEventCstor, 272 event->status, event->model, event->capture_available, 273 event->capture_session, event->capture_delay_ms, 274 event->capture_preamble_ms, event->trigger_in_data, 275 jAudioFormat, jData); 276 env->DeleteLocalRef(jAudioFormat); 277 env->DeleteLocalRef(jData); 278 } 279 280 281 env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject, 282 SOUNDTRIGGER_EVENT_RECOGNITION, 0, 0, jEvent); 283 284 env->DeleteLocalRef(jEvent); 285 if (env->ExceptionCheck()) { 286 ALOGW("An exception occurred while notifying an event."); 287 env->ExceptionClear(); 288 } 289 } 290 291 void JNISoundTriggerCallback::onSoundModelEvent(struct sound_trigger_model_event *event) 292 { 293 JNIEnv *env = AndroidRuntime::getJNIEnv(); 294 jobject jEvent = NULL; 295 jbyteArray jData = NULL; 296 297 if (event->data_size) { 298 jData = env->NewByteArray(event->data_size); 299 jbyte *nData = env->GetByteArrayElements(jData, NULL); 300 memcpy(nData, (char *)event + event->data_offset, event->data_size); 301 env->ReleaseByteArrayElements(jData, nData, 0); 302 } 303 304 jEvent = env->NewObject(gSoundModelEventClass, gSoundModelEventCstor, 305 event->status, event->model, jData); 306 307 env->DeleteLocalRef(jData); 308 env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject, 309 SOUNDTRIGGER_EVENT_SOUNDMODEL, 0, 0, jEvent); 310 env->DeleteLocalRef(jEvent); 311 if (env->ExceptionCheck()) { 312 ALOGW("An exception occurred while notifying an event."); 313 env->ExceptionClear(); 314 } 315 } 316 317 void JNISoundTriggerCallback::onServiceStateChange(sound_trigger_service_state_t state) 318 { 319 JNIEnv *env = AndroidRuntime::getJNIEnv(); 320 env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject, 321 SOUNDTRIGGER_EVENT_SERVICE_STATE_CHANGE, state, 0, NULL); 322 if (env->ExceptionCheck()) { 323 ALOGW("An exception occurred while notifying an event."); 324 env->ExceptionClear(); 325 } 326 } 327 328 void JNISoundTriggerCallback::onServiceDied() 329 { 330 JNIEnv *env = AndroidRuntime::getJNIEnv(); 331 332 env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject, 333 SOUNDTRIGGER_EVENT_SERVICE_DIED, 0, 0, NULL); 334 if (env->ExceptionCheck()) { 335 ALOGW("An exception occurred while notifying an event."); 336 env->ExceptionClear(); 337 } 338 } 339 340 // ---------------------------------------------------------------------------- 341 342 static sp<SoundTrigger> getSoundTrigger(JNIEnv* env, jobject thiz) 343 { 344 Mutex::Autolock l(gLock); 345 SoundTrigger* const st = (SoundTrigger*)env->GetLongField(thiz, 346 gModuleFields.mNativeContext); 347 return sp<SoundTrigger>(st); 348 } 349 350 static sp<SoundTrigger> setSoundTrigger(JNIEnv* env, jobject thiz, const sp<SoundTrigger>& module) 351 { 352 Mutex::Autolock l(gLock); 353 sp<SoundTrigger> old = (SoundTrigger*)env->GetLongField(thiz, 354 gModuleFields.mNativeContext); 355 if (module.get()) { 356 module->incStrong((void*)setSoundTrigger); 357 } 358 if (old != 0) { 359 old->decStrong((void*)setSoundTrigger); 360 } 361 env->SetLongField(thiz, gModuleFields.mNativeContext, (jlong)module.get()); 362 return old; 363 } 364 365 366 static jint 367 android_hardware_SoundTrigger_listModules(JNIEnv *env, jobject clazz, 368 jobject jModules) 369 { 370 ALOGV("listModules"); 371 372 if (jModules == NULL) { 373 ALOGE("listModules NULL AudioPatch ArrayList"); 374 return SOUNDTRIGGER_STATUS_BAD_VALUE; 375 } 376 if (!env->IsInstanceOf(jModules, gArrayListClass)) { 377 ALOGE("listModules not an arraylist"); 378 return SOUNDTRIGGER_STATUS_BAD_VALUE; 379 } 380 381 unsigned int numModules = 0; 382 struct sound_trigger_module_descriptor *nModules = NULL; 383 384 status_t status = SoundTrigger::listModules(nModules, &numModules); 385 if (status != NO_ERROR || numModules == 0) { 386 return (jint)status; 387 } 388 389 nModules = (struct sound_trigger_module_descriptor *) 390 calloc(numModules, sizeof(struct sound_trigger_module_descriptor)); 391 392 status = SoundTrigger::listModules(nModules, &numModules); 393 ALOGV("listModules SoundTrigger::listModules status %d numModules %d", status, numModules); 394 395 if (status != NO_ERROR) { 396 numModules = 0; 397 } 398 399 for (size_t i = 0; i < numModules; i++) { 400 char str[SOUND_TRIGGER_MAX_STRING_LEN]; 401 402 jstring implementor = env->NewStringUTF(nModules[i].properties.implementor); 403 jstring description = env->NewStringUTF(nModules[i].properties.description); 404 SoundTrigger::guidToString(&nModules[i].properties.uuid, 405 str, 406 SOUND_TRIGGER_MAX_STRING_LEN); 407 jstring uuid = env->NewStringUTF(str); 408 409 ALOGV("listModules module %zu id %d description %s maxSoundModels %d", 410 i, nModules[i].handle, nModules[i].properties.description, 411 nModules[i].properties.max_sound_models); 412 413 jobject newModuleDesc = env->NewObject(gModulePropertiesClass, gModulePropertiesCstor, 414 nModules[i].handle, 415 implementor, description, uuid, 416 nModules[i].properties.version, 417 nModules[i].properties.max_sound_models, 418 nModules[i].properties.max_key_phrases, 419 nModules[i].properties.max_users, 420 nModules[i].properties.recognition_modes, 421 nModules[i].properties.capture_transition, 422 nModules[i].properties.max_buffer_ms, 423 nModules[i].properties.concurrent_capture, 424 nModules[i].properties.power_consumption_mw, 425 nModules[i].properties.trigger_in_event); 426 427 env->DeleteLocalRef(implementor); 428 env->DeleteLocalRef(description); 429 env->DeleteLocalRef(uuid); 430 if (newModuleDesc == NULL) { 431 status = SOUNDTRIGGER_STATUS_ERROR; 432 goto exit; 433 } 434 env->CallBooleanMethod(jModules, gArrayListMethods.add, newModuleDesc); 435 } 436 437 exit: 438 free(nModules); 439 return (jint) status; 440 } 441 442 static void 443 android_hardware_SoundTrigger_setup(JNIEnv *env, jobject thiz, jobject weak_this) 444 { 445 ALOGV("setup"); 446 447 sp<JNISoundTriggerCallback> callback = new JNISoundTriggerCallback(env, thiz, weak_this); 448 449 sound_trigger_module_handle_t handle = 450 (sound_trigger_module_handle_t)env->GetIntField(thiz, gModuleFields.mId); 451 452 sp<SoundTrigger> module = SoundTrigger::attach(handle, callback); 453 if (module == 0) { 454 return; 455 } 456 457 setSoundTrigger(env, thiz, module); 458 } 459 460 static void 461 android_hardware_SoundTrigger_detach(JNIEnv *env, jobject thiz) 462 { 463 ALOGV("detach"); 464 sp<SoundTrigger> module = setSoundTrigger(env, thiz, 0); 465 ALOGV("detach module %p", module.get()); 466 if (module != 0) { 467 ALOGV("detach module->detach()"); 468 module->detach(); 469 } 470 } 471 472 static void 473 android_hardware_SoundTrigger_finalize(JNIEnv *env, jobject thiz) 474 { 475 ALOGV("finalize"); 476 sp<SoundTrigger> module = getSoundTrigger(env, thiz); 477 if (module != 0) { 478 ALOGW("SoundTrigger finalized without being detached"); 479 } 480 android_hardware_SoundTrigger_detach(env, thiz); 481 } 482 483 static jint 484 android_hardware_SoundTrigger_loadSoundModel(JNIEnv *env, jobject thiz, 485 jobject jSoundModel, jintArray jHandle) 486 { 487 jint status = SOUNDTRIGGER_STATUS_OK; 488 jbyte *nData = NULL; 489 struct sound_trigger_sound_model *nSoundModel; 490 jbyteArray jData; 491 sp<MemoryDealer> memoryDealer; 492 sp<IMemory> memory; 493 size_t size; 494 sound_model_handle_t handle; 495 jobject jUuid; 496 jstring jUuidString; 497 const char *nUuidString; 498 499 ALOGV("loadSoundModel"); 500 sp<SoundTrigger> module = getSoundTrigger(env, thiz); 501 if (module == NULL) { 502 return SOUNDTRIGGER_STATUS_ERROR; 503 } 504 if (jHandle == NULL) { 505 return SOUNDTRIGGER_STATUS_BAD_VALUE; 506 } 507 jsize jHandleLen = env->GetArrayLength(jHandle); 508 if (jHandleLen == 0) { 509 return SOUNDTRIGGER_STATUS_BAD_VALUE; 510 } 511 jint *nHandle = env->GetIntArrayElements(jHandle, NULL); 512 if (nHandle == NULL) { 513 return SOUNDTRIGGER_STATUS_ERROR; 514 } 515 if (!env->IsInstanceOf(jSoundModel, gSoundModelClass)) { 516 status = SOUNDTRIGGER_STATUS_BAD_VALUE; 517 goto exit; 518 } 519 size_t offset; 520 sound_trigger_sound_model_type_t type; 521 if (env->IsInstanceOf(jSoundModel, gKeyphraseSoundModelClass)) { 522 offset = sizeof(struct sound_trigger_phrase_sound_model); 523 type = SOUND_MODEL_TYPE_KEYPHRASE; 524 } else { 525 offset = sizeof(struct sound_trigger_sound_model); 526 type = SOUND_MODEL_TYPE_UNKNOWN; 527 } 528 529 jUuid = env->GetObjectField(jSoundModel, gSoundModelFields.uuid); 530 jUuidString = (jstring)env->CallObjectMethod(jUuid, gUUIDMethods.toString); 531 nUuidString = env->GetStringUTFChars(jUuidString, NULL); 532 sound_trigger_uuid_t nUuid; 533 SoundTrigger::stringToGuid(nUuidString, &nUuid); 534 env->ReleaseStringUTFChars(jUuidString, nUuidString); 535 env->DeleteLocalRef(jUuidString); 536 537 sound_trigger_uuid_t nVendorUuid; 538 jUuid = env->GetObjectField(jSoundModel, gSoundModelFields.vendorUuid); 539 if (jUuid != NULL) { 540 jUuidString = (jstring)env->CallObjectMethod(jUuid, gUUIDMethods.toString); 541 nUuidString = env->GetStringUTFChars(jUuidString, NULL); 542 SoundTrigger::stringToGuid(nUuidString, &nVendorUuid); 543 env->ReleaseStringUTFChars(jUuidString, nUuidString); 544 env->DeleteLocalRef(jUuidString); 545 } else { 546 SoundTrigger::stringToGuid("00000000-0000-0000-0000-000000000000", &nVendorUuid); 547 } 548 549 jData = (jbyteArray)env->GetObjectField(jSoundModel, gSoundModelFields.data); 550 if (jData == NULL) { 551 status = SOUNDTRIGGER_STATUS_BAD_VALUE; 552 goto exit; 553 } 554 size = env->GetArrayLength(jData); 555 556 nData = env->GetByteArrayElements(jData, NULL); 557 if (jData == NULL) { 558 status = SOUNDTRIGGER_STATUS_ERROR; 559 goto exit; 560 } 561 562 memoryDealer = new MemoryDealer(offset + size, "SoundTrigge-JNI::LoadModel"); 563 if (memoryDealer == 0) { 564 status = SOUNDTRIGGER_STATUS_ERROR; 565 goto exit; 566 } 567 memory = memoryDealer->allocate(offset + size); 568 if (memory == 0 || memory->pointer() == NULL) { 569 status = SOUNDTRIGGER_STATUS_ERROR; 570 goto exit; 571 } 572 573 nSoundModel = (struct sound_trigger_sound_model *)memory->pointer(); 574 575 nSoundModel->type = type; 576 nSoundModel->uuid = nUuid; 577 nSoundModel->vendor_uuid = nVendorUuid; 578 nSoundModel->data_size = size; 579 nSoundModel->data_offset = offset; 580 memcpy((char *)nSoundModel + offset, nData, size); 581 if (type == SOUND_MODEL_TYPE_KEYPHRASE) { 582 struct sound_trigger_phrase_sound_model *phraseModel = 583 (struct sound_trigger_phrase_sound_model *)nSoundModel; 584 585 jobjectArray jPhrases = 586 (jobjectArray)env->GetObjectField(jSoundModel, gKeyphraseSoundModelFields.keyphrases); 587 if (jPhrases == NULL) { 588 status = SOUNDTRIGGER_STATUS_BAD_VALUE; 589 goto exit; 590 } 591 592 size_t numPhrases = env->GetArrayLength(jPhrases); 593 phraseModel->num_phrases = numPhrases; 594 ALOGV("loadSoundModel numPhrases %zu", numPhrases); 595 for (size_t i = 0; i < numPhrases; i++) { 596 jobject jPhrase = env->GetObjectArrayElement(jPhrases, i); 597 phraseModel->phrases[i].id = 598 env->GetIntField(jPhrase,gKeyphraseFields.id); 599 phraseModel->phrases[i].recognition_mode = 600 env->GetIntField(jPhrase,gKeyphraseFields.recognitionModes); 601 602 jintArray jUsers = (jintArray)env->GetObjectField(jPhrase, gKeyphraseFields.users); 603 phraseModel->phrases[i].num_users = env->GetArrayLength(jUsers); 604 jint *nUsers = env->GetIntArrayElements(jUsers, NULL); 605 memcpy(phraseModel->phrases[i].users, 606 nUsers, 607 phraseModel->phrases[i].num_users * sizeof(int)); 608 env->ReleaseIntArrayElements(jUsers, nUsers, 0); 609 env->DeleteLocalRef(jUsers); 610 611 jstring jLocale = (jstring)env->GetObjectField(jPhrase, gKeyphraseFields.locale); 612 const char *nLocale = env->GetStringUTFChars(jLocale, NULL); 613 strncpy(phraseModel->phrases[i].locale, 614 nLocale, 615 SOUND_TRIGGER_MAX_LOCALE_LEN); 616 jstring jText = (jstring)env->GetObjectField(jPhrase, gKeyphraseFields.text); 617 const char *nText = env->GetStringUTFChars(jText, NULL); 618 strncpy(phraseModel->phrases[i].text, 619 nText, 620 SOUND_TRIGGER_MAX_STRING_LEN); 621 622 env->ReleaseStringUTFChars(jLocale, nLocale); 623 env->DeleteLocalRef(jLocale); 624 env->ReleaseStringUTFChars(jText, nText); 625 env->DeleteLocalRef(jText); 626 ALOGV("loadSoundModel phrases %zu text %s locale %s", 627 i, phraseModel->phrases[i].text, phraseModel->phrases[i].locale); 628 env->DeleteLocalRef(jPhrase); 629 } 630 env->DeleteLocalRef(jPhrases); 631 } 632 status = module->loadSoundModel(memory, &handle); 633 ALOGV("loadSoundModel status %d handle %d", status, handle); 634 635 exit: 636 if (nHandle != NULL) { 637 nHandle[0] = (jint)handle; 638 env->ReleaseIntArrayElements(jHandle, nHandle, NULL); 639 } 640 if (nData != NULL) { 641 env->ReleaseByteArrayElements(jData, nData, NULL); 642 } 643 return status; 644 } 645 646 static jint 647 android_hardware_SoundTrigger_unloadSoundModel(JNIEnv *env, jobject thiz, 648 jint jHandle) 649 { 650 jint status = SOUNDTRIGGER_STATUS_OK; 651 ALOGV("unloadSoundModel"); 652 sp<SoundTrigger> module = getSoundTrigger(env, thiz); 653 if (module == NULL) { 654 return SOUNDTRIGGER_STATUS_ERROR; 655 } 656 status = module->unloadSoundModel((sound_model_handle_t)jHandle); 657 658 return status; 659 } 660 661 static jint 662 android_hardware_SoundTrigger_startRecognition(JNIEnv *env, jobject thiz, 663 jint jHandle, jobject jConfig) 664 { 665 jint status = SOUNDTRIGGER_STATUS_OK; 666 ALOGV("startRecognition"); 667 sp<SoundTrigger> module = getSoundTrigger(env, thiz); 668 if (module == NULL) { 669 return SOUNDTRIGGER_STATUS_ERROR; 670 } 671 672 if (!env->IsInstanceOf(jConfig, gRecognitionConfigClass)) { 673 return SOUNDTRIGGER_STATUS_BAD_VALUE; 674 } 675 676 jbyteArray jData = (jbyteArray)env->GetObjectField(jConfig, gRecognitionConfigFields.data); 677 jsize dataSize = 0; 678 jbyte *nData = NULL; 679 if (jData != NULL) { 680 dataSize = env->GetArrayLength(jData); 681 if (dataSize == 0) { 682 return SOUNDTRIGGER_STATUS_BAD_VALUE; 683 } 684 nData = env->GetByteArrayElements(jData, NULL); 685 if (nData == NULL) { 686 return SOUNDTRIGGER_STATUS_ERROR; 687 } 688 } 689 690 size_t totalSize = sizeof(struct sound_trigger_recognition_config) + dataSize; 691 sp<MemoryDealer> memoryDealer = 692 new MemoryDealer(totalSize, "SoundTrigge-JNI::StartRecognition"); 693 if (memoryDealer == 0) { 694 return SOUNDTRIGGER_STATUS_ERROR; 695 } 696 sp<IMemory> memory = memoryDealer->allocate(totalSize); 697 if (memory == 0 || memory->pointer() == NULL) { 698 return SOUNDTRIGGER_STATUS_ERROR; 699 } 700 if (dataSize != 0) { 701 memcpy((char *)memory->pointer() + sizeof(struct sound_trigger_recognition_config), 702 nData, 703 dataSize); 704 env->ReleaseByteArrayElements(jData, nData, 0); 705 } 706 env->DeleteLocalRef(jData); 707 struct sound_trigger_recognition_config *config = 708 (struct sound_trigger_recognition_config *)memory->pointer(); 709 config->data_size = dataSize; 710 config->data_offset = sizeof(struct sound_trigger_recognition_config); 711 config->capture_requested = env->GetIntField(jConfig, 712 gRecognitionConfigFields.captureRequested); 713 714 config->num_phrases = 0; 715 jobjectArray jPhrases = 716 (jobjectArray)env->GetObjectField(jConfig, gRecognitionConfigFields.keyphrases); 717 if (jPhrases != NULL) { 718 config->num_phrases = env->GetArrayLength(jPhrases); 719 } 720 ALOGV("startRecognition num phrases %d", config->num_phrases); 721 for (size_t i = 0; i < config->num_phrases; i++) { 722 jobject jPhrase = env->GetObjectArrayElement(jPhrases, i); 723 config->phrases[i].id = env->GetIntField(jPhrase, 724 gKeyphraseRecognitionExtraFields.id); 725 config->phrases[i].recognition_modes = env->GetIntField(jPhrase, 726 gKeyphraseRecognitionExtraFields.recognitionModes); 727 config->phrases[i].confidence_level = env->GetIntField(jPhrase, 728 gKeyphraseRecognitionExtraFields.coarseConfidenceLevel); 729 config->phrases[i].num_levels = 0; 730 jobjectArray jConfidenceLevels = (jobjectArray)env->GetObjectField(jPhrase, 731 gKeyphraseRecognitionExtraFields.confidenceLevels); 732 if (jConfidenceLevels != NULL) { 733 config->phrases[i].num_levels = env->GetArrayLength(jConfidenceLevels); 734 } 735 ALOGV("startRecognition phrase %zu num_levels %d", i, config->phrases[i].num_levels); 736 for (size_t j = 0; j < config->phrases[i].num_levels; j++) { 737 jobject jConfidenceLevel = env->GetObjectArrayElement(jConfidenceLevels, j); 738 config->phrases[i].levels[j].user_id = env->GetIntField(jConfidenceLevel, 739 gConfidenceLevelFields.userId); 740 config->phrases[i].levels[j].level = env->GetIntField(jConfidenceLevel, 741 gConfidenceLevelFields.confidenceLevel); 742 env->DeleteLocalRef(jConfidenceLevel); 743 } 744 ALOGV("startRecognition phrases %zu", i); 745 env->DeleteLocalRef(jConfidenceLevels); 746 env->DeleteLocalRef(jPhrase); 747 } 748 env->DeleteLocalRef(jPhrases); 749 750 status = module->startRecognition(jHandle, memory); 751 return status; 752 } 753 754 static jint 755 android_hardware_SoundTrigger_stopRecognition(JNIEnv *env, jobject thiz, 756 jint jHandle) 757 { 758 jint status = SOUNDTRIGGER_STATUS_OK; 759 ALOGV("stopRecognition"); 760 sp<SoundTrigger> module = getSoundTrigger(env, thiz); 761 if (module == NULL) { 762 return SOUNDTRIGGER_STATUS_ERROR; 763 } 764 status = module->stopRecognition(jHandle); 765 return status; 766 } 767 768 static JNINativeMethod gMethods[] = { 769 {"listModules", 770 "(Ljava/util/ArrayList;)I", 771 (void *)android_hardware_SoundTrigger_listModules}, 772 }; 773 774 775 static JNINativeMethod gModuleMethods[] = { 776 {"native_setup", 777 "(Ljava/lang/Object;)V", 778 (void *)android_hardware_SoundTrigger_setup}, 779 {"native_finalize", 780 "()V", 781 (void *)android_hardware_SoundTrigger_finalize}, 782 {"detach", 783 "()V", 784 (void *)android_hardware_SoundTrigger_detach}, 785 {"loadSoundModel", 786 "(Landroid/hardware/soundtrigger/SoundTrigger$SoundModel;[I)I", 787 (void *)android_hardware_SoundTrigger_loadSoundModel}, 788 {"unloadSoundModel", 789 "(I)I", 790 (void *)android_hardware_SoundTrigger_unloadSoundModel}, 791 {"startRecognition", 792 "(ILandroid/hardware/soundtrigger/SoundTrigger$RecognitionConfig;)I", 793 (void *)android_hardware_SoundTrigger_startRecognition}, 794 {"stopRecognition", 795 "(I)I", 796 (void *)android_hardware_SoundTrigger_stopRecognition}, 797 }; 798 799 int register_android_hardware_SoundTrigger(JNIEnv *env) 800 { 801 jclass arrayListClass = env->FindClass("java/util/ArrayList"); 802 gArrayListClass = (jclass) env->NewGlobalRef(arrayListClass); 803 gArrayListMethods.add = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z"); 804 805 jclass uuidClass = env->FindClass("java/util/UUID"); 806 gUUIDClass = (jclass) env->NewGlobalRef(uuidClass); 807 gUUIDMethods.toString = env->GetMethodID(uuidClass, "toString", "()Ljava/lang/String;"); 808 809 jclass lClass = env->FindClass(kSoundTriggerClassPathName); 810 gSoundTriggerClass = (jclass) env->NewGlobalRef(lClass); 811 812 jclass moduleClass = env->FindClass(kModuleClassPathName); 813 gModuleClass = (jclass) env->NewGlobalRef(moduleClass); 814 gPostEventFromNative = env->GetStaticMethodID(moduleClass, "postEventFromNative", 815 "(Ljava/lang/Object;IIILjava/lang/Object;)V"); 816 gModuleFields.mNativeContext = env->GetFieldID(moduleClass, "mNativeContext", "J"); 817 gModuleFields.mId = env->GetFieldID(moduleClass, "mId", "I"); 818 819 820 jclass modulePropertiesClass = env->FindClass(kModulePropertiesClassPathName); 821 gModulePropertiesClass = (jclass) env->NewGlobalRef(modulePropertiesClass); 822 gModulePropertiesCstor = env->GetMethodID(modulePropertiesClass, "<init>", 823 "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;IIIIIZIZIZ)V"); 824 825 jclass soundModelClass = env->FindClass(kSoundModelClassPathName); 826 gSoundModelClass = (jclass) env->NewGlobalRef(soundModelClass); 827 gSoundModelFields.uuid = env->GetFieldID(soundModelClass, "uuid", "Ljava/util/UUID;"); 828 gSoundModelFields.vendorUuid = env->GetFieldID(soundModelClass, "vendorUuid", "Ljava/util/UUID;"); 829 gSoundModelFields.data = env->GetFieldID(soundModelClass, "data", "[B"); 830 831 jclass keyphraseClass = env->FindClass(kKeyphraseClassPathName); 832 gKeyphraseClass = (jclass) env->NewGlobalRef(keyphraseClass); 833 gKeyphraseFields.id = env->GetFieldID(keyphraseClass, "id", "I"); 834 gKeyphraseFields.recognitionModes = env->GetFieldID(keyphraseClass, "recognitionModes", "I"); 835 gKeyphraseFields.locale = env->GetFieldID(keyphraseClass, "locale", "Ljava/lang/String;"); 836 gKeyphraseFields.text = env->GetFieldID(keyphraseClass, "text", "Ljava/lang/String;"); 837 gKeyphraseFields.users = env->GetFieldID(keyphraseClass, "users", "[I"); 838 839 jclass keyphraseSoundModelClass = env->FindClass(kKeyphraseSoundModelClassPathName); 840 gKeyphraseSoundModelClass = (jclass) env->NewGlobalRef(keyphraseSoundModelClass); 841 gKeyphraseSoundModelFields.keyphrases = env->GetFieldID(keyphraseSoundModelClass, 842 "keyphrases", 843 "[Landroid/hardware/soundtrigger/SoundTrigger$Keyphrase;"); 844 845 846 jclass recognitionEventClass = env->FindClass(kRecognitionEventClassPathName); 847 gRecognitionEventClass = (jclass) env->NewGlobalRef(recognitionEventClass); 848 gRecognitionEventCstor = env->GetMethodID(recognitionEventClass, "<init>", 849 "(IIZIIIZLandroid/media/AudioFormat;[B)V"); 850 851 jclass keyphraseRecognitionEventClass = env->FindClass(kKeyphraseRecognitionEventClassPathName); 852 gKeyphraseRecognitionEventClass = (jclass) env->NewGlobalRef(keyphraseRecognitionEventClass); 853 gKeyphraseRecognitionEventCstor = env->GetMethodID(keyphraseRecognitionEventClass, "<init>", 854 "(IIZIIIZLandroid/media/AudioFormat;[B[Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;)V"); 855 856 857 jclass keyRecognitionConfigClass = env->FindClass(kRecognitionConfigClassPathName); 858 gRecognitionConfigClass = (jclass) env->NewGlobalRef(keyRecognitionConfigClass); 859 gRecognitionConfigFields.captureRequested = env->GetFieldID(keyRecognitionConfigClass, 860 "captureRequested", 861 "Z"); 862 gRecognitionConfigFields.keyphrases = env->GetFieldID(keyRecognitionConfigClass, 863 "keyphrases", 864 "[Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;"); 865 gRecognitionConfigFields.data = env->GetFieldID(keyRecognitionConfigClass, 866 "data", 867 "[B"); 868 869 jclass keyphraseRecognitionExtraClass = env->FindClass(kKeyphraseRecognitionExtraClassPathName); 870 gKeyphraseRecognitionExtraClass = (jclass) env->NewGlobalRef(keyphraseRecognitionExtraClass); 871 gKeyphraseRecognitionExtraCstor = env->GetMethodID(keyphraseRecognitionExtraClass, "<init>", 872 "(III[Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;)V"); 873 gKeyphraseRecognitionExtraFields.id = env->GetFieldID(gKeyphraseRecognitionExtraClass, "id", "I"); 874 gKeyphraseRecognitionExtraFields.recognitionModes = env->GetFieldID(gKeyphraseRecognitionExtraClass, 875 "recognitionModes", "I"); 876 gKeyphraseRecognitionExtraFields.coarseConfidenceLevel = env->GetFieldID(gKeyphraseRecognitionExtraClass, 877 "coarseConfidenceLevel", "I"); 878 gKeyphraseRecognitionExtraFields.confidenceLevels = env->GetFieldID(gKeyphraseRecognitionExtraClass, 879 "confidenceLevels", 880 "[Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;"); 881 882 jclass confidenceLevelClass = env->FindClass(kConfidenceLevelClassPathName); 883 gConfidenceLevelClass = (jclass) env->NewGlobalRef(confidenceLevelClass); 884 gConfidenceLevelCstor = env->GetMethodID(confidenceLevelClass, "<init>", "(II)V"); 885 gConfidenceLevelFields.userId = env->GetFieldID(confidenceLevelClass, "userId", "I"); 886 gConfidenceLevelFields.confidenceLevel = env->GetFieldID(confidenceLevelClass, 887 "confidenceLevel", "I"); 888 889 jclass audioFormatClass = env->FindClass(kAudioFormatClassPathName); 890 gAudioFormatClass = (jclass) env->NewGlobalRef(audioFormatClass); 891 gAudioFormatCstor = env->GetMethodID(audioFormatClass, "<init>", "(III)V"); 892 893 jclass soundModelEventClass = env->FindClass(kSoundModelEventClassPathName); 894 gSoundModelEventClass = (jclass) env->NewGlobalRef(soundModelEventClass); 895 gSoundModelEventCstor = env->GetMethodID(soundModelEventClass, "<init>", 896 "(II[B)V"); 897 898 899 int status = AndroidRuntime::registerNativeMethods(env, 900 kSoundTriggerClassPathName, gMethods, NELEM(gMethods)); 901 902 if (status == 0) { 903 status = AndroidRuntime::registerNativeMethods(env, 904 kModuleClassPathName, gModuleMethods, NELEM(gModuleMethods)); 905 } 906 907 908 return status; 909 } 910