From 60b62bc5c11c0bfcdf84ca8f5b2053e5747f86bc Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Fri, 18 Apr 2014 17:50:49 -0700 Subject: [PATCH] add sound trigger JNI Add JNI for sound trigger hardware native service. Change-Id: Idd0ee42c7af5fe20e7d8295994211de3a517bd13 --- core/jni/Android.mk | 2 + core/jni/AndroidRuntime.cpp | 2 + core/jni/android_hardware_SoundTrigger.cpp | 661 +++++++++++++++++++++ 3 files changed, 665 insertions(+) create mode 100644 core/jni/android_hardware_SoundTrigger.cpp diff --git a/core/jni/Android.mk b/core/jni/Android.mk index f446c3a5f10fb..c90a513a72516 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -141,6 +141,7 @@ LOCAL_SRC_FILES:= \ android_hardware_camera2_DngCreator.cpp \ android_hardware_SensorManager.cpp \ android_hardware_SerialPort.cpp \ + android_hardware_SoundTrigger.cpp \ android_hardware_UsbDevice.cpp \ android_hardware_UsbDeviceConnection.cpp \ android_hardware_UsbRequest.cpp \ @@ -236,6 +237,7 @@ LOCAL_SHARED_LIBRARIES := \ libpdfium \ libimg_utils \ libnetd_client \ + libsoundtrigger ifeq ($(USE_OPENGL_RENDERER),true) LOCAL_SHARED_LIBRARIES += libhwui diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index e06987608c545..5172cda851378 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -83,6 +83,7 @@ extern int register_android_hardware_camera2_legacy_LegacyCameraDevice(JNIEnv *e extern int register_android_hardware_camera2_DngCreator(JNIEnv *env); extern int register_android_hardware_SensorManager(JNIEnv *env); extern int register_android_hardware_SerialPort(JNIEnv *env); +extern int register_android_hardware_SoundTrigger(JNIEnv *env); extern int register_android_hardware_UsbDevice(JNIEnv *env); extern int register_android_hardware_UsbDeviceConnection(JNIEnv *env); extern int register_android_hardware_UsbRequest(JNIEnv *env); @@ -1290,6 +1291,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_hardware_camera2_DngCreator), REG_JNI(register_android_hardware_SensorManager), REG_JNI(register_android_hardware_SerialPort), + REG_JNI(register_android_hardware_SoundTrigger), REG_JNI(register_android_hardware_UsbDevice), REG_JNI(register_android_hardware_UsbDeviceConnection), REG_JNI(register_android_hardware_UsbRequest), diff --git a/core/jni/android_hardware_SoundTrigger.cpp b/core/jni/android_hardware_SoundTrigger.cpp new file mode 100644 index 0000000000000..69e991d261abb --- /dev/null +++ b/core/jni/android_hardware_SoundTrigger.cpp @@ -0,0 +1,661 @@ +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "SoundTrigger-JNI" +#include + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace android; + +static jclass gArrayListClass; +static struct { + jmethodID add; +} gArrayListMethods; + +static const char* const kSoundTriggerClassPathName = "android/hardware/soundtrigger/SoundTrigger"; +static jclass gSoundTriggerClass; + +static const char* const kModuleClassPathName = "android/hardware/soundtrigger/SoundTriggerModule"; +static jclass gModuleClass; +static struct { + jfieldID mNativeContext; + jfieldID mId; +} gModuleFields; +static jmethodID gPostEventFromNative; + +static const char* const kModulePropertiesClassPathName = + "android/hardware/soundtrigger/SoundTrigger$ModuleProperties"; +static jclass gModulePropertiesClass; +static jmethodID gModulePropertiesCstor; + +static const char* const kSoundModelClassPathName = + "android/hardware/soundtrigger/SoundTrigger$SoundModel"; +static jclass gSoundModelClass; +static struct { + jfieldID data; +} gSoundModelFields; + +static const char* const kKeyPhraseClassPathName = + "android/hardware/soundtrigger/SoundTrigger$KeyPhrase"; +static jclass gKeyPhraseClass; +static struct { + jfieldID recognitionModes; + jfieldID locale; + jfieldID text; + jfieldID numUsers; +} gKeyPhraseFields; + +static const char* const kKeyPhraseSoundModelClassPathName = + "android/hardware/soundtrigger/SoundTrigger$KeyPhraseSoundModel"; +static jclass gKeyPhraseSoundModelClass; +static struct { + jfieldID keyPhrases; +} gKeyPhraseSoundModelFields; + + +static const char* const kRecognitionEventClassPathName = + "android/hardware/soundtrigger/SoundTrigger$RecognitionEvent"; +static jclass gRecognitionEventClass; +static jmethodID gRecognitionEventCstor; + +static const char* const kKeyPhraseRecognitionEventClassPathName = + "android/hardware/soundtrigger/SoundTrigger$KeyPhraseRecognitionEvent"; +static jclass gKeyPhraseRecognitionEventClass; +static jmethodID gKeyPhraseRecognitionEventCstor; + +static const char* const kKeyPhraseRecognitionExtraClassPathName = + "android/hardware/soundtrigger/SoundTrigger$KeyPhraseRecognitionExtra"; +static jclass gKeyPhraseRecognitionExtraClass; +static jmethodID gKeyPhraseRecognitionExtraCstor; + +static Mutex gLock; + +enum { + SOUNDTRIGGER_STATUS_OK = 0, + SOUNDTRIGGER_STATUS_ERROR = INT_MIN, + SOUNDTRIGGER_PERMISSION_DENIED = -1, + SOUNDTRIGGER_STATUS_NO_INIT = -19, + SOUNDTRIGGER_STATUS_BAD_VALUE = -22, + SOUNDTRIGGER_STATUS_DEAD_OBJECT = -32, + SOUNDTRIGGER_INVALID_OPERATION = -38, +}; + +enum { + SOUNDTRIGGER_EVENT_RECOGNITION = 1, + SOUNDTRIGGER_EVENT_SERVICE_DIED = 2, +}; + +// ---------------------------------------------------------------------------- +// ref-counted object for callbacks +class JNISoundTriggerCallback: public SoundTriggerCallback +{ +public: + JNISoundTriggerCallback(JNIEnv* env, jobject thiz, jobject weak_thiz); + ~JNISoundTriggerCallback(); + + virtual void onRecognitionEvent(struct sound_trigger_recognition_event *event); + virtual void onServiceDied(); + +private: + jclass mClass; // Reference to SoundTrigger class + jobject mObject; // Weak ref to SoundTrigger Java object to call on +}; + +JNISoundTriggerCallback::JNISoundTriggerCallback(JNIEnv* env, jobject thiz, jobject weak_thiz) +{ + + // Hold onto the SoundTriggerModule class for use in calling the static method + // that posts events to the application thread. + jclass clazz = env->GetObjectClass(thiz); + if (clazz == NULL) { + ALOGE("Can't find class %s", kModuleClassPathName); + return; + } + mClass = (jclass)env->NewGlobalRef(clazz); + + // We use a weak reference so the SoundTriggerModule object can be garbage collected. + // The reference is only used as a proxy for callbacks. + mObject = env->NewGlobalRef(weak_thiz); +} + +JNISoundTriggerCallback::~JNISoundTriggerCallback() +{ + // remove global references + JNIEnv *env = AndroidRuntime::getJNIEnv(); + env->DeleteGlobalRef(mObject); + env->DeleteGlobalRef(mClass); +} + +void JNISoundTriggerCallback::onRecognitionEvent(struct sound_trigger_recognition_event *event) +{ + JNIEnv *env = AndroidRuntime::getJNIEnv(); + + jobject jEvent; + + jbyteArray jData = NULL; + if (event->data_size) { + jData = env->NewByteArray(event->data_size); + jbyte *nData = env->GetByteArrayElements(jData, NULL); + memcpy(nData, (char *)event + event->data_offset, event->data_size); + env->ReleaseByteArrayElements(jData, nData, 0); + } + + if (event->type == SOUND_MODEL_TYPE_KEYPHRASE) { + struct sound_trigger_phrase_recognition_event *phraseEvent = + (struct sound_trigger_phrase_recognition_event *)event; + + jobjectArray jExtras = env->NewObjectArray(phraseEvent->num_phrases, + gKeyPhraseRecognitionExtraClass, NULL); + if (jExtras == NULL) { + return; + } + + for (size_t i = 0; i < phraseEvent->num_phrases; i++) { + jintArray jConfidenceLevels = env->NewIntArray(phraseEvent->phrase_extras[i].num_users); + if (jConfidenceLevels == NULL) { + return; + } + jint *nConfidenceLevels = env->GetIntArrayElements(jConfidenceLevels, NULL); + memcpy(nConfidenceLevels, + phraseEvent->phrase_extras[i].confidence_levels, + phraseEvent->phrase_extras[i].num_users * sizeof(int)); + env->ReleaseIntArrayElements(jConfidenceLevels, nConfidenceLevels, 0); + jobject jNewExtra = env->NewObject(gKeyPhraseRecognitionExtraClass, + gKeyPhraseRecognitionExtraCstor, + jConfidenceLevels, + phraseEvent->phrase_extras[i].recognition_modes); + + if (jNewExtra == NULL) { + return; + } + env->SetObjectArrayElement(jExtras, i, jNewExtra); + + } + jEvent = env->NewObject(gKeyPhraseRecognitionEventClass, gKeyPhraseRecognitionEventCstor, + event->status, event->model, event->capture_available, + event->capture_session, event->capture_delay_ms, jData, + phraseEvent->key_phrase_in_capture, jExtras); + } else { + jEvent = env->NewObject(gRecognitionEventClass, gRecognitionEventCstor, + event->status, event->model, event->capture_available, + event->capture_session, event->capture_delay_ms, jData); + } + + + env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject, + SOUNDTRIGGER_EVENT_RECOGNITION, 0, 0, jEvent); + if (env->ExceptionCheck()) { + ALOGW("An exception occurred while notifying an event."); + env->ExceptionClear(); + } +} + +void JNISoundTriggerCallback::onServiceDied() +{ + JNIEnv *env = AndroidRuntime::getJNIEnv(); + + env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject, + SOUNDTRIGGER_EVENT_SERVICE_DIED, 0, 0, NULL); + if (env->ExceptionCheck()) { + ALOGW("An exception occurred while notifying an event."); + env->ExceptionClear(); + } +} + +// ---------------------------------------------------------------------------- + +static sp getSoundTrigger(JNIEnv* env, jobject thiz) +{ + Mutex::Autolock l(gLock); + SoundTrigger* const st = (SoundTrigger*)env->GetLongField(thiz, + gModuleFields.mNativeContext); + return sp(st); +} + +static sp setSoundTrigger(JNIEnv* env, jobject thiz, const sp& module) +{ + Mutex::Autolock l(gLock); + sp old = (SoundTrigger*)env->GetLongField(thiz, + gModuleFields.mNativeContext); + if (module.get()) { + module->incStrong((void*)setSoundTrigger); + } + if (old != 0) { + old->decStrong((void*)setSoundTrigger); + } + env->SetLongField(thiz, gModuleFields.mNativeContext, (jlong)module.get()); + return old; +} + + +static jint +android_hardware_SoundTrigger_listModules(JNIEnv *env, jobject clazz, + jobject jModules) +{ + ALOGV("listModules"); + + if (jModules == NULL) { + ALOGE("listModules NULL AudioPatch ArrayList"); + return SOUNDTRIGGER_STATUS_BAD_VALUE; + } + if (!env->IsInstanceOf(jModules, gArrayListClass)) { + ALOGE("listModules not an arraylist"); + return SOUNDTRIGGER_STATUS_BAD_VALUE; + } + + unsigned int numModules = 0; + struct sound_trigger_module_descriptor *nModules = NULL; + + status_t status = SoundTrigger::listModules(nModules, &numModules); + if (status != NO_ERROR || numModules == 0) { + return (jint)status; + } + + nModules = (struct sound_trigger_module_descriptor *) + calloc(numModules, sizeof(struct sound_trigger_module_descriptor)); + + status = SoundTrigger::listModules(nModules, &numModules); + ALOGV("listModules SoundTrigger::listModules status %d numModules %d", status, numModules); + + if (status != NO_ERROR) { + numModules = 0; + } + + for (size_t i = 0; i < numModules; i++) { + char str[SOUND_TRIGGER_MAX_STRING_LEN]; + + jstring implementor = env->NewStringUTF(nModules[i].properties.implementor); + jstring description = env->NewStringUTF(nModules[i].properties.description); + SoundTrigger::guidToString(&nModules[i].properties.uuid, + str, + SOUND_TRIGGER_MAX_STRING_LEN); + jstring uuid = env->NewStringUTF(str); + + ALOGV("listModules module %d id %d description %s maxSoundModels %d", + i, nModules[i].handle, nModules[i].properties.description, + nModules[i].properties.max_sound_models); + + jobject newModuleDesc = env->NewObject(gModulePropertiesClass, gModulePropertiesCstor, + nModules[i].handle, + implementor, description, uuid, + nModules[i].properties.version, + nModules[i].properties.max_sound_models, + nModules[i].properties.max_key_phrases, + nModules[i].properties.max_users, + nModules[i].properties.recognition_modes, + nModules[i].properties.capture_transition, + nModules[i].properties.max_buffer_ms, + nModules[i].properties.concurrent_capture, + nModules[i].properties.power_consumption_mw); + + env->DeleteLocalRef(implementor); + env->DeleteLocalRef(description); + env->DeleteLocalRef(uuid); + if (newModuleDesc == NULL) { + status = SOUNDTRIGGER_STATUS_ERROR; + goto exit; + } + env->CallBooleanMethod(jModules, gArrayListMethods.add, newModuleDesc); + } + +exit: + free(nModules); + return (jint) status; +} + +static void +android_hardware_SoundTrigger_setup(JNIEnv *env, jobject thiz, jobject weak_this) +{ + ALOGV("setup"); + + sp callback = new JNISoundTriggerCallback(env, thiz, weak_this); + + sound_trigger_module_handle_t handle = + (sound_trigger_module_handle_t)env->GetIntField(thiz, gModuleFields.mId); + + sp module = SoundTrigger::attach(handle, callback); + if (module == 0) { + return; + } + + setSoundTrigger(env, thiz, module); +} + +static void +android_hardware_SoundTrigger_detach(JNIEnv *env, jobject thiz) +{ + ALOGV("detach"); + sp module = setSoundTrigger(env, thiz, 0); + ALOGV("detach module %p", module.get()); + if (module != 0) { + ALOGV("detach module->detach()"); + module->detach(); + } +} + +static void +android_hardware_SoundTrigger_finalize(JNIEnv *env, jobject thiz) +{ + ALOGV("finalize"); + sp module = getSoundTrigger(env, thiz); + if (module != 0) { + ALOGW("SoundTrigger finalized without being detached"); + } + android_hardware_SoundTrigger_detach(env, thiz); +} + +static jint +android_hardware_SoundTrigger_loadSoundModel(JNIEnv *env, jobject thiz, + jobject jSoundModel, jintArray jHandle) +{ + jint status = SOUNDTRIGGER_STATUS_OK; + char *nData = NULL; + struct sound_trigger_sound_model *nSoundModel; + jbyteArray jData; + sp memoryDealer; + sp memory; + size_t size; + sound_model_handle_t handle; + + ALOGV("loadSoundModel"); + sp module = getSoundTrigger(env, thiz); + if (module == NULL) { + return SOUNDTRIGGER_STATUS_ERROR; + } + if (jHandle == NULL) { + return SOUNDTRIGGER_STATUS_BAD_VALUE; + } + jsize jHandleLen = env->GetArrayLength(jHandle); + if (jHandleLen == 0) { + return SOUNDTRIGGER_STATUS_BAD_VALUE; + } + jint *nHandle = env->GetIntArrayElements(jHandle, NULL); + if (nHandle == NULL) { + return SOUNDTRIGGER_STATUS_ERROR; + } + if (!env->IsInstanceOf(jSoundModel, gSoundModelClass)) { + status = SOUNDTRIGGER_STATUS_BAD_VALUE; + goto exit; + } + size_t offset; + sound_trigger_sound_model_type_t type; + if (env->IsInstanceOf(jSoundModel, gKeyPhraseSoundModelClass)) { + offset = sizeof(struct sound_trigger_phrase_sound_model); + type = SOUND_MODEL_TYPE_KEYPHRASE; + } else { + offset = sizeof(struct sound_trigger_sound_model); + type = SOUND_MODEL_TYPE_UNKNOWN; + } + jData = (jbyteArray)env->GetObjectField(jSoundModel, gSoundModelFields.data); + if (jData == NULL) { + status = SOUNDTRIGGER_STATUS_BAD_VALUE; + goto exit; + } + size = env->GetArrayLength(jData); + + nData = (char *)env->GetByteArrayElements(jData, NULL); + if (jData == NULL) { + status = SOUNDTRIGGER_STATUS_ERROR; + goto exit; + } + + memoryDealer = new MemoryDealer(offset + size, "SoundTrigge-JNI::LoadModel"); + if (memoryDealer == 0) { + status = SOUNDTRIGGER_STATUS_ERROR; + goto exit; + } + memory = memoryDealer->allocate(offset + size); + if (memory == 0 || memory->pointer() == NULL) { + status = SOUNDTRIGGER_STATUS_ERROR; + goto exit; + } + + nSoundModel = (struct sound_trigger_sound_model *)memory->pointer(); + + nSoundModel->type = type; + nSoundModel->data_size = size; + nSoundModel->data_offset = offset; + memcpy((char *)nSoundModel + offset, nData, size); + if (type == SOUND_MODEL_TYPE_KEYPHRASE) { + struct sound_trigger_phrase_sound_model *phraseModel = + (struct sound_trigger_phrase_sound_model *)nSoundModel; + + jobjectArray jPhrases = + (jobjectArray)env->GetObjectField(jSoundModel, gKeyPhraseSoundModelFields.keyPhrases); + if (jPhrases == NULL) { + status = SOUNDTRIGGER_STATUS_BAD_VALUE; + goto exit; + } + + size_t numPhrases = env->GetArrayLength(jPhrases); + phraseModel->num_phrases = numPhrases; + ALOGV("loadSoundModel numPhrases %d", numPhrases); + for (size_t i = 0; i < numPhrases; i++) { + jobject jPhrase = env->GetObjectArrayElement(jPhrases, i); + phraseModel->phrases[i].recognition_mode = + env->GetIntField(jPhrase,gKeyPhraseFields.recognitionModes); + phraseModel->phrases[i].num_users = + env->GetIntField(jPhrase, gKeyPhraseFields.numUsers); + jstring jLocale = (jstring)env->GetObjectField(jPhrase, gKeyPhraseFields.locale); + const char *nLocale = env->GetStringUTFChars(jLocale, NULL); + strncpy(phraseModel->phrases[i].locale, + nLocale, + SOUND_TRIGGER_MAX_LOCALE_LEN); + jstring jText = (jstring)env->GetObjectField(jPhrase, gKeyPhraseFields.text); + const char *nText = env->GetStringUTFChars(jText, NULL); + strncpy(phraseModel->phrases[i].text, + nText, + SOUND_TRIGGER_MAX_STRING_LEN); + + env->ReleaseStringUTFChars(jLocale, nLocale); + env->DeleteLocalRef(jLocale); + env->ReleaseStringUTFChars(jText, nText); + env->DeleteLocalRef(jText); + ALOGV("loadSoundModel phrases %d text %s locale %s", + i, phraseModel->phrases[i].text, phraseModel->phrases[i].locale); + } + env->DeleteLocalRef(jPhrases); + } + status = module->loadSoundModel(memory, &handle); + ALOGV("loadSoundModel status %d handle %d", status, handle); + +exit: + if (nHandle != NULL) { + nHandle[0] = (jint)handle; + env->ReleaseIntArrayElements(jHandle, nHandle, NULL); + } + if (nData != NULL) { + env->ReleaseByteArrayElements(jData, (jbyte *)nData, NULL); + } + return status; +} + +static jint +android_hardware_SoundTrigger_unloadSoundModel(JNIEnv *env, jobject thiz, + jint jHandle) +{ + jint status = SOUNDTRIGGER_STATUS_OK; + ALOGV("unloadSoundModel"); + sp module = getSoundTrigger(env, thiz); + if (module == NULL) { + return SOUNDTRIGGER_STATUS_ERROR; + } + status = module->unloadSoundModel((sound_model_handle_t)jHandle); + + return status; +} + +static jint +android_hardware_SoundTrigger_startRecognition(JNIEnv *env, jobject thiz, + jint jHandle, jbyteArray jData) +{ + jint status = SOUNDTRIGGER_STATUS_OK; + ALOGV("startRecognition"); + sp module = getSoundTrigger(env, thiz); + if (module == NULL) { + return SOUNDTRIGGER_STATUS_ERROR; + } + jsize dataSize = 0; + char *nData = NULL; + sp memory; + if (jData != NULL) { + dataSize = env->GetArrayLength(jData); + if (dataSize == 0) { + return SOUNDTRIGGER_STATUS_BAD_VALUE; + } + nData = (char *)env->GetByteArrayElements(jData, NULL); + if (nData == NULL) { + return SOUNDTRIGGER_STATUS_ERROR; + } + sp memoryDealer = + new MemoryDealer(dataSize, "SoundTrigge-JNI::StartRecognition"); + if (memoryDealer == 0) { + return SOUNDTRIGGER_STATUS_ERROR; + } + memory = memoryDealer->allocate(dataSize); + if (memory == 0 || memory->pointer() == NULL) { + return SOUNDTRIGGER_STATUS_ERROR; + } + memcpy(memory->pointer(), nData, dataSize); + } + + status = module->startRecognition(jHandle, memory); + return status; +} + +static jint +android_hardware_SoundTrigger_stopRecognition(JNIEnv *env, jobject thiz, + jint jHandle) +{ + jint status = SOUNDTRIGGER_STATUS_OK; + ALOGV("stopRecognition"); + sp module = getSoundTrigger(env, thiz); + if (module == NULL) { + return SOUNDTRIGGER_STATUS_ERROR; + } + status = module->stopRecognition(jHandle); + return status; +} + +static JNINativeMethod gMethods[] = { + {"listModules", + "(Ljava/util/ArrayList;)I", + (void *)android_hardware_SoundTrigger_listModules}, +}; + + +static JNINativeMethod gModuleMethods[] = { + {"native_setup", + "(Ljava/lang/Object;)V", + (void *)android_hardware_SoundTrigger_setup}, + {"native_finalize", + "()V", + (void *)android_hardware_SoundTrigger_finalize}, + {"detach", + "()V", + (void *)android_hardware_SoundTrigger_detach}, + {"loadSoundModel", + "(Landroid/hardware/soundtrigger/SoundTrigger$SoundModel;[I)I", + (void *)android_hardware_SoundTrigger_loadSoundModel}, + {"unloadSoundModel", + "(I)I", + (void *)android_hardware_SoundTrigger_unloadSoundModel}, + {"startRecognition", + "(I[B)I", + (void *)android_hardware_SoundTrigger_startRecognition}, + {"stopRecognition", + "(I)I", + (void *)android_hardware_SoundTrigger_stopRecognition}, +}; + +int register_android_hardware_SoundTrigger(JNIEnv *env) +{ + jclass arrayListClass = env->FindClass("java/util/ArrayList"); + gArrayListClass = (jclass) env->NewGlobalRef(arrayListClass); + gArrayListMethods.add = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z"); + + jclass lClass = env->FindClass(kSoundTriggerClassPathName); + gSoundTriggerClass = (jclass) env->NewGlobalRef(lClass); + + jclass moduleClass = env->FindClass(kModuleClassPathName); + gModuleClass = (jclass) env->NewGlobalRef(moduleClass); + gPostEventFromNative = env->GetStaticMethodID(moduleClass, "postEventFromNative", + "(Ljava/lang/Object;IIILjava/lang/Object;)V"); + gModuleFields.mNativeContext = env->GetFieldID(moduleClass, "mNativeContext", "J"); + gModuleFields.mId = env->GetFieldID(moduleClass, "mId", "I"); + + + jclass modulePropertiesClass = env->FindClass(kModulePropertiesClassPathName); + gModulePropertiesClass = (jclass) env->NewGlobalRef(modulePropertiesClass); + gModulePropertiesCstor = env->GetMethodID(modulePropertiesClass, "", + "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;IIIIIZIZI)V"); + + jclass soundModelClass = env->FindClass(kSoundModelClassPathName); + gSoundModelClass = (jclass) env->NewGlobalRef(soundModelClass); + gSoundModelFields.data = env->GetFieldID(soundModelClass, "data", "[B"); + + jclass keyPhraseClass = env->FindClass(kKeyPhraseClassPathName); + gKeyPhraseClass = (jclass) env->NewGlobalRef(keyPhraseClass); + gKeyPhraseFields.recognitionModes = env->GetFieldID(keyPhraseClass, "recognitionModes", "I"); + gKeyPhraseFields.locale = env->GetFieldID(keyPhraseClass, "locale", "Ljava/lang/String;"); + gKeyPhraseFields.text = env->GetFieldID(keyPhraseClass, "text", "Ljava/lang/String;"); + gKeyPhraseFields.numUsers = env->GetFieldID(keyPhraseClass, "numUsers", "I"); + + jclass keyPhraseSoundModelClass = env->FindClass(kKeyPhraseSoundModelClassPathName); + gKeyPhraseSoundModelClass = (jclass) env->NewGlobalRef(keyPhraseSoundModelClass); + gKeyPhraseSoundModelFields.keyPhrases = env->GetFieldID(keyPhraseSoundModelClass, + "keyPhrases", + "[Landroid/hardware/soundtrigger/SoundTrigger$KeyPhrase;"); + + + jclass recognitionEventClass = env->FindClass(kRecognitionEventClassPathName); + gRecognitionEventClass = (jclass) env->NewGlobalRef(recognitionEventClass); + gRecognitionEventCstor = env->GetMethodID(recognitionEventClass, "", + "(IIZII[B)V"); + + jclass keyPhraseRecognitionEventClass = env->FindClass(kKeyPhraseRecognitionEventClassPathName); + gKeyPhraseRecognitionEventClass = (jclass) env->NewGlobalRef(keyPhraseRecognitionEventClass); + gKeyPhraseRecognitionEventCstor = env->GetMethodID(keyPhraseRecognitionEventClass, "", + "(IIZII[BZ[Landroid/hardware/soundtrigger/SoundTrigger$KeyPhraseRecognitionExtra;)V"); + + + jclass keyPhraseRecognitionExtraClass = env->FindClass(kKeyPhraseRecognitionExtraClassPathName); + gKeyPhraseRecognitionExtraClass = (jclass) env->NewGlobalRef(keyPhraseRecognitionExtraClass); + gKeyPhraseRecognitionExtraCstor = env->GetMethodID(keyPhraseRecognitionExtraClass, "", + "([II)V"); + + int status = AndroidRuntime::registerNativeMethods(env, + kSoundTriggerClassPathName, gMethods, NELEM(gMethods)); + + if (status == 0) { + status = AndroidRuntime::registerNativeMethods(env, + kModuleClassPathName, gModuleMethods, NELEM(gModuleMethods)); + } + + return status; +}