diff options
| author | 2016-04-14 19:59:58 +0000 | |
|---|---|---|
| committer | 2016-04-14 20:00:00 +0000 | |
| commit | 59623ee33742e69c4cb24901e5e81e2aca6496e7 (patch) | |
| tree | 642ec591e3a4a207cb0a3f160085f351422f9861 | |
| parent | c180d68a079ed02999c6015401c6f99c87005855 (diff) | |
| parent | fac2515695b9be78743d243b88f92d7687d24772 (diff) | |
Merge "SoundTriggerHelper: Add ability to manage multiple voice models." into nyc-dev
| -rw-r--r-- | services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java | 338 |
1 files changed, 182 insertions, 156 deletions
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java index f13e019b5683..53377f93507d 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java @@ -84,22 +84,15 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { private final PhoneStateListener mPhoneStateListener; private final PowerManager mPowerManager; - // The SoundTriggerManager layer handles multiple generic recognition models. We store the - // ModelData here in a hashmap. - private final HashMap<UUID, ModelData> mGenericModelDataMap; - - // This ModelData instance ensures that the keyphrase sound model is a singleton and - // all other sound models are of type Generic. Any keyphrase sound model will be stored here - // and any previously running instances will be replaced. This restriction was earlier - // implemented by three instance variables which stored data about the keyphrase - // model. That data now gets encapsulated in this ModelData instance. - 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; + // The SoundTriggerManager layer handles multiple recognition models of type generic and + // keyphrase. We store the ModelData here in a hashmap. + private final HashMap<UUID, ModelData> mModelDataMap; + + // An index of keyphrase sound models so that we can reach them easily. We support indexing + // keyphrase sound models with a keyphrase ID. Sound model with the same keyphrase ID will + // replace an existing model, thus there is a 1:1 mapping from keyphrase ID to a voice + // sound model. + private HashMap<Integer, UUID> mKeyphraseUuidMap; private boolean mCallActive = false; private boolean mIsPowerSaveMode = false; @@ -119,7 +112,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { mContext = context; mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_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(); if (status != SoundTrigger.STATUS_OK || modules.size() == 0) { Slog.w(TAG, "listModules status=" + status + ", # of modules=" + modules.size()); @@ -153,6 +147,10 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { synchronized (mLock) { 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, INVALID_VALUE /* keyphraseId */); } @@ -180,20 +178,48 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { + " soundModel=" + soundModel + ", callback=" + callback.asBinder() + ", recognitionConfig=" + recognitionConfig); Slog.d(TAG, "moduleProperties=" + mModuleProperties); - if (mKeyphraseModelData != null) { - Slog.d(TAG, mKeyphraseModelData.toString()); - } else { - Slog.d(TAG, "Null KeyphraseModelData."); + dumpModelStateLocked(); + } + + 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); } } + 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 * generic sound models. @@ -228,17 +254,15 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { initializeTelephonyAndPowerStateListeners(); } - // If the previous model is different (for the same UUID), ensure that its unloaded - // and stopped before proceeding. This works for both keyphrase and generic models. - // Specifically for keyphrase since we have 'mKeyphraseModelData' holding a single - // allowed instance of such a model, this ensures that a previously loaded (or started) - // keyphrase model is appropriately stopped. This ensures no regression with the - // previous version of this code as 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 the existing SoundModel is different (for the same UUID for Generic and same + // keyphrase ID for voice), ensure that it is unloaded and stopped before proceeding. + // This works for both keyphrase and generic models. This logic also ensures that a + // previously loaded (or started) model is appropriately stopped. Since this is a + // generalization of the previous logic with a single keyphrase model, we should have + // no regression with the previous version of this code as was given in the + // startKeyphrase() routine. 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; if (modelData.getSoundModel().equals(soundModel) && modelData.isModelStarted()) { // The model has not changed, but the previous model is "started". @@ -273,7 +297,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { modelData.clearCallback(); } - // Load the model if its not loaded. + // Load the model if it is not loaded. if (!modelData.isModelLoaded()) { // Load the model 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]); } modelData.setCallback(callback); - if (modelData.isKeyphraseModel()) { - mKeyphraseId = keyphraseId; - } modelData.setRequested(true); modelData.setRecognitionConfig(recognitionConfig); modelData.setSoundModel(soundModel); @@ -322,8 +343,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { return STATUS_ERROR; } - ModelData modelData = mGenericModelDataMap.get(modelId); - if (modelData == null) { + ModelData modelData = mModelDataMap.get(modelId); + if (modelData == null || !modelData.isGenericModel()) { Slog.w(TAG, "Attempting stopRecognition on invalid model with id:" + modelId); return STATUS_ERROR; } @@ -355,21 +376,23 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { 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) { Slog.d(TAG, "stopRecognition for keyphraseId=" + keyphraseId + ", callback =" + callback.asBinder()); - Slog.d(TAG, "current callback=" + (mKeyphraseModelData == null ? "null" : - mKeyphraseModelData.getCallback().asBinder())); + Slog.d(TAG, "current callback=" + (modelData == null ? "null" : + modelData.getCallback().asBinder())); } - int status = stopRecognition(mKeyphraseModelData, callback); + int status = stopRecognition(modelData, callback); if (status != SoundTrigger.STATUS_OK) { 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; } } @@ -424,9 +447,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { internalClearGlobalStateLocked(); } - if (modelData.isKeyphraseModel()) { - mKeyphraseId = INVALID_VALUE; - } return status; } } @@ -475,25 +495,18 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { return; } - // Stop Keyphrase recognition if one exists. - if (mKeyphraseModelData != null && mKeyphraseModelData.getHandle() != INVALID_VALUE) { - 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()) { + // Stop all recognition models. + for (ModelData model : mModelDataMap.values()) { if (model.isModelStarted()) { + model.setRequested(false); int status = stopRecognitionLocked(model, false /* do not notify for synchronous calls */); if (status != STATUS_OK) { - // What else can we do if there is an error here. - Slog.w(TAG, "Error stopping generic model: " + model.getHandle()); + Slog.w(TAG, "Error stopping keyphrase model: " + model.getHandle()); } model.clearState(); model.clearCallback(); + model.setRecognitionConfig(null); } } internalClearGlobalStateLocked(); @@ -507,24 +520,27 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { int unloadKeyphraseSoundModel(int keyphraseId) { synchronized (mLock) { MetricsLogger.count(mContext, "sth_unload_keyphrase_sound_model", 1); - if (mModule == null || mKeyphraseModelData == null || - mKeyphraseModelData.getHandle() == INVALID_VALUE) { + ModelData modelData = getKeyphraseModelDataLocked(keyphraseId); + if (mModule == null || modelData == null || modelData.getHandle() == INVALID_VALUE || + !modelData.isKeyphraseModel()) { return STATUS_ERROR; } // Stop recognition if it's the current one. - mKeyphraseModelData.setRequested(false); - int status = updateRecognitionLocked(mKeyphraseModelData, isRecognitionAllowed(), + modelData.setRequested(false); + int status = updateRecognitionLocked(modelData, isRecognitionAllowed(), false /* don't notify */); if (status != SoundTrigger.STATUS_OK) { 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) { Slog.w(TAG, "unloadKeyphraseSoundModel call failed with " + status); } - mKeyphraseModelData.clearState(); + + // Remove it from existence. + removeKeyphraseModelLocked(keyphraseId); return status; } } @@ -535,8 +551,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { if (modelId == null || mModule == null) { return STATUS_ERROR; } - ModelData modelData = mGenericModelDataMap.get(modelId); - if (modelData == null) { + ModelData modelData = mModelDataMap.get(modelId); + if (modelData == null || !modelData.isGenericModel()) { Slog.w(TAG, "Unload error: Attempting unload invalid generic model with id:" + modelId); 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() force-marking model as unloaded."); } - mGenericModelDataMap.remove(modelId); - if (DBG) dumpGenericModelStateLocked(); + + // Remove it from existence. + mModelDataMap.remove(modelId); + if (DBG) dumpModelStateLocked(); return status; } } @@ -612,7 +630,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { return; } 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: " + event.soundModelHandle); return; @@ -723,67 +741,64 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } catch (RemoteException e) { Slog.w(TAG, "RemoteException in onError", e); } finally { - internalClearKeyphraseStateLocked(); - internalClearGenericModelStateLocked(); + internalClearModelStateLocked(); internalClearGlobalStateLocked(); } } - private void onKeyphraseRecognitionSuccessLocked(KeyphraseRecognitionEvent event) { - Slog.i(TAG, "Recognition success"); - MetricsLogger.count(mContext, "sth_keyphrase_recognition_event", 1); - - if (mKeyphraseModelData == null) { - Slog.e(TAG, "Received onRecognition event for null keyphrase model data."); - return; - } - - if (mKeyphraseModelData.getCallback() == null) { - Slog.w(TAG, "Received onRecognition event without any listener for it."); - return; + 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; + return INVALID_VALUE; } // TODO: Handle more than one keyphrase extras. - if (mKeyphraseId != keyphraseExtras[0].id) { - Slog.w(TAG, "received onRecognition event for a different keyphrase"); + return keyphraseExtras[0].id; + } + + private void onKeyphraseRecognitionSuccessLocked(KeyphraseRecognitionEvent event) { + Slog.i(TAG, "Recognition success"); + MetricsLogger.count(mContext, "sth_keyphrase_recognition_event", 1); + int keyphraseId = getKeyphraseIdFromEvent(event); + ModelData modelData = getKeyphraseModelDataLocked(keyphraseId); + + if (modelData == null || !modelData.isKeyphraseModel()) { + Slog.e(TAG, "Keyphase model data does not exist for ID:" + keyphraseId); + return; + } + + if (modelData.getCallback() == null) { + Slog.w(TAG, "Received onRecognition event without callback for keyphrase model."); return; } try { - mKeyphraseModelData.getCallback().onKeyphraseDetected( - (KeyphraseRecognitionEvent) event); + modelData.getCallback().onKeyphraseDetected((KeyphraseRecognitionEvent) event); } catch (RemoteException e) { Slog.w(TAG, "RemoteException in onKeyphraseDetected", e); } - mKeyphraseModelData.setStopped(); + modelData.setStopped(); - RecognitionConfig config = mKeyphraseModelData.getRecognitionConfig(); + RecognitionConfig config = modelData.getRecognitionConfig(); if (config != null) { // 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. - if (mKeyphraseModelData.getRequested()) { - updateRecognitionLocked(mKeyphraseModelData, isRecognitionAllowed(), - true /* notify */); + if (modelData.getRequested()) { + updateRecognitionLocked(modelData, isRecognitionAllowed(), true /* notify */); } } private void updateAllRecognitionsLocked(boolean notify) { boolean isAllowed = isRecognitionAllowed(); - // Keyphrase model. - if (mKeyphraseModelData != null) { - updateRecognitionLocked(mKeyphraseModelData, isAllowed, notify); - } - for (UUID modelId : mGenericModelDataMap.keySet()) { - ModelData modelData = mGenericModelDataMap.get(modelId); + for (ModelData modelData : mModelDataMap.values()) { updateRecognitionLocked(modelData, isAllowed, notify); } } @@ -809,11 +824,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } catch (RemoteException e) { Slog.w(TAG, "RemoteException in onError", e); } finally { - if (mKeyphraseModelData != null) { - mKeyphraseModelData.clearState(); - } - internalClearKeyphraseStateLocked(); - internalClearGenericModelStateLocked(); + internalClearModelStateLocked(); internalClearGlobalStateLocked(); if (mModule != null) { mModule.detach(); @@ -822,10 +833,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } - // internalClearGlobalStateLocked() gets split into two routines. Cleanup that is - // 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. + // internalClearGlobalStateLocked() cleans up the telephony and power save listeners. private void internalClearGlobalStateLocked() { // Unregister from call state changes. mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); @@ -837,20 +845,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } - private void internalClearKeyphraseStateLocked() { - if (mKeyphraseModelData != null) { - mKeyphraseModelData.setStopped(); - 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); + // Clears state for all models (generic and keyphrase). + private void internalClearModelStateLocked() { + for (ModelData modelData : mModelDataMap.values()) { modelData.clearState(); modelData.clearCallback(); } @@ -884,14 +881,10 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { synchronized (mLock) { pw.print(" module properties="); pw.println(mModuleProperties == null ? "null" : mModuleProperties); - pw.print(" keyphrase ID="); pw.println(mKeyphraseId); pw.print(" call active="); pw.println(mCallActive); pw.print(" power save mode active="); pw.println(mIsPowerSaveMode); 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. private void sendErrorCallbacksToAll(int errorCode) throws RemoteException { - IRecognitionStatusCallback keyphraseListener = mKeyphraseModelData.getCallback(); - if (keyphraseListener != null) { - keyphraseListener.onError(STATUS_ERROR); - } - for (UUID modelId: mGenericModelDataMap.keySet()) { - ModelData modelData = mGenericModelDataMap.get(modelId); - IRecognitionStatusCallback keyphraseCallback = mKeyphraseModelData.getCallback(); - if (keyphraseCallback != null) { - keyphraseCallback.onError(STATUS_ERROR); + for (ModelData modelData : mModelDataMap.values()) { + IRecognitionStatusCallback callback = modelData.getCallback(); + if (callback != null) { + callback.onError(STATUS_ERROR); } } } private ModelData getOrCreateGenericModelDataLocked(UUID modelId) { - ModelData modelData = mGenericModelDataMap.get(modelId); + ModelData modelData = mModelDataMap.get(modelId); if (modelData == null) { 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; } + 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 // iterate through to find the right object (since we don't expect 100s of models // to be stored). private ModelData getModelDataForLocked(int modelHandle) { // Fetch ModelData object corresponding to the model handle. - for (ModelData model : mGenericModelDataMap.values()) { + for (ModelData model : mModelDataMap.values()) { if (model.getHandle() == modelHandle) { return model; } @@ -1051,9 +1070,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { return status; } - private void dumpGenericModelStateLocked() { - for (UUID modelId : mGenericModelDataMap.keySet()) { - ModelData modelData = mGenericModelDataMap.get(modelId); + private void dumpModelStateLocked() { + for (UUID modelId : mModelDataMap.keySet()) { + ModelData modelData = mModelDataMap.get(modelId); Slog.i(TAG, "Model :" + modelData.toString()); } } @@ -1065,14 +1084,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { mRecognitionRunning = false; return mRecognitionRunning; } - if (mKeyphraseModelData != null && mKeyphraseModelData.getCallback() != null && - mKeyphraseModelData.isModelStarted() && - mKeyphraseModelData.getHandle() != INVALID_VALUE) { - mRecognitionRunning = true; - return mRecognitionRunning; - } - for (UUID modelId : mGenericModelDataMap.keySet()) { - ModelData modelData = mGenericModelDataMap.get(modelId); + for (ModelData modelData : mModelDataMap.values()) { if (modelData.isModelStarted()) { mRecognitionRunning = true; return mRecognitionRunning; @@ -1233,6 +1245,10 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { return mModelType == SoundModel.TYPE_KEYPHRASE; } + synchronized boolean isGenericModel() { + return mModelType == SoundModel.TYPE_GENERIC_SOUND; + } + synchronized String stateToString() { switch(mModelState) { case MODEL_NOTLOADED: return "NOT_LOADED"; @@ -1259,7 +1275,17 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { "ModelState: " + stateToString() + "\n" + requestedToString() + "\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"; } } } |