Merge "SoundTriggerHelper: Add ability to manage multiple voice models." into nyc-dev

am: 59623ee

* commit '59623ee33742e69c4cb24901e5e81e2aca6496e7':
  SoundTriggerHelper: Add ability to manage multiple voice models.

Change-Id: I58ca6746ae13abc99bf938f371bae8b283853e19
This commit is contained in:
Arunesh Mishra
2016-04-14 20:09:17 +00:00
committed by android-build-merger

View File

@@ -84,22 +84,15 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
private final PhoneStateListener mPhoneStateListener; private final PhoneStateListener mPhoneStateListener;
private final PowerManager mPowerManager; private final PowerManager mPowerManager;
// The SoundTriggerManager layer handles multiple generic recognition models. We store the // The SoundTriggerManager layer handles multiple recognition models of type generic and
// ModelData here in a hashmap. // keyphrase. We store the ModelData here in a hashmap.
private final HashMap<UUID, ModelData> mGenericModelDataMap; private final HashMap<UUID, ModelData> mModelDataMap;
// This ModelData instance ensures that the keyphrase sound model is a singleton and // An index of keyphrase sound models so that we can reach them easily. We support indexing
// all other sound models are of type Generic. Any keyphrase sound model will be stored here // keyphrase sound models with a keyphrase ID. Sound model with the same keyphrase ID will
// and any previously running instances will be replaced. This restriction was earlier // replace an existing model, thus there is a 1:1 mapping from keyphrase ID to a voice
// implemented by three instance variables which stored data about the keyphrase // sound model.
// model. That data now gets encapsulated in this ModelData instance. private HashMap<Integer, UUID> mKeyphraseUuidMap;
private ModelData mKeyphraseModelData;
// The keyphrase ID for keyphrase sound models. We store this specially here since ModelData
// does not support this.
// TODO: The role of the keyphrase ID is a bit unclear. Its just used to ensure that
// recognition events have the correct keyphrase ID check.
private int mKeyphraseId = INVALID_VALUE;
private boolean mCallActive = false; private boolean mCallActive = false;
private boolean mIsPowerSaveMode = false; private boolean mIsPowerSaveMode = false;
@@ -119,7 +112,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
mContext = context; mContext = context;
mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mGenericModelDataMap = new HashMap<UUID, ModelData>(); mModelDataMap = new HashMap<UUID, ModelData>();
mKeyphraseUuidMap = new HashMap<Integer, UUID>();
mPhoneStateListener = new MyCallStateListener(); mPhoneStateListener = new MyCallStateListener();
if (status != SoundTrigger.STATUS_OK || modules.size() == 0) { if (status != SoundTrigger.STATUS_OK || modules.size() == 0) {
Slog.w(TAG, "listModules status=" + status + ", # of modules=" + modules.size()); Slog.w(TAG, "listModules status=" + status + ", # of modules=" + modules.size());
@@ -153,6 +147,10 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
synchronized (mLock) { synchronized (mLock) {
ModelData modelData = getOrCreateGenericModelDataLocked(modelId); ModelData modelData = getOrCreateGenericModelDataLocked(modelId);
if (modelData == null) {
Slog.w(TAG, "Irrecoverable error occurred, check UUID / sound model data.");
return STATUS_ERROR;
}
return startRecognition(soundModel, modelData, callback, recognitionConfig, return startRecognition(soundModel, modelData, callback, recognitionConfig,
INVALID_VALUE /* keyphraseId */); INVALID_VALUE /* keyphraseId */);
} }
@@ -180,20 +178,48 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
+ " soundModel=" + soundModel + ", callback=" + callback.asBinder() + " soundModel=" + soundModel + ", callback=" + callback.asBinder()
+ ", recognitionConfig=" + recognitionConfig); + ", recognitionConfig=" + recognitionConfig);
Slog.d(TAG, "moduleProperties=" + mModuleProperties); Slog.d(TAG, "moduleProperties=" + mModuleProperties);
if (mKeyphraseModelData != null) { dumpModelStateLocked();
Slog.d(TAG, mKeyphraseModelData.toString()); }
} else {
Slog.d(TAG, "Null KeyphraseModelData."); ModelData model = getKeyphraseModelDataLocked(keyphraseId);
if (model != null && !model.isKeyphraseModel()) {
Slog.e(TAG, "Generic model with same UUID exists.");
return STATUS_ERROR;
}
// Process existing model first.
if (model != null && model.getModelId() != soundModel.uuid) {
// The existing model has a different UUID, should be replaced.
int status = cleanUpExistingKeyphraseModel(model);
removeKeyphraseModelLocked(keyphraseId);
if (status != STATUS_OK) {
return status;
} }
model = null;
} }
if (mKeyphraseModelData == null) {
mKeyphraseModelData = ModelData.createKeyphraseModelData(soundModel.uuid); // We need to create a new one: either no previous models existed for given keyphrase id
// or the existing model had a different UUID and was cleaned up.
if (model == null) {
model = createKeyphraseModelDataLocked(soundModel.uuid, keyphraseId);
} }
return startRecognition(soundModel, mKeyphraseModelData, callback, recognitionConfig,
return startRecognition(soundModel, model, callback, recognitionConfig,
keyphraseId); keyphraseId);
} }
} }
private int cleanUpExistingKeyphraseModel(ModelData modelData) {
// Stop and clean up a previous ModelData if one exists. This usually is used when the
// previous model has a different UUID for the same keyphrase ID.
int status = tryStopAndUnloadLocked(modelData, true /* stop */, true /* unload */);
if (status != STATUS_OK) {
Slog.w(TAG, "Unable to stop or unload previous model: " +
modelData.toString());
}
return status;
}
/** /**
* Starts recognition for the given sound model. A single routine for both keyphrase and * Starts recognition for the given sound model. A single routine for both keyphrase and
* generic sound models. * generic sound models.
@@ -228,17 +254,15 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
initializeTelephonyAndPowerStateListeners(); initializeTelephonyAndPowerStateListeners();
} }
// If the previous model is different (for the same UUID), ensure that its unloaded // If the existing SoundModel is different (for the same UUID for Generic and same
// and stopped before proceeding. This works for both keyphrase and generic models. // keyphrase ID for voice), ensure that it is unloaded and stopped before proceeding.
// Specifically for keyphrase since we have 'mKeyphraseModelData' holding a single // This works for both keyphrase and generic models. This logic also ensures that a
// allowed instance of such a model, this ensures that a previously loaded (or started) // previously loaded (or started) model is appropriately stopped. Since this is a
// keyphrase model is appropriately stopped. This ensures no regression with the // generalization of the previous logic with a single keyphrase model, we should have
// previous version of this code as given in the startKeyphrase() routine. // no regression with the previous version of this code as was given in the
// // startKeyphrase() routine.
// For generic sound models, all this means is that if we are given a different sound
// model with the same UUID, then we will "replace" it.
if (modelData.getSoundModel() != null) { if (modelData.getSoundModel() != null) {
boolean stopModel = false; // Stop the model after checking that its started. boolean stopModel = false; // Stop the model after checking that it is started.
boolean unloadModel = false; boolean unloadModel = false;
if (modelData.getSoundModel().equals(soundModel) && modelData.isModelStarted()) { if (modelData.getSoundModel().equals(soundModel) && modelData.isModelStarted()) {
// The model has not changed, but the previous model is "started". // The model has not changed, but the previous model is "started".
@@ -273,7 +297,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
modelData.clearCallback(); modelData.clearCallback();
} }
// Load the model if its not loaded. // Load the model if it is not loaded.
if (!modelData.isModelLoaded()) { if (!modelData.isModelLoaded()) {
// Load the model // Load the model
int[] handle = new int[] { INVALID_VALUE }; int[] handle = new int[] { INVALID_VALUE };
@@ -291,9 +315,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
Slog.d(TAG, "Sound model loaded with handle:" + handle[0]); Slog.d(TAG, "Sound model loaded with handle:" + handle[0]);
} }
modelData.setCallback(callback); modelData.setCallback(callback);
if (modelData.isKeyphraseModel()) {
mKeyphraseId = keyphraseId;
}
modelData.setRequested(true); modelData.setRequested(true);
modelData.setRecognitionConfig(recognitionConfig); modelData.setRecognitionConfig(recognitionConfig);
modelData.setSoundModel(soundModel); modelData.setSoundModel(soundModel);
@@ -322,8 +343,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
return STATUS_ERROR; return STATUS_ERROR;
} }
ModelData modelData = mGenericModelDataMap.get(modelId); ModelData modelData = mModelDataMap.get(modelId);
if (modelData == null) { if (modelData == null || !modelData.isGenericModel()) {
Slog.w(TAG, "Attempting stopRecognition on invalid model with id:" + modelId); Slog.w(TAG, "Attempting stopRecognition on invalid model with id:" + modelId);
return STATUS_ERROR; return STATUS_ERROR;
} }
@@ -355,21 +376,23 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
return STATUS_ERROR; return STATUS_ERROR;
} }
ModelData modelData = getKeyphraseModelDataLocked(keyphraseId);
if (modelData == null || !modelData.isKeyphraseModel()) {
Slog.e(TAG, "No model exists for given keyphrase Id.");
return STATUS_ERROR;
}
if (DBG) { if (DBG) {
Slog.d(TAG, "stopRecognition for keyphraseId=" + keyphraseId + ", callback =" + Slog.d(TAG, "stopRecognition for keyphraseId=" + keyphraseId + ", callback =" +
callback.asBinder()); callback.asBinder());
Slog.d(TAG, "current callback=" + (mKeyphraseModelData == null ? "null" : Slog.d(TAG, "current callback=" + (modelData == null ? "null" :
mKeyphraseModelData.getCallback().asBinder())); modelData.getCallback().asBinder()));
} }
int status = stopRecognition(mKeyphraseModelData, callback); int status = stopRecognition(modelData, callback);
if (status != SoundTrigger.STATUS_OK) { if (status != SoundTrigger.STATUS_OK) {
return status; return status;
} }
// We leave the sound model loaded but not started, this helps us when we start
// back.
// Also clear the internal state once the recognition has been stopped.
internalClearKeyphraseStateLocked();
return status; return status;
} }
} }
@@ -424,9 +447,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
internalClearGlobalStateLocked(); internalClearGlobalStateLocked();
} }
if (modelData.isKeyphraseModel()) {
mKeyphraseId = INVALID_VALUE;
}
return status; return status;
} }
} }
@@ -475,25 +495,18 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
return; return;
} }
// Stop Keyphrase recognition if one exists. // Stop all recognition models.
if (mKeyphraseModelData != null && mKeyphraseModelData.getHandle() != INVALID_VALUE) { for (ModelData model : mModelDataMap.values()) {
mKeyphraseModelData.setRequested(false);
int status = updateRecognitionLocked(mKeyphraseModelData, isRecognitionAllowed(),
false /* don't notify for synchronous calls */);
internalClearKeyphraseStateLocked();
}
// Stop all generic recognition models.
for (ModelData model : mGenericModelDataMap.values()) {
if (model.isModelStarted()) { if (model.isModelStarted()) {
model.setRequested(false);
int status = stopRecognitionLocked(model, int status = stopRecognitionLocked(model,
false /* do not notify for synchronous calls */); false /* do not notify for synchronous calls */);
if (status != STATUS_OK) { if (status != STATUS_OK) {
// What else can we do if there is an error here. Slog.w(TAG, "Error stopping keyphrase model: " + model.getHandle());
Slog.w(TAG, "Error stopping generic model: " + model.getHandle());
} }
model.clearState(); model.clearState();
model.clearCallback(); model.clearCallback();
model.setRecognitionConfig(null);
} }
} }
internalClearGlobalStateLocked(); internalClearGlobalStateLocked();
@@ -507,24 +520,27 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
int unloadKeyphraseSoundModel(int keyphraseId) { int unloadKeyphraseSoundModel(int keyphraseId) {
synchronized (mLock) { synchronized (mLock) {
MetricsLogger.count(mContext, "sth_unload_keyphrase_sound_model", 1); MetricsLogger.count(mContext, "sth_unload_keyphrase_sound_model", 1);
if (mModule == null || mKeyphraseModelData == null || ModelData modelData = getKeyphraseModelDataLocked(keyphraseId);
mKeyphraseModelData.getHandle() == INVALID_VALUE) { if (mModule == null || modelData == null || modelData.getHandle() == INVALID_VALUE ||
!modelData.isKeyphraseModel()) {
return STATUS_ERROR; return STATUS_ERROR;
} }
// Stop recognition if it's the current one. // Stop recognition if it's the current one.
mKeyphraseModelData.setRequested(false); modelData.setRequested(false);
int status = updateRecognitionLocked(mKeyphraseModelData, isRecognitionAllowed(), int status = updateRecognitionLocked(modelData, isRecognitionAllowed(),
false /* don't notify */); false /* don't notify */);
if (status != SoundTrigger.STATUS_OK) { if (status != SoundTrigger.STATUS_OK) {
Slog.w(TAG, "Stop recognition failed for keyphrase ID:" + status); Slog.w(TAG, "Stop recognition failed for keyphrase ID:" + status);
} }
status = mModule.unloadSoundModel(mKeyphraseModelData.getHandle()); status = mModule.unloadSoundModel(modelData.getHandle());
if (status != SoundTrigger.STATUS_OK) { if (status != SoundTrigger.STATUS_OK) {
Slog.w(TAG, "unloadKeyphraseSoundModel call failed with " + status); Slog.w(TAG, "unloadKeyphraseSoundModel call failed with " + status);
} }
mKeyphraseModelData.clearState();
// Remove it from existence.
removeKeyphraseModelLocked(keyphraseId);
return status; return status;
} }
} }
@@ -535,8 +551,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
if (modelId == null || mModule == null) { if (modelId == null || mModule == null) {
return STATUS_ERROR; return STATUS_ERROR;
} }
ModelData modelData = mGenericModelDataMap.get(modelId); ModelData modelData = mModelDataMap.get(modelId);
if (modelData == null) { if (modelData == null || !modelData.isGenericModel()) {
Slog.w(TAG, "Unload error: Attempting unload invalid generic model with id:" + Slog.w(TAG, "Unload error: Attempting unload invalid generic model with id:" +
modelId); modelId);
return STATUS_ERROR; return STATUS_ERROR;
@@ -559,8 +575,10 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
Slog.w(TAG, "unloadGenericSoundModel() call failed with " + status); Slog.w(TAG, "unloadGenericSoundModel() call failed with " + status);
Slog.w(TAG, "unloadGenericSoundModel() force-marking model as unloaded."); Slog.w(TAG, "unloadGenericSoundModel() force-marking model as unloaded.");
} }
mGenericModelDataMap.remove(modelId);
if (DBG) dumpGenericModelStateLocked(); // Remove it from existence.
mModelDataMap.remove(modelId);
if (DBG) dumpModelStateLocked();
return status; return status;
} }
} }
@@ -612,7 +630,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
return; return;
} }
ModelData model = getModelDataForLocked(event.soundModelHandle); ModelData model = getModelDataForLocked(event.soundModelHandle);
if (model == null) { if (model == null || !model.isGenericModel()) {
Slog.w(TAG, "Generic recognition event: Model does not exist for handle: " + Slog.w(TAG, "Generic recognition event: Model does not exist for handle: " +
event.soundModelHandle); event.soundModelHandle);
return; return;
@@ -723,67 +741,64 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
} catch (RemoteException e) { } catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onError", e); Slog.w(TAG, "RemoteException in onError", e);
} finally { } finally {
internalClearKeyphraseStateLocked(); internalClearModelStateLocked();
internalClearGenericModelStateLocked();
internalClearGlobalStateLocked(); internalClearGlobalStateLocked();
} }
} }
private int getKeyphraseIdFromEvent(KeyphraseRecognitionEvent event) {
if (event == null) {
Slog.w(TAG, "Null RecognitionEvent received.");
return INVALID_VALUE;
}
KeyphraseRecognitionExtra[] keyphraseExtras =
((KeyphraseRecognitionEvent) event).keyphraseExtras;
if (keyphraseExtras == null || keyphraseExtras.length == 0) {
Slog.w(TAG, "Invalid keyphrase recognition event!");
return INVALID_VALUE;
}
// TODO: Handle more than one keyphrase extras.
return keyphraseExtras[0].id;
}
private void onKeyphraseRecognitionSuccessLocked(KeyphraseRecognitionEvent event) { private void onKeyphraseRecognitionSuccessLocked(KeyphraseRecognitionEvent event) {
Slog.i(TAG, "Recognition success"); Slog.i(TAG, "Recognition success");
MetricsLogger.count(mContext, "sth_keyphrase_recognition_event", 1); MetricsLogger.count(mContext, "sth_keyphrase_recognition_event", 1);
int keyphraseId = getKeyphraseIdFromEvent(event);
ModelData modelData = getKeyphraseModelDataLocked(keyphraseId);
if (mKeyphraseModelData == null) { if (modelData == null || !modelData.isKeyphraseModel()) {
Slog.e(TAG, "Received onRecognition event for null keyphrase model data."); Slog.e(TAG, "Keyphase model data does not exist for ID:" + keyphraseId);
return; return;
} }
if (mKeyphraseModelData.getCallback() == null) { if (modelData.getCallback() == null) {
Slog.w(TAG, "Received onRecognition event without any listener for it."); Slog.w(TAG, "Received onRecognition event without callback for keyphrase model.");
return;
}
KeyphraseRecognitionExtra[] keyphraseExtras =
((KeyphraseRecognitionEvent) event).keyphraseExtras;
if (keyphraseExtras == null || keyphraseExtras.length == 0) {
Slog.w(TAG, "Invalid keyphrase recognition event!");
return;
}
// TODO: Handle more than one keyphrase extras.
if (mKeyphraseId != keyphraseExtras[0].id) {
Slog.w(TAG, "received onRecognition event for a different keyphrase");
return; return;
} }
try { try {
mKeyphraseModelData.getCallback().onKeyphraseDetected( modelData.getCallback().onKeyphraseDetected((KeyphraseRecognitionEvent) event);
(KeyphraseRecognitionEvent) event);
} catch (RemoteException e) { } catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onKeyphraseDetected", e); Slog.w(TAG, "RemoteException in onKeyphraseDetected", e);
} }
mKeyphraseModelData.setStopped(); modelData.setStopped();
RecognitionConfig config = mKeyphraseModelData.getRecognitionConfig(); RecognitionConfig config = modelData.getRecognitionConfig();
if (config != null) { if (config != null) {
// Whether we should continue by starting this again. // Whether we should continue by starting this again.
mKeyphraseModelData.setRequested(config.allowMultipleTriggers); modelData.setRequested(config.allowMultipleTriggers);
} }
// TODO: Remove this block if the lower layer supports multiple triggers. // TODO: Remove this block if the lower layer supports multiple triggers.
if (mKeyphraseModelData.getRequested()) { if (modelData.getRequested()) {
updateRecognitionLocked(mKeyphraseModelData, isRecognitionAllowed(), updateRecognitionLocked(modelData, isRecognitionAllowed(), true /* notify */);
true /* notify */);
} }
} }
private void updateAllRecognitionsLocked(boolean notify) { private void updateAllRecognitionsLocked(boolean notify) {
boolean isAllowed = isRecognitionAllowed(); boolean isAllowed = isRecognitionAllowed();
// Keyphrase model. for (ModelData modelData : mModelDataMap.values()) {
if (mKeyphraseModelData != null) {
updateRecognitionLocked(mKeyphraseModelData, isAllowed, notify);
}
for (UUID modelId : mGenericModelDataMap.keySet()) {
ModelData modelData = mGenericModelDataMap.get(modelId);
updateRecognitionLocked(modelData, isAllowed, notify); updateRecognitionLocked(modelData, isAllowed, notify);
} }
} }
@@ -809,11 +824,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
} catch (RemoteException e) { } catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onError", e); Slog.w(TAG, "RemoteException in onError", e);
} finally { } finally {
if (mKeyphraseModelData != null) { internalClearModelStateLocked();
mKeyphraseModelData.clearState();
}
internalClearKeyphraseStateLocked();
internalClearGenericModelStateLocked();
internalClearGlobalStateLocked(); internalClearGlobalStateLocked();
if (mModule != null) { if (mModule != null) {
mModule.detach(); mModule.detach();
@@ -822,10 +833,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
} }
} }
// internalClearGlobalStateLocked() gets split into two routines. Cleanup that is // internalClearGlobalStateLocked() cleans up the telephony and power save listeners.
// specific to keyphrase sound models named as internalClearKeyphraseStateLocked() and
// internalClearGlobalStateLocked() for global state. The global cleanup routine will be used
// by the cleanup happening with the generic sound models.
private void internalClearGlobalStateLocked() { private void internalClearGlobalStateLocked() {
// Unregister from call state changes. // Unregister from call state changes.
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
@@ -837,20 +845,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
} }
} }
private void internalClearKeyphraseStateLocked() { // Clears state for all models (generic and keyphrase).
if (mKeyphraseModelData != null) { private void internalClearModelStateLocked() {
mKeyphraseModelData.setStopped(); for (ModelData modelData : mModelDataMap.values()) {
mKeyphraseModelData.setRequested(false);
mKeyphraseModelData.setRecognitionConfig(null);
mKeyphraseModelData.setCallback(null);
}
mKeyphraseId = INVALID_VALUE;
}
private void internalClearGenericModelStateLocked() {
for (UUID modelId : mGenericModelDataMap.keySet()) {
ModelData modelData = mGenericModelDataMap.get(modelId);
modelData.clearState(); modelData.clearState();
modelData.clearCallback(); modelData.clearCallback();
} }
@@ -884,14 +881,10 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
synchronized (mLock) { synchronized (mLock) {
pw.print(" module properties="); pw.print(" module properties=");
pw.println(mModuleProperties == null ? "null" : mModuleProperties); pw.println(mModuleProperties == null ? "null" : mModuleProperties);
pw.print(" keyphrase ID="); pw.println(mKeyphraseId);
pw.print(" call active="); pw.println(mCallActive); pw.print(" call active="); pw.println(mCallActive);
pw.print(" power save mode active="); pw.println(mIsPowerSaveMode); pw.print(" power save mode active="); pw.println(mIsPowerSaveMode);
pw.print(" service disabled="); pw.println(mServiceDisabled); pw.print(" service disabled="); pw.println(mServiceDisabled);
if (mKeyphraseModelData != null) {
pw.println(mKeyphraseModelData.toString());
}
} }
} }
@@ -914,34 +907,60 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
// Sends an error callback to all models with a valid registered callback. // Sends an error callback to all models with a valid registered callback.
private void sendErrorCallbacksToAll(int errorCode) throws RemoteException { private void sendErrorCallbacksToAll(int errorCode) throws RemoteException {
IRecognitionStatusCallback keyphraseListener = mKeyphraseModelData.getCallback(); for (ModelData modelData : mModelDataMap.values()) {
if (keyphraseListener != null) { IRecognitionStatusCallback callback = modelData.getCallback();
keyphraseListener.onError(STATUS_ERROR); if (callback != null) {
} callback.onError(STATUS_ERROR);
for (UUID modelId: mGenericModelDataMap.keySet()) {
ModelData modelData = mGenericModelDataMap.get(modelId);
IRecognitionStatusCallback keyphraseCallback = mKeyphraseModelData.getCallback();
if (keyphraseCallback != null) {
keyphraseCallback.onError(STATUS_ERROR);
} }
} }
} }
private ModelData getOrCreateGenericModelDataLocked(UUID modelId) { private ModelData getOrCreateGenericModelDataLocked(UUID modelId) {
ModelData modelData = mGenericModelDataMap.get(modelId); ModelData modelData = mModelDataMap.get(modelId);
if (modelData == null) { if (modelData == null) {
modelData = ModelData.createGenericModelData(modelId); modelData = ModelData.createGenericModelData(modelId);
mGenericModelDataMap.put(modelId, modelData); mModelDataMap.put(modelId, modelData);
} else if (!modelData.isGenericModel()) {
Slog.e(TAG, "UUID already used for non-generic model.");
return null;
} }
return modelData; return modelData;
} }
private void removeKeyphraseModelLocked(int keyphraseId) {
UUID uuid = mKeyphraseUuidMap.get(keyphraseId);
if (uuid == null) {
return;
}
mModelDataMap.remove(uuid);
mKeyphraseUuidMap.remove(keyphraseId);
}
private ModelData getKeyphraseModelDataLocked(int keyphraseId) {
UUID uuid = mKeyphraseUuidMap.get(keyphraseId);
if (uuid == null) {
return null;
}
return mModelDataMap.get(uuid);
}
// Use this to create a new ModelData entry for a keyphrase Id. It will overwrite existing
// mapping if one exists.
private ModelData createKeyphraseModelDataLocked(UUID modelId, int keyphraseId) {
mKeyphraseUuidMap.remove(keyphraseId);
mModelDataMap.remove(modelId);
mKeyphraseUuidMap.put(keyphraseId, modelId);
ModelData modelData = ModelData.createKeyphraseModelData(modelId);
mModelDataMap.put(modelId, modelData);
return modelData;
}
// Instead of maintaining a second hashmap of modelHandle -> ModelData, we just // Instead of maintaining a second hashmap of modelHandle -> ModelData, we just
// iterate through to find the right object (since we don't expect 100s of models // iterate through to find the right object (since we don't expect 100s of models
// to be stored). // to be stored).
private ModelData getModelDataForLocked(int modelHandle) { private ModelData getModelDataForLocked(int modelHandle) {
// Fetch ModelData object corresponding to the model handle. // Fetch ModelData object corresponding to the model handle.
for (ModelData model : mGenericModelDataMap.values()) { for (ModelData model : mModelDataMap.values()) {
if (model.getHandle() == modelHandle) { if (model.getHandle() == modelHandle) {
return model; return model;
} }
@@ -1051,9 +1070,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
return status; return status;
} }
private void dumpGenericModelStateLocked() { private void dumpModelStateLocked() {
for (UUID modelId : mGenericModelDataMap.keySet()) { for (UUID modelId : mModelDataMap.keySet()) {
ModelData modelData = mGenericModelDataMap.get(modelId); ModelData modelData = mModelDataMap.get(modelId);
Slog.i(TAG, "Model :" + modelData.toString()); Slog.i(TAG, "Model :" + modelData.toString());
} }
} }
@@ -1065,14 +1084,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
mRecognitionRunning = false; mRecognitionRunning = false;
return mRecognitionRunning; return mRecognitionRunning;
} }
if (mKeyphraseModelData != null && mKeyphraseModelData.getCallback() != null && for (ModelData modelData : mModelDataMap.values()) {
mKeyphraseModelData.isModelStarted() &&
mKeyphraseModelData.getHandle() != INVALID_VALUE) {
mRecognitionRunning = true;
return mRecognitionRunning;
}
for (UUID modelId : mGenericModelDataMap.keySet()) {
ModelData modelData = mGenericModelDataMap.get(modelId);
if (modelData.isModelStarted()) { if (modelData.isModelStarted()) {
mRecognitionRunning = true; mRecognitionRunning = true;
return mRecognitionRunning; return mRecognitionRunning;
@@ -1233,6 +1245,10 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
return mModelType == SoundModel.TYPE_KEYPHRASE; return mModelType == SoundModel.TYPE_KEYPHRASE;
} }
synchronized boolean isGenericModel() {
return mModelType == SoundModel.TYPE_GENERIC_SOUND;
}
synchronized String stateToString() { synchronized String stateToString() {
switch(mModelState) { switch(mModelState) {
case MODEL_NOTLOADED: return "NOT_LOADED"; case MODEL_NOTLOADED: return "NOT_LOADED";
@@ -1259,7 +1275,17 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
"ModelState: " + stateToString() + "\n" + "ModelState: " + stateToString() + "\n" +
requestedToString() + "\n" + requestedToString() + "\n" +
callbackToString() + "\n" + callbackToString() + "\n" +
uuidToString(); uuidToString() + "\n" + modelTypeToString();
}
synchronized String modelTypeToString() {
String type = null;
switch (mModelType) {
case SoundModel.TYPE_GENERIC_SOUND: type = "Generic"; break;
case SoundModel.TYPE_UNKNOWN: type = "Unknown"; break;
case SoundModel.TYPE_KEYPHRASE: type = "Keyphrase"; break;
}
return "Model type: " + type + "\n";
} }
} }
} }