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