blob: 1c4c9ecc8a09548a3d7ee40c3d2bfd797a108125 [file] [log] [blame]
/*
**
** 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 <utils/Log.h>
#include "jni.h"
#include "JNIHelp.h"
#include "core_jni_helpers.h"
#include <system/sound_trigger.h>
#include <soundtrigger/SoundTriggerCallback.h>
#include <soundtrigger/SoundTrigger.h>
#include <utils/RefBase.h>
#include <utils/Vector.h>
#include <binder/IMemory.h>
#include <binder/MemoryDealer.h>
#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<SoundTrigger> getSoundTrigger(JNIEnv* env, jobject thiz)
{
Mutex::Autolock l(gLock);
SoundTrigger* const st = (SoundTrigger*)env->GetLongField(thiz,
gModuleFields.mNativeContext);
return sp<SoundTrigger>(st);
}
static sp<SoundTrigger> setSoundTrigger(JNIEnv* env, jobject thiz, const sp<SoundTrigger>& module)
{
Mutex::Autolock l(gLock);
sp<SoundTrigger> 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<JNISoundTriggerCallback> callback = new JNISoundTriggerCallback(env, thiz, weak_this);
sound_trigger_module_handle_t handle =
(sound_trigger_module_handle_t)env->GetIntField(thiz, gModuleFields.mId);
sp<SoundTrigger> 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<SoundTrigger> 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<SoundTrigger> 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> memoryDealer;
sp<IMemory> memory;
size_t size;
sound_model_handle_t handle;
jobject jUuid;
jstring jUuidString;
const char *nUuidString;
ALOGV("loadSoundModel");
sp<SoundTrigger> 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<SoundTrigger> 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<SoundTrigger> 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> memoryDealer =
new MemoryDealer(totalSize, "SoundTrigge-JNI::StartRecognition");
if (memoryDealer == 0) {
return SOUNDTRIGGER_STATUS_ERROR;
}
sp<IMemory> 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<SoundTrigger> 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, "<init>",
"(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, "<init>",
"(IIZIIIZLandroid/media/AudioFormat;[B)V");
jclass keyphraseRecognitionEventClass = FindClassOrDie(env,
kKeyphraseRecognitionEventClassPathName);
gKeyphraseRecognitionEventClass = MakeGlobalRefOrDie(env, keyphraseRecognitionEventClass);
gKeyphraseRecognitionEventCstor = GetMethodIDOrDie(env, keyphraseRecognitionEventClass, "<init>",
"(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,
"<init>", "(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, "<init>", "(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, "<init>", "(IIII)V");
jclass soundModelEventClass = FindClassOrDie(env, kSoundModelEventClassPathName);
gSoundModelEventClass = MakeGlobalRefOrDie(env, soundModelEventClass);
gSoundModelEventCstor = GetMethodIDOrDie(env, soundModelEventClass, "<init>", "(II[B)V");
RegisterMethodsOrDie(env, kSoundTriggerClassPathName, gMethods, NELEM(gMethods));
return RegisterMethodsOrDie(env, kModuleClassPathName, gModuleMethods, NELEM(gModuleMethods));
}