From 2d1de78a4c8fc224329a9622c9326e42a42ddadd Mon Sep 17 00:00:00 2001 From: Arunesh Mishra Date: Sun, 21 Feb 2016 18:10:28 -0800 Subject: [PATCH] Unload logic for generic and keyphrase sound models. When delete is called, now the STH unloads the corresponding models. Fix bug with keyphrase where a delete call didn't unload (or stop) the keyphrase model. Bug: 27279380 Change-Id: Ia34f713d2aecef4102c0f0ccc57b8d2e5febe4bb --- .../server/soundtrigger/SoundTriggerHelper.java | 103 +++++++++++++++++++-- .../server/soundtrigger/SoundTriggerInternal.java | 2 + .../server/soundtrigger/SoundTriggerService.java | 10 +- .../VoiceInteractionManagerService.java | 4 + tests/SoundTriggerTestApp/AndroidManifest.xml | 2 + .../soundtrigger/TestSoundTriggerActivity.java | 63 ++++++++++--- 6 files changed, 162 insertions(+), 22 deletions(-) diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java index b4c4bf8b5ef3..7ee7423538a0 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java @@ -199,6 +199,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } modelData.setHandle(handle[0]); modelData.setLoaded(); + Slog.d(TAG, "Generic sound model loaded with handle:" + handle[0]); } modelData.setCallback(callback); modelData.setRecognitionConfig(recognitionConfig); @@ -227,7 +228,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { synchronized (mLock) { if (DBG) { - Slog.d(TAG, "startRecognition for keyphraseId=" + keyphraseId + Slog.d(TAG, "startKeyphraseRecognition for keyphraseId=" + keyphraseId + " soundModel=" + soundModel + ", listener=" + listener.asBinder() + ", recognitionConfig=" + recognitionConfig); Slog.d(TAG, "moduleProperties=" + mModuleProperties); @@ -243,13 +244,13 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } if (mModuleProperties == null) { - Slog.w(TAG, "Attempting startRecognition without the capability"); + Slog.w(TAG, "Attempting startKeyphraseRecognition without the capability"); return STATUS_ERROR; } if (mModule == null) { mModule = SoundTrigger.attachModule(mModuleProperties.id, this, null); if (mModule == null) { - Slog.w(TAG, "startRecognition cannot attach to sound trigger module"); + Slog.w(TAG, "startKeyphraseRecognition cannot attach to sound trigger module"); return STATUS_ERROR; } } @@ -348,26 +349,29 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } if (currentCallback == null || !modelData.isModelStarted()) { - // startRecognition hasn't been called or it failed. - Slog.w(TAG, "Attempting stopRecognition without a successful startRecognition"); + // startGenericRecognition hasn't been called or it failed. + Slog.w(TAG, "Attempting stopGenericRecognition without a successful" + + " startGenericRecognition"); return STATUS_ERROR; } if (currentCallback.asBinder() != listener.asBinder()) { // We don't allow a different listener to stop the recognition than the one // that started it. - Slog.w(TAG, "Attempting stopRecognition for another recognition"); + Slog.w(TAG, "Attempting stopGenericRecognition for another recognition"); return STATUS_ERROR; } - int status = stopGenericRecognitionLocked(modelData, false /* don't notify for synchronous calls */); + int status = stopGenericRecognitionLocked(modelData, + false /* don't notify for synchronous calls */); if (status != SoundTrigger.STATUS_OK) { + Slog.w(TAG, "stopGenericRecognition failed: " + 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. - modelData.clearState(); + modelData.setLoaded(); modelData.clearCallback(); if (!computeRecognitionRunning()) { internalClearGlobalStateLocked(); @@ -471,6 +475,66 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { return mModuleProperties; } + int unloadKeyphraseSoundModel(int keyphraseId) { + if (mModule == null || mCurrentKeyphraseModelHandle == INVALID_VALUE) { + return STATUS_ERROR; + } + if (mKeyphraseId != keyphraseId) { + Slog.w(TAG, "Given sound model is not the one loaded."); + return STATUS_ERROR; + } + + synchronized (mLock) { + // Stop recognition if it's the current one. + mRequested = false; + int status = updateRecognitionLocked(false /* don't notify */); + if (status != SoundTrigger.STATUS_OK) { + Slog.w(TAG, "Stop recognition failed for keyphrase ID:" + status); + } + + status = mModule.unloadSoundModel(mCurrentKeyphraseModelHandle); + if (status != SoundTrigger.STATUS_OK) { + Slog.w(TAG, "unloadKeyphraseSoundModel call failed with " + status); + } + internalClearKeyphraseSoundModelLocked(); + return status; + } + } + + int unloadGenericSoundModel(UUID modelId) { + if (modelId == null || mModule == null) { + return STATUS_ERROR; + } + ModelData modelData = mGenericModelDataMap.get(modelId); + if (modelData == null) { + Slog.w(TAG, "Unload error: Attempting unload invalid generic model with id:" + modelId); + return STATUS_ERROR; + } + synchronized (mLock) { + if (!modelData.isModelLoaded()) { + // Nothing to do here. + Slog.i(TAG, "Unload: Given generic model is not loaded:" + modelId); + return STATUS_OK; + } + if (modelData.isModelStarted()) { + int status = stopGenericRecognitionLocked(modelData, + false /* don't notify for synchronous calls */); + if (status != SoundTrigger.STATUS_OK) { + Slog.w(TAG, "stopGenericRecognition failed: " + status); + } + } + + int status = mModule.unloadSoundModel(modelData.getHandle()); + if (status != SoundTrigger.STATUS_OK) { + Slog.w(TAG, "unloadGenericSoundModel() call failed with " + status); + Slog.w(TAG, "unloadGenericSoundModel() force-marking model as unloaded."); + } + mGenericModelDataMap.remove(modelId); + if (DBG) dumpGenericModelState(); + return status; + } + } + //---- SoundTrigger.StatusListener methods @Override public void onRecognition(RecognitionEvent event) { @@ -913,6 +977,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } } else { + Slog.i(TAG, "startRecognition successful."); modelData.setStarted(); // Notify of resume if needed. if (notify) { @@ -923,6 +988,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } } + if (DBG) dumpGenericModelState(); return status; } @@ -951,9 +1017,17 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } } + if (DBG) dumpGenericModelState(); return status; } + private void dumpGenericModelState() { + for (UUID modelId : mGenericModelDataMap.keySet()) { + ModelData modelData = mGenericModelDataMap.get(modelId); + Slog.i(TAG, "Model :" + modelData.toString()); + } + } + // Computes whether we have any recognition running at all (voice or generic). Sets // the mRecognitionRunning variable with the result. private boolean computeRecognitionRunning() { @@ -1069,5 +1143,18 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { synchronized RecognitionConfig getRecognitionConfig() { return mRecognitionConfig; } + + String stateToString() { + switch(mModelState) { + case MODEL_NOTLOADED: return "NOT_LOADED"; + case MODEL_LOADED: return "LOADED"; + case MODEL_STARTED: return "STARTED"; + } + return "Unknown state"; + } + + public String toString() { + return "Handle: " + mModelHandle + "ModelState: " + stateToString(); + } } } diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java index 772287624f6a..113431f2de7e 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerInternal.java @@ -75,5 +75,7 @@ public abstract class SoundTriggerInternal { public abstract ModuleProperties getModuleProperties(); + public abstract int unloadKeyphraseModel(int keyphaseId); + public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args); } diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java index 251f3146f8cc..a4c1210ba188 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java @@ -122,10 +122,10 @@ public class SoundTriggerService extends SystemService { public int startRecognition(ParcelUuid parcelUuid, IRecognitionStatusCallback callback, RecognitionConfig config) { enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER); + if (!isInitialized()) return STATUS_ERROR; if (DEBUG) { Slog.i(TAG, "startRecognition(): Uuid : " + parcelUuid); } - if (!isInitialized()) return STATUS_ERROR; GenericSoundModel model = getSoundModel(parcelUuid); if (model == null) { @@ -173,6 +173,8 @@ public class SoundTriggerService extends SystemService { if (DEBUG) { Slog.i(TAG, "deleteSoundModel(): id = " + soundModelId); } + // Unload the model if it is loaded. + mSoundTriggerHelper.unloadGenericSoundModel(soundModelId.getUuid()); mDbHelper.deleteGenericSoundModel(soundModelId.getUuid()); } } @@ -216,6 +218,12 @@ public class SoundTriggerService extends SystemService { } @Override + public int unloadKeyphraseModel(int keyphraseId) { + if (!isInitialized()) return STATUS_ERROR; + return mSoundTriggerHelper.unloadKeyphraseSoundModel(keyphraseId); + } + + @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!isInitialized()) return; mSoundTriggerHelper.dump(fd, pw, args); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 4a54643b3cf5..6ab0b998bd6c 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -760,6 +760,10 @@ public class VoiceInteractionManagerService extends SystemService { final long caller = Binder.clearCallingIdentity(); boolean deleted = false; try { + int unloadStatus = mSoundTriggerInternal.unloadKeyphraseModel(keyphraseId); + if (unloadStatus != SoundTriggerInternal.STATUS_OK) { + Slog.w(TAG, "Unable to unload keyphrase sound model:" + unloadStatus); + } deleted = mDbHelper.deleteKeyphraseSoundModel(keyphraseId, callingUid, bcp47Locale); return deleted ? SoundTriggerInternal.STATUS_OK : SoundTriggerInternal.STATUS_ERROR; } finally { diff --git a/tests/SoundTriggerTestApp/AndroidManifest.xml b/tests/SoundTriggerTestApp/AndroidManifest.xml index a72b3ddb7c0c..dc4cdb59b8db 100644 --- a/tests/SoundTriggerTestApp/AndroidManifest.xml +++ b/tests/SoundTriggerTestApp/AndroidManifest.xml @@ -2,10 +2,12 @@ package="com.android.test.soundtrigger"> +