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
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 kGenericSoundModelClassPathName =
72                                     "android/hardware/soundtrigger/SoundTrigger$GenericSoundModel";
73static jclass gGenericSoundModelClass;
74
75static const char* const kKeyphraseClassPathName =
76                                     "android/hardware/soundtrigger/SoundTrigger$Keyphrase";
77static jclass gKeyphraseClass;
78static struct {
79    jfieldID id;
80    jfieldID recognitionModes;
81    jfieldID locale;
82    jfieldID text;
83    jfieldID users;
84} gKeyphraseFields;
85
86static const char* const kKeyphraseSoundModelClassPathName =
87                                 "android/hardware/soundtrigger/SoundTrigger$KeyphraseSoundModel";
88static jclass gKeyphraseSoundModelClass;
89static struct {
90    jfieldID    keyphrases;
91} gKeyphraseSoundModelFields;
92
93static const char* const kRecognitionConfigClassPathName =
94                                     "android/hardware/soundtrigger/SoundTrigger$RecognitionConfig";
95static jclass gRecognitionConfigClass;
96static struct {
97    jfieldID captureRequested;
98    jfieldID keyphrases;
99    jfieldID data;
100} gRecognitionConfigFields;
101
102static const char* const kRecognitionEventClassPathName =
103                                     "android/hardware/soundtrigger/SoundTrigger$RecognitionEvent";
104static jclass gRecognitionEventClass;
105static jmethodID   gRecognitionEventCstor;
106
107static const char* const kKeyphraseRecognitionEventClassPathName =
108                             "android/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionEvent";
109static jclass gKeyphraseRecognitionEventClass;
110static jmethodID   gKeyphraseRecognitionEventCstor;
111
112static const char* const kGenericRecognitionEventClassPathName =
113                             "android/hardware/soundtrigger/SoundTrigger$GenericRecognitionEvent";
114static jclass gGenericRecognitionEventClass;
115static jmethodID   gGenericRecognitionEventCstor;
116
117static const char* const kKeyphraseRecognitionExtraClassPathName =
118                             "android/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra";
119static jclass gKeyphraseRecognitionExtraClass;
120static jmethodID   gKeyphraseRecognitionExtraCstor;
121static struct {
122    jfieldID id;
123    jfieldID recognitionModes;
124    jfieldID coarseConfidenceLevel;
125    jfieldID confidenceLevels;
126} gKeyphraseRecognitionExtraFields;
127
128static const char* const kConfidenceLevelClassPathName =
129                             "android/hardware/soundtrigger/SoundTrigger$ConfidenceLevel";
130static jclass gConfidenceLevelClass;
131static jmethodID   gConfidenceLevelCstor;
132static struct {
133    jfieldID userId;
134    jfieldID confidenceLevel;
135} gConfidenceLevelFields;
136
137static const char* const kAudioFormatClassPathName =
138                             "android/media/AudioFormat";
139static jclass gAudioFormatClass;
140static jmethodID gAudioFormatCstor;
141
142static const char* const kSoundModelEventClassPathName =
143                                     "android/hardware/soundtrigger/SoundTrigger$SoundModelEvent";
144static jclass gSoundModelEventClass;
145static jmethodID   gSoundModelEventCstor;
146
147static Mutex gLock;
148
149enum {
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
159enum  {
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
168class JNISoundTriggerCallback: public SoundTriggerCallback
169{
170public:
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
179private:
180    jclass      mClass;     // Reference to SoundTrigger class
181    jobject     mObject;    // Weak ref to SoundTrigger Java object to call on
182};
183
184JNISoundTriggerCallback::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
201JNISoundTriggerCallback::~JNISoundTriggerCallback()
202{
203    // remove global references
204    JNIEnv *env = AndroidRuntime::getJNIEnv();
205    env->DeleteGlobalRef(mObject);
206    env->DeleteGlobalRef(mClass);
207}
208
209void 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
309void 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
335void 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
346void 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
360static 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
368static 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
384static jint
385android_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
455exit:
456    free(nModules);
457    return (jint) status;
458}
459
460static void
461android_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
478static void
479android_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
490static void
491android_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
501static jint
502android_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
658exit:
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
669static jint
670android_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
684static jint
685android_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
777static jint
778android_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
791static const JNINativeMethod gMethods[] = {
792    {"listModules",
793        "(Ljava/util/ArrayList;)I",
794        (void *)android_hardware_SoundTrigger_listModules},
795};
796
797
798static 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
822int 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