/* ** ** 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 "core_jni_helpers.h" #include #include #include #include #include #include #include #include "android_media_AudioFormat.h" using namespace android; static jclass gArrayListClass; static struct { jmethodID add; } gArrayListMethods; static jclass gUUIDClass; static struct { jmethodID toString; } gUUIDMethods; 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 uuid; jfieldID vendorUuid; jfieldID data; } gSoundModelFields; static const char* const kKeyphraseClassPathName = "android/hardware/soundtrigger/SoundTrigger$Keyphrase"; static jclass gKeyphraseClass; static struct { jfieldID id; jfieldID recognitionModes; jfieldID locale; jfieldID text; jfieldID users; } gKeyphraseFields; static const char* const kKeyphraseSoundModelClassPathName = "android/hardware/soundtrigger/SoundTrigger$KeyphraseSoundModel"; static jclass gKeyphraseSoundModelClass; static struct { jfieldID keyphrases; } gKeyphraseSoundModelFields; static const char* const kRecognitionConfigClassPathName = "android/hardware/soundtrigger/SoundTrigger$RecognitionConfig"; static jclass gRecognitionConfigClass; static struct { jfieldID captureRequested; jfieldID keyphrases; jfieldID data; } gRecognitionConfigFields; 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 struct { jfieldID id; jfieldID recognitionModes; jfieldID coarseConfidenceLevel; jfieldID confidenceLevels; } gKeyphraseRecognitionExtraFields; static const char* const kConfidenceLevelClassPathName = "android/hardware/soundtrigger/SoundTrigger$ConfidenceLevel"; static jclass gConfidenceLevelClass; static jmethodID gConfidenceLevelCstor; static struct { jfieldID userId; jfieldID confidenceLevel; } gConfidenceLevelFields; static const char* const kAudioFormatClassPathName = "android/media/AudioFormat"; static jclass gAudioFormatClass; static jmethodID gAudioFormatCstor; static const char* const kSoundModelEventClassPathName = "android/hardware/soundtrigger/SoundTrigger$SoundModelEvent"; static jclass gSoundModelEventClass; static jmethodID gSoundModelEventCstor; 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, SOUNDTRIGGER_EVENT_SOUNDMODEL = 3, SOUNDTRIGGER_EVENT_SERVICE_STATE_CHANGE = 4, }; // ---------------------------------------------------------------------------- // 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 onSoundModelEvent(struct sound_trigger_model_event *event); virtual void onServiceStateChange(sound_trigger_service_state_t state); 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 = NULL; 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); } jobject jAudioFormat = NULL; if (event->trigger_in_data || event->capture_available) { jAudioFormat = env->NewObject(gAudioFormatClass, gAudioFormatCstor, audioFormatFromNative(event->audio_config.format), event->audio_config.sample_rate, inChannelMaskFromNative(event->audio_config.channel_mask)); } 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++) { jobjectArray jConfidenceLevels = env->NewObjectArray( phraseEvent->phrase_extras[i].num_levels, gConfidenceLevelClass, NULL); if (jConfidenceLevels == NULL) { return; } for (size_t j = 0; j < phraseEvent->phrase_extras[i].num_levels; j++) { jobject jConfidenceLevel = env->NewObject(gConfidenceLevelClass, gConfidenceLevelCstor, phraseEvent->phrase_extras[i].levels[j].user_id, phraseEvent->phrase_extras[i].levels[j].level); env->SetObjectArrayElement(jConfidenceLevels, j, jConfidenceLevel); env->DeleteLocalRef(jConfidenceLevel); } jobject jNewExtra = env->NewObject(gKeyphraseRecognitionExtraClass, gKeyphraseRecognitionExtraCstor, phraseEvent->phrase_extras[i].id, phraseEvent->phrase_extras[i].recognition_modes, phraseEvent->phrase_extras[i].confidence_level, jConfidenceLevels); if (jNewExtra == NULL) { return; } env->SetObjectArrayElement(jExtras, i, jNewExtra); env->DeleteLocalRef(jNewExtra); env->DeleteLocalRef(jConfidenceLevels); } jEvent = env->NewObject(gKeyphraseRecognitionEventClass, gKeyphraseRecognitionEventCstor, event->status, event->model, event->capture_available, event->capture_session, event->capture_delay_ms, event->capture_preamble_ms, event->trigger_in_data, jAudioFormat, jData, jExtras); env->DeleteLocalRef(jExtras); } else { jEvent = env->NewObject(gRecognitionEventClass, gRecognitionEventCstor, event->status, event->model, event->capture_available, event->capture_session, event->capture_delay_ms, event->capture_preamble_ms, event->trigger_in_data, jAudioFormat, jData); } if (jAudioFormat != NULL) { env->DeleteLocalRef(jAudioFormat); } if (jData != NULL) { env->DeleteLocalRef(jData); } env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject, SOUNDTRIGGER_EVENT_RECOGNITION, 0, 0, jEvent); env->DeleteLocalRef(jEvent); if (env->ExceptionCheck()) { ALOGW("An exception occurred while notifying an event."); env->ExceptionClear(); } } void JNISoundTriggerCallback::onSoundModelEvent(struct sound_trigger_model_event *event) { JNIEnv *env = AndroidRuntime::getJNIEnv(); jobject jEvent = NULL; 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); } jEvent = env->NewObject(gSoundModelEventClass, gSoundModelEventCstor, event->status, event->model, jData); env->DeleteLocalRef(jData); env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject, SOUNDTRIGGER_EVENT_SOUNDMODEL, 0, 0, jEvent); env->DeleteLocalRef(jEvent); if (env->ExceptionCheck()) { ALOGW("An exception occurred while notifying an event."); env->ExceptionClear(); } } void JNISoundTriggerCallback::onServiceStateChange(sound_trigger_service_state_t state) { JNIEnv *env = AndroidRuntime::getJNIEnv(); env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject, SOUNDTRIGGER_EVENT_SERVICE_STATE_CHANGE, state, 0, NULL); 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 %zu 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, nModules[i].properties.trigger_in_event); 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; jbyte *nData = NULL; struct sound_trigger_sound_model *nSoundModel; jbyteArray jData; sp memoryDealer; sp memory; size_t size; sound_model_handle_t handle; jobject jUuid; jstring jUuidString; const char *nUuidString; 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; } jUuid = env->GetObjectField(jSoundModel, gSoundModelFields.uuid); jUuidString = (jstring)env->CallObjectMethod(jUuid, gUUIDMethods.toString); nUuidString = env->GetStringUTFChars(jUuidString, NULL); sound_trigger_uuid_t nUuid; SoundTrigger::stringToGuid(nUuidString, &nUuid); env->ReleaseStringUTFChars(jUuidString, nUuidString); env->DeleteLocalRef(jUuidString); sound_trigger_uuid_t nVendorUuid; jUuid = env->GetObjectField(jSoundModel, gSoundModelFields.vendorUuid); if (jUuid != NULL) { jUuidString = (jstring)env->CallObjectMethod(jUuid, gUUIDMethods.toString); nUuidString = env->GetStringUTFChars(jUuidString, NULL); SoundTrigger::stringToGuid(nUuidString, &nVendorUuid); env->ReleaseStringUTFChars(jUuidString, nUuidString); env->DeleteLocalRef(jUuidString); } else { SoundTrigger::stringToGuid("00000000-0000-0000-0000-000000000000", &nVendorUuid); } jData = (jbyteArray)env->GetObjectField(jSoundModel, gSoundModelFields.data); if (jData == NULL) { status = SOUNDTRIGGER_STATUS_BAD_VALUE; goto exit; } size = env->GetArrayLength(jData); nData = 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->uuid = nUuid; nSoundModel->vendor_uuid = nVendorUuid; 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 %zu", numPhrases); for (size_t i = 0; i < numPhrases; i++) { jobject jPhrase = env->GetObjectArrayElement(jPhrases, i); phraseModel->phrases[i].id = env->GetIntField(jPhrase,gKeyphraseFields.id); phraseModel->phrases[i].recognition_mode = env->GetIntField(jPhrase,gKeyphraseFields.recognitionModes); jintArray jUsers = (jintArray)env->GetObjectField(jPhrase, gKeyphraseFields.users); phraseModel->phrases[i].num_users = env->GetArrayLength(jUsers); jint *nUsers = env->GetIntArrayElements(jUsers, NULL); memcpy(phraseModel->phrases[i].users, nUsers, phraseModel->phrases[i].num_users * sizeof(int)); env->ReleaseIntArrayElements(jUsers, nUsers, 0); env->DeleteLocalRef(jUsers); 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 %zu text %s locale %s", i, phraseModel->phrases[i].text, phraseModel->phrases[i].locale); env->DeleteLocalRef(jPhrase); } 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, 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, jobject jConfig) { jint status = SOUNDTRIGGER_STATUS_OK; ALOGV("startRecognition"); sp module = getSoundTrigger(env, thiz); if (module == NULL) { return SOUNDTRIGGER_STATUS_ERROR; } if (!env->IsInstanceOf(jConfig, gRecognitionConfigClass)) { return SOUNDTRIGGER_STATUS_BAD_VALUE; } jbyteArray jData = (jbyteArray)env->GetObjectField(jConfig, gRecognitionConfigFields.data); jsize dataSize = 0; jbyte *nData = NULL; if (jData != NULL) { dataSize = env->GetArrayLength(jData); if (dataSize == 0) { return SOUNDTRIGGER_STATUS_BAD_VALUE; } nData = env->GetByteArrayElements(jData, NULL); if (nData == NULL) { return SOUNDTRIGGER_STATUS_ERROR; } } size_t totalSize = sizeof(struct sound_trigger_recognition_config) + dataSize; sp memoryDealer = new MemoryDealer(totalSize, "SoundTrigge-JNI::StartRecognition"); if (memoryDealer == 0) { return SOUNDTRIGGER_STATUS_ERROR; } sp memory = memoryDealer->allocate(totalSize); if (memory == 0 || memory->pointer() == NULL) { return SOUNDTRIGGER_STATUS_ERROR; } if (dataSize != 0) { memcpy((char *)memory->pointer() + sizeof(struct sound_trigger_recognition_config), nData, dataSize); env->ReleaseByteArrayElements(jData, nData, 0); } env->DeleteLocalRef(jData); struct sound_trigger_recognition_config *config = (struct sound_trigger_recognition_config *)memory->pointer(); config->data_size = dataSize; config->data_offset = sizeof(struct sound_trigger_recognition_config); config->capture_requested = env->GetBooleanField(jConfig, gRecognitionConfigFields.captureRequested); config->num_phrases = 0; jobjectArray jPhrases = (jobjectArray)env->GetObjectField(jConfig, gRecognitionConfigFields.keyphrases); if (jPhrases != NULL) { config->num_phrases = env->GetArrayLength(jPhrases); } ALOGV("startRecognition num phrases %d", config->num_phrases); for (size_t i = 0; i < config->num_phrases; i++) { jobject jPhrase = env->GetObjectArrayElement(jPhrases, i); config->phrases[i].id = env->GetIntField(jPhrase, gKeyphraseRecognitionExtraFields.id); config->phrases[i].recognition_modes = env->GetIntField(jPhrase, gKeyphraseRecognitionExtraFields.recognitionModes); config->phrases[i].confidence_level = env->GetIntField(jPhrase, gKeyphraseRecognitionExtraFields.coarseConfidenceLevel); config->phrases[i].num_levels = 0; jobjectArray jConfidenceLevels = (jobjectArray)env->GetObjectField(jPhrase, gKeyphraseRecognitionExtraFields.confidenceLevels); if (jConfidenceLevels != NULL) { config->phrases[i].num_levels = env->GetArrayLength(jConfidenceLevels); } ALOGV("startRecognition phrase %zu num_levels %d", i, config->phrases[i].num_levels); for (size_t j = 0; j < config->phrases[i].num_levels; j++) { jobject jConfidenceLevel = env->GetObjectArrayElement(jConfidenceLevels, j); config->phrases[i].levels[j].user_id = env->GetIntField(jConfidenceLevel, gConfidenceLevelFields.userId); config->phrases[i].levels[j].level = env->GetIntField(jConfidenceLevel, gConfidenceLevelFields.confidenceLevel); env->DeleteLocalRef(jConfidenceLevel); } ALOGV("startRecognition phrases %zu", i); env->DeleteLocalRef(jConfidenceLevels); env->DeleteLocalRef(jPhrase); } env->DeleteLocalRef(jPhrases); 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", "(ILandroid/hardware/soundtrigger/SoundTrigger$RecognitionConfig;)I", (void *)android_hardware_SoundTrigger_startRecognition}, {"stopRecognition", "(I)I", (void *)android_hardware_SoundTrigger_stopRecognition}, }; int register_android_hardware_SoundTrigger(JNIEnv *env) { jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList"); gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass); gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z"); jclass uuidClass = FindClassOrDie(env, "java/util/UUID"); gUUIDClass = MakeGlobalRefOrDie(env, uuidClass); gUUIDMethods.toString = GetMethodIDOrDie(env, uuidClass, "toString", "()Ljava/lang/String;"); jclass lClass = FindClassOrDie(env, kSoundTriggerClassPathName); gSoundTriggerClass = MakeGlobalRefOrDie(env, lClass); jclass moduleClass = FindClassOrDie(env, kModuleClassPathName); gModuleClass = MakeGlobalRefOrDie(env, moduleClass); gPostEventFromNative = GetStaticMethodIDOrDie(env, moduleClass, "postEventFromNative", "(Ljava/lang/Object;IIILjava/lang/Object;)V"); gModuleFields.mNativeContext = GetFieldIDOrDie(env, moduleClass, "mNativeContext", "J"); gModuleFields.mId = GetFieldIDOrDie(env, moduleClass, "mId", "I"); jclass modulePropertiesClass = FindClassOrDie(env, kModulePropertiesClassPathName); gModulePropertiesClass = MakeGlobalRefOrDie(env, modulePropertiesClass); gModulePropertiesCstor = GetMethodIDOrDie(env, modulePropertiesClass, "", "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;IIIIIZIZIZ)V"); jclass soundModelClass = FindClassOrDie(env, kSoundModelClassPathName); gSoundModelClass = MakeGlobalRefOrDie(env, soundModelClass); gSoundModelFields.uuid = GetFieldIDOrDie(env, soundModelClass, "uuid", "Ljava/util/UUID;"); gSoundModelFields.vendorUuid = GetFieldIDOrDie(env, soundModelClass, "vendorUuid", "Ljava/util/UUID;"); gSoundModelFields.data = GetFieldIDOrDie(env, soundModelClass, "data", "[B"); jclass keyphraseClass = FindClassOrDie(env, kKeyphraseClassPathName); gKeyphraseClass = MakeGlobalRefOrDie(env, keyphraseClass); gKeyphraseFields.id = GetFieldIDOrDie(env, keyphraseClass, "id", "I"); gKeyphraseFields.recognitionModes = GetFieldIDOrDie(env, keyphraseClass, "recognitionModes", "I"); gKeyphraseFields.locale = GetFieldIDOrDie(env, keyphraseClass, "locale", "Ljava/lang/String;"); gKeyphraseFields.text = GetFieldIDOrDie(env, keyphraseClass, "text", "Ljava/lang/String;"); gKeyphraseFields.users = GetFieldIDOrDie(env, keyphraseClass, "users", "[I"); jclass keyphraseSoundModelClass = FindClassOrDie(env, kKeyphraseSoundModelClassPathName); gKeyphraseSoundModelClass = MakeGlobalRefOrDie(env, keyphraseSoundModelClass); gKeyphraseSoundModelFields.keyphrases = GetFieldIDOrDie(env, keyphraseSoundModelClass, "keyphrases", "[Landroid/hardware/soundtrigger/SoundTrigger$Keyphrase;"); jclass recognitionEventClass = FindClassOrDie(env, kRecognitionEventClassPathName); gRecognitionEventClass = MakeGlobalRefOrDie(env, recognitionEventClass); gRecognitionEventCstor = GetMethodIDOrDie(env, recognitionEventClass, "", "(IIZIIIZLandroid/media/AudioFormat;[B)V"); jclass keyphraseRecognitionEventClass = FindClassOrDie(env, kKeyphraseRecognitionEventClassPathName); gKeyphraseRecognitionEventClass = MakeGlobalRefOrDie(env, keyphraseRecognitionEventClass); gKeyphraseRecognitionEventCstor = GetMethodIDOrDie(env, keyphraseRecognitionEventClass, "", "(IIZIIIZLandroid/media/AudioFormat;[B[Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;)V"); jclass keyRecognitionConfigClass = FindClassOrDie(env, kRecognitionConfigClassPathName); gRecognitionConfigClass = MakeGlobalRefOrDie(env, keyRecognitionConfigClass); gRecognitionConfigFields.captureRequested = GetFieldIDOrDie(env, keyRecognitionConfigClass, "captureRequested", "Z"); gRecognitionConfigFields.keyphrases = GetFieldIDOrDie(env, keyRecognitionConfigClass, "keyphrases", "[Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;"); gRecognitionConfigFields.data = GetFieldIDOrDie(env, keyRecognitionConfigClass, "data", "[B"); jclass keyphraseRecognitionExtraClass = FindClassOrDie(env, kKeyphraseRecognitionExtraClassPathName); gKeyphraseRecognitionExtraClass = MakeGlobalRefOrDie(env, keyphraseRecognitionExtraClass); gKeyphraseRecognitionExtraCstor = GetMethodIDOrDie(env, keyphraseRecognitionExtraClass, "", "(III[Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;)V"); gKeyphraseRecognitionExtraFields.id = GetFieldIDOrDie(env, gKeyphraseRecognitionExtraClass, "id", "I"); gKeyphraseRecognitionExtraFields.recognitionModes = GetFieldIDOrDie(env, gKeyphraseRecognitionExtraClass, "recognitionModes", "I"); gKeyphraseRecognitionExtraFields.coarseConfidenceLevel = GetFieldIDOrDie(env, gKeyphraseRecognitionExtraClass, "coarseConfidenceLevel", "I"); gKeyphraseRecognitionExtraFields.confidenceLevels = GetFieldIDOrDie(env, gKeyphraseRecognitionExtraClass, "confidenceLevels", "[Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;"); jclass confidenceLevelClass = FindClassOrDie(env, kConfidenceLevelClassPathName); gConfidenceLevelClass = MakeGlobalRefOrDie(env, confidenceLevelClass); gConfidenceLevelCstor = GetMethodIDOrDie(env, confidenceLevelClass, "", "(II)V"); gConfidenceLevelFields.userId = GetFieldIDOrDie(env, confidenceLevelClass, "userId", "I"); gConfidenceLevelFields.confidenceLevel = GetFieldIDOrDie(env, confidenceLevelClass, "confidenceLevel", "I"); jclass audioFormatClass = FindClassOrDie(env, kAudioFormatClassPathName); gAudioFormatClass = MakeGlobalRefOrDie(env, audioFormatClass); gAudioFormatCstor = GetMethodIDOrDie(env, audioFormatClass, "", "(IIII)V"); jclass soundModelEventClass = FindClassOrDie(env, kSoundModelEventClassPathName); gSoundModelEventClass = MakeGlobalRefOrDie(env, soundModelEventClass); gSoundModelEventCstor = GetMethodIDOrDie(env, soundModelEventClass, "", "(II[B)V"); RegisterMethodsOrDie(env, kSoundTriggerClassPathName, gMethods, NELEM(gMethods)); return RegisterMethodsOrDie(env, kModuleClassPathName, gModuleMethods, NELEM(gModuleMethods)); }