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(jExtras); 269 } else { 270 jEvent = env->NewObject(gRecognitionEventClass, gRecognitionEventCstor, 271 event->status, event->model, event->capture_available, 272 event->capture_session, event->capture_delay_ms, 273 event->capture_preamble_ms, event->trigger_in_data, 274 jAudioFormat, jData); 275 } 276 277 if (jAudioFormat != NULL) { 278 env->DeleteLocalRef(jAudioFormat); 279 } 280 if (jData != NULL) { 281 env->DeleteLocalRef(jData); 282 } 283 284 env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject, 285 SOUNDTRIGGER_EVENT_RECOGNITION, 0, 0, jEvent); 286 287 env->DeleteLocalRef(jEvent); 288 if (env->ExceptionCheck()) { 289 ALOGW("An exception occurred while notifying an event."); 290 env->ExceptionClear(); 291 } 292 } 293 294 void JNISoundTriggerCallback::onSoundModelEvent(struct sound_trigger_model_event *event) 295 { 296 JNIEnv *env = AndroidRuntime::getJNIEnv(); 297 jobject jEvent = NULL; 298 jbyteArray jData = NULL; 299 300 if (event->data_size) { 301 jData = env->NewByteArray(event->data_size); 302 jbyte *nData = env->GetByteArrayElements(jData, NULL); 303 memcpy(nData, (char *)event + event->data_offset, event->data_size); 304 env->ReleaseByteArrayElements(jData, nData, 0); 305 } 306 307 jEvent = env->NewObject(gSoundModelEventClass, gSoundModelEventCstor, 308 event->status, event->model, jData); 309 310 env->DeleteLocalRef(jData); 311 env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject, 312 SOUNDTRIGGER_EVENT_SOUNDMODEL, 0, 0, jEvent); 313 env->DeleteLocalRef(jEvent); 314 if (env->ExceptionCheck()) { 315 ALOGW("An exception occurred while notifying an event."); 316 env->ExceptionClear(); 317 } 318 } 319 320 void JNISoundTriggerCallback::onServiceStateChange(sound_trigger_service_state_t state) 321 { 322 JNIEnv *env = AndroidRuntime::getJNIEnv(); 323 env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject, 324 SOUNDTRIGGER_EVENT_SERVICE_STATE_CHANGE, state, 0, NULL); 325 if (env->ExceptionCheck()) { 326 ALOGW("An exception occurred while notifying an event."); 327 env->ExceptionClear(); 328 } 329 } 330 331 void JNISoundTriggerCallback::onServiceDied() 332 { 333 JNIEnv *env = AndroidRuntime::getJNIEnv(); 334 335 env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject, 336 SOUNDTRIGGER_EVENT_SERVICE_DIED, 0, 0, NULL); 337 if (env->ExceptionCheck()) { 338 ALOGW("An exception occurred while notifying an event."); 339 env->ExceptionClear(); 340 } 341 } 342 343 // ---------------------------------------------------------------------------- 344 345 static sp<SoundTrigger> getSoundTrigger(JNIEnv* env, jobject thiz) 346 { 347 Mutex::Autolock l(gLock); 348 SoundTrigger* const st = (SoundTrigger*)env->GetLongField(thiz, 349 gModuleFields.mNativeContext); 350 return sp<SoundTrigger>(st); 351 } 352 353 static sp<SoundTrigger> setSoundTrigger(JNIEnv* env, jobject thiz, const sp<SoundTrigger>& module) 354 { 355 Mutex::Autolock l(gLock); 356 sp<SoundTrigger> old = (SoundTrigger*)env->GetLongField(thiz, 357 gModuleFields.mNativeContext); 358 if (module.get()) { 359 module->incStrong((void*)setSoundTrigger); 360 } 361 if (old != 0) { 362 old->decStrong((void*)setSoundTrigger); 363 } 364 env->SetLongField(thiz, gModuleFields.mNativeContext, (jlong)module.get()); 365 return old; 366 } 367 368 369 static jint 370 android_hardware_SoundTrigger_listModules(JNIEnv *env, jobject clazz, 371 jobject jModules) 372 { 373 ALOGV("listModules"); 374 375 if (jModules == NULL) { 376 ALOGE("listModules NULL AudioPatch ArrayList"); 377 return SOUNDTRIGGER_STATUS_BAD_VALUE; 378 } 379 if (!env->IsInstanceOf(jModules, gArrayListClass)) { 380 ALOGE("listModules not an arraylist"); 381 return SOUNDTRIGGER_STATUS_BAD_VALUE; 382 } 383 384 unsigned int numModules = 0; 385 struct sound_trigger_module_descriptor *nModules = NULL; 386 387 status_t status = SoundTrigger::listModules(nModules, &numModules); 388 if (status != NO_ERROR || numModules == 0) { 389 return (jint)status; 390 } 391 392 nModules = (struct sound_trigger_module_descriptor *) 393 calloc(numModules, sizeof(struct sound_trigger_module_descriptor)); 394 395 status = SoundTrigger::listModules(nModules, &numModules); 396 ALOGV("listModules SoundTrigger::listModules status %d numModules %d", status, numModules); 397 398 if (status != NO_ERROR) { 399 numModules = 0; 400 } 401 402 for (size_t i = 0; i < numModules; i++) { 403 char str[SOUND_TRIGGER_MAX_STRING_LEN]; 404 405 jstring implementor = env->NewStringUTF(nModules[i].properties.implementor); 406 jstring description = env->NewStringUTF(nModules[i].properties.description); 407 SoundTrigger::guidToString(&nModules[i].properties.uuid, 408 str, 409 SOUND_TRIGGER_MAX_STRING_LEN); 410 jstring uuid = env->NewStringUTF(str); 411 412 ALOGV("listModules module %zu id %d description %s maxSoundModels %d", 413 i, nModules[i].handle, nModules[i].properties.description, 414 nModules[i].properties.max_sound_models); 415 416 jobject newModuleDesc = env->NewObject(gModulePropertiesClass, gModulePropertiesCstor, 417 nModules[i].handle, 418 implementor, description, uuid, 419 nModules[i].properties.version, 420 nModules[i].properties.max_sound_models, 421 nModules[i].properties.max_key_phrases, 422 nModules[i].properties.max_users, 423 nModules[i].properties.recognition_modes, 424 nModules[i].properties.capture_transition, 425 nModules[i].properties.max_buffer_ms, 426 nModules[i].properties.concurrent_capture, 427 nModules[i].properties.power_consumption_mw, 428 nModules[i].properties.trigger_in_event); 429 430 env->DeleteLocalRef(implementor); 431 env->DeleteLocalRef(description); 432 env->DeleteLocalRef(uuid); 433 if (newModuleDesc == NULL) { 434 status = SOUNDTRIGGER_STATUS_ERROR; 435 goto exit; 436 } 437 env->CallBooleanMethod(jModules, gArrayListMethods.add, newModuleDesc); 438 } 439 440 exit: 441 free(nModules); 442 return (jint) status; 443 } 444 445 static void 446 android_hardware_SoundTrigger_setup(JNIEnv *env, jobject thiz, jobject weak_this) 447 { 448 ALOGV("setup"); 449 450 sp<JNISoundTriggerCallback> callback = new JNISoundTriggerCallback(env, thiz, weak_this); 451 452 sound_trigger_module_handle_t handle = 453 (sound_trigger_module_handle_t)env->GetIntField(thiz, gModuleFields.mId); 454 455 sp<SoundTrigger> module = SoundTrigger::attach(handle, callback); 456 if (module == 0) { 457 return; 458 } 459 460 setSoundTrigger(env, thiz, module); 461 } 462 463 static void 464 android_hardware_SoundTrigger_detach(JNIEnv *env, jobject thiz) 465 { 466 ALOGV("detach"); 467 sp<SoundTrigger> module = setSoundTrigger(env, thiz, 0); 468 ALOGV("detach module %p", module.get()); 469 if (module != 0) { 470 ALOGV("detach module->detach()"); 471 module->detach(); 472 } 473 } 474 475 static void 476 android_hardware_SoundTrigger_finalize(JNIEnv *env, jobject thiz) 477 { 478 ALOGV("finalize"); 479 sp<SoundTrigger> module = getSoundTrigger(env, thiz); 480 if (module != 0) { 481 ALOGW("SoundTrigger finalized without being detached"); 482 } 483 android_hardware_SoundTrigger_detach(env, thiz); 484 } 485 486 static jint 487 android_hardware_SoundTrigger_loadSoundModel(JNIEnv *env, jobject thiz, 488 jobject jSoundModel, jintArray jHandle) 489 { 490 jint status = SOUNDTRIGGER_STATUS_OK; 491 jbyte *nData = NULL; 492 struct sound_trigger_sound_model *nSoundModel; 493 jbyteArray jData; 494 sp<MemoryDealer> memoryDealer; 495 sp<IMemory> memory; 496 size_t size; 497 sound_model_handle_t handle; 498 jobject jUuid; 499 jstring jUuidString; 500 const char *nUuidString; 501 502 ALOGV("loadSoundModel"); 503 sp<SoundTrigger> module = getSoundTrigger(env, thiz); 504 if (module == NULL) { 505 return SOUNDTRIGGER_STATUS_ERROR; 506 } 507 if (jHandle == NULL) { 508 return SOUNDTRIGGER_STATUS_BAD_VALUE; 509 } 510 jsize jHandleLen = env->GetArrayLength(jHandle); 511 if (jHandleLen == 0) { 512 return SOUNDTRIGGER_STATUS_BAD_VALUE; 513 } 514 jint *nHandle = env->GetIntArrayElements(jHandle, NULL); 515 if (nHandle == NULL) { 516 return SOUNDTRIGGER_STATUS_ERROR; 517 } 518 if (!env->IsInstanceOf(jSoundModel, gSoundModelClass)) { 519 status = SOUNDTRIGGER_STATUS_BAD_VALUE; 520 goto exit; 521 } 522 size_t offset; 523 sound_trigger_sound_model_type_t type; 524 if (env->IsInstanceOf(jSoundModel, gKeyphraseSoundModelClass)) { 525 offset = sizeof(struct sound_trigger_phrase_sound_model); 526 type = SOUND_MODEL_TYPE_KEYPHRASE; 527 } else { 528 offset = sizeof(struct sound_trigger_sound_model); 529 type = SOUND_MODEL_TYPE_UNKNOWN; 530 } 531 532 jUuid = env->GetObjectField(jSoundModel, gSoundModelFields.uuid); 533 jUuidString = (jstring)env->CallObjectMethod(jUuid, gUUIDMethods.toString); 534 nUuidString = env->GetStringUTFChars(jUuidString, NULL); 535 sound_trigger_uuid_t nUuid; 536 SoundTrigger::stringToGuid(nUuidString, &nUuid); 537 env->ReleaseStringUTFChars(jUuidString, nUuidString); 538 env->DeleteLocalRef(jUuidString); 539 540 sound_trigger_uuid_t nVendorUuid; 541 jUuid = env->GetObjectField(jSoundModel, gSoundModelFields.vendorUuid); 542 if (jUuid != NULL) { 543 jUuidString = (jstring)env->CallObjectMethod(jUuid, gUUIDMethods.toString); 544 nUuidString = env->GetStringUTFChars(jUuidString, NULL); 545 SoundTrigger::stringToGuid(nUuidString, &nVendorUuid); 546 env->ReleaseStringUTFChars(jUuidString, nUuidString); 547 env->DeleteLocalRef(jUuidString); 548 } else { 549 SoundTrigger::stringToGuid("00000000-0000-0000-0000-000000000000", &nVendorUuid); 550 } 551 552 jData = (jbyteArray)env->GetObjectField(jSoundModel, gSoundModelFields.data); 553 if (jData == NULL) { 554 status = SOUNDTRIGGER_STATUS_BAD_VALUE; 555 goto exit; 556 } 557 size = env->GetArrayLength(jData); 558 559 nData = env->GetByteArrayElements(jData, NULL); 560 if (jData == NULL) { 561 status = SOUNDTRIGGER_STATUS_ERROR; 562 goto exit; 563 } 564 565 memoryDealer = new MemoryDealer(offset + size, "SoundTrigge-JNI::LoadModel"); 566 if (memoryDealer == 0) { 567 status = SOUNDTRIGGER_STATUS_ERROR; 568 goto exit; 569 } 570 memory = memoryDealer->allocate(offset + size); 571 if (memory == 0 || memory->pointer() == NULL) { 572 status = SOUNDTRIGGER_STATUS_ERROR; 573 goto exit; 574 } 575 576 nSoundModel = (struct sound_trigger_sound_model *)memory->pointer(); 577 578 nSoundModel->type = type; 579 nSoundModel->uuid = nUuid; 580 nSoundModel->vendor_uuid = nVendorUuid; 581 nSoundModel->data_size = size; 582 nSoundModel->data_offset = offset; 583 memcpy((char *)nSoundModel + offset, nData, size); 584 if (type == SOUND_MODEL_TYPE_KEYPHRASE) { 585 struct sound_trigger_phrase_sound_model *phraseModel = 586 (struct sound_trigger_phrase_sound_model *)nSoundModel; 587 588 jobjectArray jPhrases = 589 (jobjectArray)env->GetObjectField(jSoundModel, gKeyphraseSoundModelFields.keyphrases); 590 if (jPhrases == NULL) { 591 status = SOUNDTRIGGER_STATUS_BAD_VALUE; 592 goto exit; 593 } 594 595 size_t numPhrases = env->GetArrayLength(jPhrases); 596 phraseModel->num_phrases = numPhrases; 597 ALOGV("loadSoundModel numPhrases %zu", numPhrases); 598 for (size_t i = 0; i < numPhrases; i++) { 599 jobject jPhrase = env->GetObjectArrayElement(jPhrases, i); 600 phraseModel->phrases[i].id = 601 env->GetIntField(jPhrase,gKeyphraseFields.id); 602 phraseModel->phrases[i].recognition_mode = 603 env->GetIntField(jPhrase,gKeyphraseFields.recognitionModes); 604 605 jintArray jUsers = (jintArray)env->GetObjectField(jPhrase, gKeyphraseFields.users); 606 phraseModel->phrases[i].num_users = env->GetArrayLength(jUsers); 607 jint *nUsers = env->GetIntArrayElements(jUsers, NULL); 608 memcpy(phraseModel->phrases[i].users, 609 nUsers, 610 phraseModel->phrases[i].num_users * sizeof(int)); 611 env->ReleaseIntArrayElements(jUsers, nUsers, 0); 612 env->DeleteLocalRef(jUsers); 613 614 jstring jLocale = (jstring)env->GetObjectField(jPhrase, gKeyphraseFields.locale); 615 const char *nLocale = env->GetStringUTFChars(jLocale, NULL); 616 strncpy(phraseModel->phrases[i].locale, 617 nLocale, 618 SOUND_TRIGGER_MAX_LOCALE_LEN); 619 jstring jText = (jstring)env->GetObjectField(jPhrase, gKeyphraseFields.text); 620 const char *nText = env->GetStringUTFChars(jText, NULL); 621 strncpy(phraseModel->phrases[i].text, 622 nText, 623 SOUND_TRIGGER_MAX_STRING_LEN); 624 625 env->ReleaseStringUTFChars(jLocale, nLocale); 626 env->DeleteLocalRef(jLocale); 627 env->ReleaseStringUTFChars(jText, nText); 628 env->DeleteLocalRef(jText); 629 ALOGV("loadSoundModel phrases %zu text %s locale %s", 630 i, phraseModel->phrases[i].text, phraseModel->phrases[i].locale); 631 env->DeleteLocalRef(jPhrase); 632 } 633 env->DeleteLocalRef(jPhrases); 634 } 635 status = module->loadSoundModel(memory, &handle); 636 ALOGV("loadSoundModel status %d handle %d", status, handle); 637 638 exit: 639 if (nHandle != NULL) { 640 nHandle[0] = (jint)handle; 641 env->ReleaseIntArrayElements(jHandle, nHandle, NULL); 642 } 643 if (nData != NULL) { 644 env->ReleaseByteArrayElements(jData, nData, NULL); 645 } 646 return status; 647 } 648 649 static jint 650 android_hardware_SoundTrigger_unloadSoundModel(JNIEnv *env, jobject thiz, 651 jint jHandle) 652 { 653 jint status = SOUNDTRIGGER_STATUS_OK; 654 ALOGV("unloadSoundModel"); 655 sp<SoundTrigger> module = getSoundTrigger(env, thiz); 656 if (module == NULL) { 657 return SOUNDTRIGGER_STATUS_ERROR; 658 } 659 status = module->unloadSoundModel((sound_model_handle_t)jHandle); 660 661 return status; 662 } 663 664 static jint 665 android_hardware_SoundTrigger_startRecognition(JNIEnv *env, jobject thiz, 666 jint jHandle, jobject jConfig) 667 { 668 jint status = SOUNDTRIGGER_STATUS_OK; 669 ALOGV("startRecognition"); 670 sp<SoundTrigger> module = getSoundTrigger(env, thiz); 671 if (module == NULL) { 672 return SOUNDTRIGGER_STATUS_ERROR; 673 } 674 675 if (!env->IsInstanceOf(jConfig, gRecognitionConfigClass)) { 676 return SOUNDTRIGGER_STATUS_BAD_VALUE; 677 } 678 679 jbyteArray jData = (jbyteArray)env->GetObjectField(jConfig, gRecognitionConfigFields.data); 680 jsize dataSize = 0; 681 jbyte *nData = NULL; 682 if (jData != NULL) { 683 dataSize = env->GetArrayLength(jData); 684 if (dataSize == 0) { 685 return SOUNDTRIGGER_STATUS_BAD_VALUE; 686 } 687 nData = env->GetByteArrayElements(jData, NULL); 688 if (nData == NULL) { 689 return SOUNDTRIGGER_STATUS_ERROR; 690 } 691 } 692 693 size_t totalSize = sizeof(struct sound_trigger_recognition_config) + dataSize; 694 sp<MemoryDealer> memoryDealer = 695 new MemoryDealer(totalSize, "SoundTrigge-JNI::StartRecognition"); 696 if (memoryDealer == 0) { 697 return SOUNDTRIGGER_STATUS_ERROR; 698 } 699 sp<IMemory> memory = memoryDealer->allocate(totalSize); 700 if (memory == 0 || memory->pointer() == NULL) { 701 return SOUNDTRIGGER_STATUS_ERROR; 702 } 703 if (dataSize != 0) { 704 memcpy((char *)memory->pointer() + sizeof(struct sound_trigger_recognition_config), 705 nData, 706 dataSize); 707 env->ReleaseByteArrayElements(jData, nData, 0); 708 } 709 env->DeleteLocalRef(jData); 710 struct sound_trigger_recognition_config *config = 711 (struct sound_trigger_recognition_config *)memory->pointer(); 712 config->data_size = dataSize; 713 config->data_offset = sizeof(struct sound_trigger_recognition_config); 714 config->capture_requested = env->GetIntField(jConfig, 715 gRecognitionConfigFields.captureRequested); 716 717 config->num_phrases = 0; 718 jobjectArray jPhrases = 719 (jobjectArray)env->GetObjectField(jConfig, gRecognitionConfigFields.keyphrases); 720 if (jPhrases != NULL) { 721 config->num_phrases = env->GetArrayLength(jPhrases); 722 } 723 ALOGV("startRecognition num phrases %d", config->num_phrases); 724 for (size_t i = 0; i < config->num_phrases; i++) { 725 jobject jPhrase = env->GetObjectArrayElement(jPhrases, i); 726 config->phrases[i].id = env->GetIntField(jPhrase, 727 gKeyphraseRecognitionExtraFields.id); 728 config->phrases[i].recognition_modes = env->GetIntField(jPhrase, 729 gKeyphraseRecognitionExtraFields.recognitionModes); 730 config->phrases[i].confidence_level = env->GetIntField(jPhrase, 731 gKeyphraseRecognitionExtraFields.coarseConfidenceLevel); 732 config->phrases[i].num_levels = 0; 733 jobjectArray jConfidenceLevels = (jobjectArray)env->GetObjectField(jPhrase, 734 gKeyphraseRecognitionExtraFields.confidenceLevels); 735 if (jConfidenceLevels != NULL) { 736 config->phrases[i].num_levels = env->GetArrayLength(jConfidenceLevels); 737 } 738 ALOGV("startRecognition phrase %zu num_levels %d", i, config->phrases[i].num_levels); 739 for (size_t j = 0; j < config->phrases[i].num_levels; j++) { 740 jobject jConfidenceLevel = env->GetObjectArrayElement(jConfidenceLevels, j); 741 config->phrases[i].levels[j].user_id = env->GetIntField(jConfidenceLevel, 742 gConfidenceLevelFields.userId); 743 config->phrases[i].levels[j].level = env->GetIntField(jConfidenceLevel, 744 gConfidenceLevelFields.confidenceLevel); 745 env->DeleteLocalRef(jConfidenceLevel); 746 } 747 ALOGV("startRecognition phrases %zu", i); 748 env->DeleteLocalRef(jConfidenceLevels); 749 env->DeleteLocalRef(jPhrase); 750 } 751 env->DeleteLocalRef(jPhrases); 752 753 status = module->startRecognition(jHandle, memory); 754 return status; 755 } 756 757 static jint 758 android_hardware_SoundTrigger_stopRecognition(JNIEnv *env, jobject thiz, 759 jint jHandle) 760 { 761 jint status = SOUNDTRIGGER_STATUS_OK; 762 ALOGV("stopRecognition"); 763 sp<SoundTrigger> module = getSoundTrigger(env, thiz); 764 if (module == NULL) { 765 return SOUNDTRIGGER_STATUS_ERROR; 766 } 767 status = module->stopRecognition(jHandle); 768 return status; 769 } 770 771 static JNINativeMethod gMethods[] = { 772 {"listModules", 773 "(Ljava/util/ArrayList;)I", 774 (void *)android_hardware_SoundTrigger_listModules}, 775 }; 776 777 778 static JNINativeMethod gModuleMethods[] = { 779 {"native_setup", 780 "(Ljava/lang/Object;)V", 781 (void *)android_hardware_SoundTrigger_setup}, 782 {"native_finalize", 783 "()V", 784 (void *)android_hardware_SoundTrigger_finalize}, 785 {"detach", 786 "()V", 787 (void *)android_hardware_SoundTrigger_detach}, 788 {"loadSoundModel", 789 "(Landroid/hardware/soundtrigger/SoundTrigger$SoundModel;[I)I", 790 (void *)android_hardware_SoundTrigger_loadSoundModel}, 791 {"unloadSoundModel", 792 "(I)I", 793 (void *)android_hardware_SoundTrigger_unloadSoundModel}, 794 {"startRecognition", 795 "(ILandroid/hardware/soundtrigger/SoundTrigger$RecognitionConfig;)I", 796 (void *)android_hardware_SoundTrigger_startRecognition}, 797 {"stopRecognition", 798 "(I)I", 799 (void *)android_hardware_SoundTrigger_stopRecognition}, 800 }; 801 802 int register_android_hardware_SoundTrigger(JNIEnv *env) 803 { 804 jclass arrayListClass = env->FindClass("java/util/ArrayList"); 805 gArrayListClass = (jclass) env->NewGlobalRef(arrayListClass); 806 gArrayListMethods.add = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z"); 807 808 jclass uuidClass = env->FindClass("java/util/UUID"); 809 gUUIDClass = (jclass) env->NewGlobalRef(uuidClass); 810 gUUIDMethods.toString = env->GetMethodID(uuidClass, "toString", "()Ljava/lang/String;"); 811 812 jclass lClass = env->FindClass(kSoundTriggerClassPathName); 813 gSoundTriggerClass = (jclass) env->NewGlobalRef(lClass); 814 815 jclass moduleClass = env->FindClass(kModuleClassPathName); 816 gModuleClass = (jclass) env->NewGlobalRef(moduleClass); 817 gPostEventFromNative = env->GetStaticMethodID(moduleClass, "postEventFromNative", 818 "(Ljava/lang/Object;IIILjava/lang/Object;)V"); 819 gModuleFields.mNativeContext = env->GetFieldID(moduleClass, "mNativeContext", "J"); 820 gModuleFields.mId = env->GetFieldID(moduleClass, "mId", "I"); 821 822 823 jclass modulePropertiesClass = env->FindClass(kModulePropertiesClassPathName); 824 gModulePropertiesClass = (jclass) env->NewGlobalRef(modulePropertiesClass); 825 gModulePropertiesCstor = env->GetMethodID(modulePropertiesClass, "<init>", 826 "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;IIIIIZIZIZ)V"); 827 828 jclass soundModelClass = env->FindClass(kSoundModelClassPathName); 829 gSoundModelClass = (jclass) env->NewGlobalRef(soundModelClass); 830 gSoundModelFields.uuid = env->GetFieldID(soundModelClass, "uuid", "Ljava/util/UUID;"); 831 gSoundModelFields.vendorUuid = env->GetFieldID(soundModelClass, "vendorUuid", "Ljava/util/UUID;"); 832 gSoundModelFields.data = env->GetFieldID(soundModelClass, "data", "[B"); 833 834 jclass keyphraseClass = env->FindClass(kKeyphraseClassPathName); 835 gKeyphraseClass = (jclass) env->NewGlobalRef(keyphraseClass); 836 gKeyphraseFields.id = env->GetFieldID(keyphraseClass, "id", "I"); 837 gKeyphraseFields.recognitionModes = env->GetFieldID(keyphraseClass, "recognitionModes", "I"); 838 gKeyphraseFields.locale = env->GetFieldID(keyphraseClass, "locale", "Ljava/lang/String;"); 839 gKeyphraseFields.text = env->GetFieldID(keyphraseClass, "text", "Ljava/lang/String;"); 840 gKeyphraseFields.users = env->GetFieldID(keyphraseClass, "users", "[I"); 841 842 jclass keyphraseSoundModelClass = env->FindClass(kKeyphraseSoundModelClassPathName); 843 gKeyphraseSoundModelClass = (jclass) env->NewGlobalRef(keyphraseSoundModelClass); 844 gKeyphraseSoundModelFields.keyphrases = env->GetFieldID(keyphraseSoundModelClass, 845 "keyphrases", 846 "[Landroid/hardware/soundtrigger/SoundTrigger$Keyphrase;"); 847 848 849 jclass recognitionEventClass = env->FindClass(kRecognitionEventClassPathName); 850 gRecognitionEventClass = (jclass) env->NewGlobalRef(recognitionEventClass); 851 gRecognitionEventCstor = env->GetMethodID(recognitionEventClass, "<init>", 852 "(IIZIIIZLandroid/media/AudioFormat;[B)V"); 853 854 jclass keyphraseRecognitionEventClass = env->FindClass(kKeyphraseRecognitionEventClassPathName); 855 gKeyphraseRecognitionEventClass = (jclass) env->NewGlobalRef(keyphraseRecognitionEventClass); 856 gKeyphraseRecognitionEventCstor = env->GetMethodID(keyphraseRecognitionEventClass, "<init>", 857 "(IIZIIIZLandroid/media/AudioFormat;[B[Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;)V"); 858 859 860 jclass keyRecognitionConfigClass = env->FindClass(kRecognitionConfigClassPathName); 861 gRecognitionConfigClass = (jclass) env->NewGlobalRef(keyRecognitionConfigClass); 862 gRecognitionConfigFields.captureRequested = env->GetFieldID(keyRecognitionConfigClass, 863 "captureRequested", 864 "Z"); 865 gRecognitionConfigFields.keyphrases = env->GetFieldID(keyRecognitionConfigClass, 866 "keyphrases", 867 "[Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;"); 868 gRecognitionConfigFields.data = env->GetFieldID(keyRecognitionConfigClass, 869 "data", 870 "[B"); 871 872 jclass keyphraseRecognitionExtraClass = env->FindClass(kKeyphraseRecognitionExtraClassPathName); 873 gKeyphraseRecognitionExtraClass = (jclass) env->NewGlobalRef(keyphraseRecognitionExtraClass); 874 gKeyphraseRecognitionExtraCstor = env->GetMethodID(keyphraseRecognitionExtraClass, "<init>", 875 "(III[Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;)V"); 876 gKeyphraseRecognitionExtraFields.id = env->GetFieldID(gKeyphraseRecognitionExtraClass, "id", "I"); 877 gKeyphraseRecognitionExtraFields.recognitionModes = env->GetFieldID(gKeyphraseRecognitionExtraClass, 878 "recognitionModes", "I"); 879 gKeyphraseRecognitionExtraFields.coarseConfidenceLevel = env->GetFieldID(gKeyphraseRecognitionExtraClass, 880 "coarseConfidenceLevel", "I"); 881 gKeyphraseRecognitionExtraFields.confidenceLevels = env->GetFieldID(gKeyphraseRecognitionExtraClass, 882 "confidenceLevels", 883 "[Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;"); 884 885 jclass confidenceLevelClass = env->FindClass(kConfidenceLevelClassPathName); 886 gConfidenceLevelClass = (jclass) env->NewGlobalRef(confidenceLevelClass); 887 gConfidenceLevelCstor = env->GetMethodID(confidenceLevelClass, "<init>", "(II)V"); 888 gConfidenceLevelFields.userId = env->GetFieldID(confidenceLevelClass, "userId", "I"); 889 gConfidenceLevelFields.confidenceLevel = env->GetFieldID(confidenceLevelClass, 890 "confidenceLevel", "I"); 891 892 jclass audioFormatClass = env->FindClass(kAudioFormatClassPathName); 893 gAudioFormatClass = (jclass) env->NewGlobalRef(audioFormatClass); 894 gAudioFormatCstor = env->GetMethodID(audioFormatClass, "<init>", "(III)V"); 895 896 jclass soundModelEventClass = env->FindClass(kSoundModelEventClassPathName); 897 gSoundModelEventClass = (jclass) env->NewGlobalRef(soundModelEventClass); 898 gSoundModelEventCstor = env->GetMethodID(soundModelEventClass, "<init>", 899 "(II[B)V"); 900 901 902 int status = AndroidRuntime::registerNativeMethods(env, 903 kSoundTriggerClassPathName, gMethods, NELEM(gMethods)); 904 905 if (status == 0) { 906 status = AndroidRuntime::registerNativeMethods(env, 907 kModuleClassPathName, gModuleMethods, NELEM(gModuleMethods)); 908 } 909 910 911 return status; 912 } 913