SoundTrigger: update API
class Keyphrase: replaced number of users by list of user IDs.
class RecognitionEvent: added capture preamble duration.
class KeyphraseRecognitionEvent: add keyphrase ID and explicit list of
user ID/confidence level pairs.
startRecognition(): takes a RecognitionConfig specifying the list of
keyphrases to listen to as well as for each keyphrase the recognition mode,
users and min confidence levels for each user.
Bug: 12378680.
Change-Id: I57036cc83b5d9e90512b8bb3f951af3a4a74c0ce
diff --git a/core/jni/android_hardware_SoundTrigger.cpp b/core/jni/android_hardware_SoundTrigger.cpp
index afb92a1..c9a0b1e 100644
--- a/core/jni/android_hardware_SoundTrigger.cpp
+++ b/core/jni/android_hardware_SoundTrigger.cpp
@@ -74,7 +74,7 @@
jfieldID recognitionModes;
jfieldID locale;
jfieldID text;
- jfieldID numUsers;
+ jfieldID users;
} gKeyphraseFields;
static const char* const kKeyphraseSoundModelClassPathName =
@@ -84,6 +84,14 @@
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";
@@ -99,6 +107,20 @@
"android/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra";
static jclass gKeyphraseRecognitionExtraClass;
static jmethodID gKeyphraseRecognitionExtraCstor;
+static struct {
+ jfieldID id;
+ jfieldID recognitionModes;
+ 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 Mutex gLock;
@@ -183,34 +205,45 @@
}
for (size_t i = 0; i < phraseEvent->num_phrases; i++) {
- jintArray jConfidenceLevels = env->NewIntArray(phraseEvent->phrase_extras[i].num_users);
+ jobjectArray jConfidenceLevels = env->NewObjectArray(
+ phraseEvent->phrase_extras[i].num_levels,
+ gConfidenceLevelClass, NULL);
+
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);
+ 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,
- jConfidenceLevels,
- phraseEvent->phrase_extras[i].recognition_modes);
+ phraseEvent->phrase_extras[i].id,
+ phraseEvent->phrase_extras[i].recognition_modes,
+ 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, jData,
+ event->capture_session, event->capture_delay_ms,
+ event->capture_preamble_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);
+ event->capture_session, event->capture_delay_ms,
+ event->capture_preamble_ms, jData);
}
@@ -381,7 +414,7 @@
jobject jSoundModel, jintArray jHandle)
{
jint status = SOUNDTRIGGER_STATUS_OK;
- char *nData = NULL;
+ jbyte *nData = NULL;
struct sound_trigger_sound_model *nSoundModel;
jbyteArray jData;
sp<MemoryDealer> memoryDealer;
@@ -437,7 +470,7 @@
}
size = env->GetArrayLength(jData);
- nData = (char *)env->GetByteArrayElements(jData, NULL);
+ nData = env->GetByteArrayElements(jData, NULL);
if (jData == NULL) {
status = SOUNDTRIGGER_STATUS_ERROR;
goto exit;
@@ -481,8 +514,16 @@
env->GetIntField(jPhrase,gKeyphraseFields.id);
phraseModel->phrases[i].recognition_mode =
env->GetIntField(jPhrase,gKeyphraseFields.recognitionModes);
- phraseModel->phrases[i].num_users =
- env->GetIntField(jPhrase, gKeyphraseFields.numUsers);
+
+ 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,
@@ -500,6 +541,7 @@
env->DeleteLocalRef(jText);
ALOGV("loadSoundModel phrases %d text %s locale %s",
i, phraseModel->phrases[i].text, phraseModel->phrases[i].locale);
+ env->DeleteLocalRef(jPhrase);
}
env->DeleteLocalRef(jPhrases);
}
@@ -512,7 +554,7 @@
env->ReleaseIntArrayElements(jHandle, nHandle, NULL);
}
if (nData != NULL) {
- env->ReleaseByteArrayElements(jData, (jbyte *)nData, NULL);
+ env->ReleaseByteArrayElements(jData, nData, NULL);
}
return status;
}
@@ -534,7 +576,7 @@
static jint
android_hardware_SoundTrigger_startRecognition(JNIEnv *env, jobject thiz,
- jint jHandle, jbyteArray jData)
+ jint jHandle, jobject jConfig)
{
jint status = SOUNDTRIGGER_STATUS_OK;
ALOGV("startRecognition");
@@ -542,30 +584,83 @@
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;
- char *nData = NULL;
- sp<IMemory> memory;
+ jbyte *nData = NULL;
if (jData != NULL) {
dataSize = env->GetArrayLength(jData);
if (dataSize == 0) {
return SOUNDTRIGGER_STATUS_BAD_VALUE;
}
- nData = (char *)env->GetByteArrayElements(jData, NULL);
+ nData = env->GetByteArrayElements(jData, NULL);
if (nData == NULL) {
return SOUNDTRIGGER_STATUS_ERROR;
}
- sp<MemoryDealer> 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);
}
+ 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->GetIntField(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].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 %d 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 %d", i);
+ env->DeleteLocalRef(jConfidenceLevels);
+ env->DeleteLocalRef(jPhrase);
+ }
+ env->DeleteLocalRef(jPhrases);
+
status = module->startRecognition(jHandle, memory);
return status;
}
@@ -608,7 +703,7 @@
"(I)I",
(void *)android_hardware_SoundTrigger_unloadSoundModel},
{"startRecognition",
- "(I[B)I",
+ "(ILandroid/hardware/soundtrigger/SoundTrigger$RecognitionConfig;)I",
(void *)android_hardware_SoundTrigger_startRecognition},
{"stopRecognition",
"(I)I",
@@ -652,7 +747,7 @@
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");
+ gKeyphraseFields.users = env->GetFieldID(keyphraseClass, "users", "[I");
jclass keyphraseSoundModelClass = env->FindClass(kKeyphraseSoundModelClassPathName);
gKeyphraseSoundModelClass = (jclass) env->NewGlobalRef(keyphraseSoundModelClass);
@@ -664,18 +759,42 @@
jclass recognitionEventClass = env->FindClass(kRecognitionEventClassPathName);
gRecognitionEventClass = (jclass) env->NewGlobalRef(recognitionEventClass);
gRecognitionEventCstor = env->GetMethodID(recognitionEventClass, "<init>",
- "(IIZII[B)V");
+ "(IIZIII[B)V");
jclass keyphraseRecognitionEventClass = env->FindClass(kKeyphraseRecognitionEventClassPathName);
gKeyphraseRecognitionEventClass = (jclass) env->NewGlobalRef(keyphraseRecognitionEventClass);
gKeyphraseRecognitionEventCstor = env->GetMethodID(keyphraseRecognitionEventClass, "<init>",
- "(IIZII[BZ[Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;)V");
+ "(IIZIII[BZ[Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;)V");
+ jclass keyRecognitionConfigClass = env->FindClass(kRecognitionConfigClassPathName);
+ gRecognitionConfigClass = (jclass) env->NewGlobalRef(keyRecognitionConfigClass);
+ gRecognitionConfigFields.captureRequested = env->GetFieldID(keyRecognitionConfigClass,
+ "captureRequested",
+ "Z");
+ gRecognitionConfigFields.keyphrases = env->GetFieldID(keyRecognitionConfigClass,
+ "keyphrases",
+ "[Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;");
+ gRecognitionConfigFields.data = env->GetFieldID(keyRecognitionConfigClass,
+ "data",
+ "[B");
+
jclass keyphraseRecognitionExtraClass = env->FindClass(kKeyphraseRecognitionExtraClassPathName);
gKeyphraseRecognitionExtraClass = (jclass) env->NewGlobalRef(keyphraseRecognitionExtraClass);
gKeyphraseRecognitionExtraCstor = env->GetMethodID(keyphraseRecognitionExtraClass, "<init>",
- "([II)V");
+ "(II[Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;)V");
+ gKeyphraseRecognitionExtraFields.id = env->GetFieldID(gKeyphraseRecognitionExtraClass, "id", "I");
+ gKeyphraseRecognitionExtraFields.recognitionModes = env->GetFieldID(gKeyphraseRecognitionExtraClass, "recognitionModes", "I");
+ gKeyphraseRecognitionExtraFields.confidenceLevels = env->GetFieldID(gKeyphraseRecognitionExtraClass,
+ "confidenceLevels",
+ "[Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;");
+
+ jclass confidenceLevelClass = env->FindClass(kConfidenceLevelClassPathName);
+ gConfidenceLevelClass = (jclass) env->NewGlobalRef(confidenceLevelClass);
+ gConfidenceLevelCstor = env->GetMethodID(confidenceLevelClass, "<init>", "(II)V");
+ gConfidenceLevelFields.userId = env->GetFieldID(confidenceLevelClass, "userId", "I");
+ gConfidenceLevelFields.confidenceLevel = env->GetFieldID(confidenceLevelClass,
+ "confidenceLevel", "I");
int status = AndroidRuntime::registerNativeMethods(env,
kSoundTriggerClassPathName, gMethods, NELEM(gMethods));
@@ -685,5 +804,6 @@
kModuleClassPathName, gModuleMethods, NELEM(gModuleMethods));
}
+
return status;
}