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