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
34using namespace android;
35
36static jclass gArrayListClass;
37static struct {
38    jmethodID    add;
39} gArrayListMethods;
40
41static jclass gUUIDClass;
42static struct {
43    jmethodID    toString;
44} gUUIDMethods;
45
46static const char* const kSoundTriggerClassPathName = "android/hardware/soundtrigger/SoundTrigger";
47static jclass gSoundTriggerClass;
48
49static const char* const kModuleClassPathName = "android/hardware/soundtrigger/SoundTriggerModule";
50static jclass gModuleClass;
51static struct {
52    jfieldID    mNativeContext;
53    jfieldID    mId;
54} gModuleFields;
55static jmethodID   gPostEventFromNative;
56
57static const char* const kModulePropertiesClassPathName =
58                                     "android/hardware/soundtrigger/SoundTrigger$ModuleProperties";
59static jclass gModulePropertiesClass;
60static jmethodID   gModulePropertiesCstor;
61
62static const char* const kSoundModelClassPathName =
63                                     "android/hardware/soundtrigger/SoundTrigger$SoundModel";
64static jclass gSoundModelClass;
65static struct {
66    jfieldID    uuid;
67    jfieldID    vendorUuid;
68    jfieldID    data;
69} gSoundModelFields;
70
71static const char* const kKeyphraseClassPathName =
72                                     "android/hardware/soundtrigger/SoundTrigger$Keyphrase";
73static jclass gKeyphraseClass;
74static struct {
75    jfieldID id;
76    jfieldID recognitionModes;
77    jfieldID locale;
78    jfieldID text;
79    jfieldID users;
80} gKeyphraseFields;
81
82static const char* const kKeyphraseSoundModelClassPathName =
83                                 "android/hardware/soundtrigger/SoundTrigger$KeyphraseSoundModel";
84static jclass gKeyphraseSoundModelClass;
85static struct {
86    jfieldID    keyphrases;
87} gKeyphraseSoundModelFields;
88
89static const char* const kRecognitionConfigClassPathName =
90                                     "android/hardware/soundtrigger/SoundTrigger$RecognitionConfig";
91static jclass gRecognitionConfigClass;
92static struct {
93    jfieldID captureRequested;
94    jfieldID keyphrases;
95    jfieldID data;
96} gRecognitionConfigFields;
97
98static const char* const kRecognitionEventClassPathName =
99                                     "android/hardware/soundtrigger/SoundTrigger$RecognitionEvent";
100static jclass gRecognitionEventClass;
101static jmethodID   gRecognitionEventCstor;
102
103static const char* const kKeyphraseRecognitionEventClassPathName =
104                             "android/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionEvent";
105static jclass gKeyphraseRecognitionEventClass;
106static jmethodID   gKeyphraseRecognitionEventCstor;
107
108static const char* const kKeyphraseRecognitionExtraClassPathName =
109                             "android/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra";
110static jclass gKeyphraseRecognitionExtraClass;
111static jmethodID   gKeyphraseRecognitionExtraCstor;
112static struct {
113    jfieldID id;
114    jfieldID recognitionModes;
115    jfieldID coarseConfidenceLevel;
116    jfieldID confidenceLevels;
117} gKeyphraseRecognitionExtraFields;
118
119static const char* const kConfidenceLevelClassPathName =
120                             "android/hardware/soundtrigger/SoundTrigger$ConfidenceLevel";
121static jclass gConfidenceLevelClass;
122static jmethodID   gConfidenceLevelCstor;
123static struct {
124    jfieldID userId;
125    jfieldID confidenceLevel;
126} gConfidenceLevelFields;
127
128static const char* const kAudioFormatClassPathName =
129                             "android/media/AudioFormat";
130static jclass gAudioFormatClass;
131static jmethodID gAudioFormatCstor;
132
133static const char* const kSoundModelEventClassPathName =
134                                     "android/hardware/soundtrigger/SoundTrigger$SoundModelEvent";
135static jclass gSoundModelEventClass;
136static jmethodID   gSoundModelEventCstor;
137
138static Mutex gLock;
139
140enum {
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
150enum  {
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
159class JNISoundTriggerCallback: public SoundTriggerCallback
160{
161public:
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
170private:
171    jclass      mClass;     // Reference to SoundTrigger class
172    jobject     mObject;    // Weak ref to SoundTrigger Java object to call on
173};
174
175JNISoundTriggerCallback::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
192JNISoundTriggerCallback::~JNISoundTriggerCallback()
193{
194    // remove global references
195    JNIEnv *env = AndroidRuntime::getJNIEnv();
196    env->DeleteGlobalRef(mObject);
197    env->DeleteGlobalRef(mClass);
198}
199
200void 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
294void 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
320void 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
331void 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
345static 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
353static 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
369static jint
370android_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
440exit:
441    free(nModules);
442    return (jint) status;
443}
444
445static void
446android_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
463static void
464android_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
475static void
476android_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
486static jint
487android_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
638exit:
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
649static jint
650android_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
664static jint
665android_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
757static jint
758android_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
771static JNINativeMethod gMethods[] = {
772    {"listModules",
773        "(Ljava/util/ArrayList;)I",
774        (void *)android_hardware_SoundTrigger_listModules},
775};
776
777
778static 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
802int 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