diff options
author | Sandeep Siddhartha <sansid@google.com> | 2014-07-30 19:02:53 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2014-07-24 15:36:21 +0000 |
commit | 7f60d881b3b757106f9fe19720f42a497956299c (patch) | |
tree | 2c9160ee726b92f48d55adc10ae2ce4282ff0d52 | |
parent | 73915cf2ca01d4e822249641c4b73f512da93e10 (diff) | |
parent | 1ed12ddb8c46193cc4d790b9c7d6a5d61afb3311 (diff) | |
download | frameworks_base-7f60d881b3b757106f9fe19720f42a497956299c.zip frameworks_base-7f60d881b3b757106f9fe19720f42a497956299c.tar.gz frameworks_base-7f60d881b3b757106f9fe19720f42a497956299c.tar.bz2 |
Merge "Make startRecognition async" into lmp-dev
5 files changed, 186 insertions, 79 deletions
diff --git a/api/current.txt b/api/current.txt index 0a4e036..e43fae4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -27467,8 +27467,8 @@ package android.service.voice { public class AlwaysOnHotwordDetector { method public android.content.Intent getManageIntent(int); method public int getSupportedRecognitionModes(); - method public int startRecognition(int); - method public int stopRecognition(); + method public void startRecognition(int); + method public void stopRecognition(); field public static final int MANAGE_ACTION_ENROLL = 0; // 0x0 field public static final int MANAGE_ACTION_RE_ENROLL = 1; // 0x1 field public static final int MANAGE_ACTION_UN_ENROLL = 2; // 0x2 @@ -27481,14 +27481,14 @@ package android.service.voice { field public static final int STATE_KEYPHRASE_ENROLLED = 2; // 0x2 field public static final int STATE_KEYPHRASE_UNENROLLED = 1; // 0x1 field public static final int STATE_KEYPHRASE_UNSUPPORTED = -1; // 0xffffffff - field public static final int STATUS_ERROR = -2147483648; // 0x80000000 - field public static final int STATUS_OK = 0; // 0x0 } public static abstract interface AlwaysOnHotwordDetector.Callback { method public abstract void onAvailabilityChanged(int); method public abstract void onDetected(byte[]); + method public abstract void onDetectionStarted(); method public abstract void onDetectionStopped(); + method public abstract void onError(); } public class VoiceInteractionService extends android.app.Service { diff --git a/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl b/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl index b1a02c1..f279668 100644 --- a/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl +++ b/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl @@ -29,7 +29,9 @@ oneway interface IRecognitionStatusCallback { */ void onDetected(in SoundTrigger.KeyphraseRecognitionEvent recognitionEvent); /** - * Called when the detection for the associated keyphrase stops. + * Called when the detection fails due to an error. + * + * @param status The error code that was seen. */ - void onDetectionStopped(); + void onError(int status); }
\ No newline at end of file diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index e40ece3..3f53ad4 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -22,13 +22,11 @@ import android.hardware.soundtrigger.KeyphraseEnrollmentInfo; import android.hardware.soundtrigger.KeyphraseMetadata; import android.hardware.soundtrigger.SoundTrigger; import android.hardware.soundtrigger.SoundTrigger.ConfidenceLevel; -import android.hardware.soundtrigger.SoundTrigger.Keyphrase; import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionEvent; import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra; import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel; import android.hardware.soundtrigger.SoundTrigger.ModuleProperties; import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig; -import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent; import android.os.AsyncTask; import android.os.Handler; import android.os.Message; @@ -37,8 +35,6 @@ import android.util.Slog; import com.android.internal.app.IVoiceInteractionManagerService; -import java.util.List; - /** * A class that lets a VoiceInteractionService implementation interact with * always-on keyphrase detection APIs. @@ -82,12 +78,6 @@ public class AlwaysOnHotwordDetector { /** Indicates that we need to un-enroll. */ public static final int MANAGE_ACTION_UN_ENROLL = 2; - /** - * Return codes for {@link #startRecognition(int)}, {@link #stopRecognition()} - */ - public static final int STATUS_ERROR = SoundTrigger.STATUS_ERROR; - public static final int STATUS_OK = SoundTrigger.STATUS_OK; - //-- Flags for startRecogntion ----// /** Empty flag for {@link #startRecognition(int)}. */ public static final int RECOGNITION_FLAG_NONE = 0; @@ -115,9 +105,19 @@ public class AlwaysOnHotwordDetector { // TODO: Set to false. static final boolean DBG = true; + private static final int STATUS_ERROR = SoundTrigger.STATUS_ERROR; + private static final int STATUS_OK = SoundTrigger.STATUS_OK; + private static final int MSG_STATE_CHANGED = 1; private static final int MSG_HOTWORD_DETECTED = 2; - private static final int MSG_DETECTION_STOPPED = 3; + private static final int MSG_DETECTION_STARTED = 3; + private static final int MSG_DETECTION_STOPPED = 4; + private static final int MSG_DETECTION_ERROR = 5; + + private static final int FLAG_REQUESTED = 0x1; + private static final int FLAG_STARTED = 0x2; + private static final int FLAG_CALL_ACTIVE = 0x4; + private static final int FLAG_MICROPHONE_OPEN = 0x8; private final String mText; private final String mLocale; @@ -135,6 +135,8 @@ public class AlwaysOnHotwordDetector { private final Handler mHandler; private int mAvailability = STATE_NOT_READY; + private int mInternalState = 0; + private int mRecognitionFlags = RECOGNITION_FLAG_NONE; /** * Callbacks for always-on hotword detection. @@ -161,15 +163,30 @@ public class AlwaysOnHotwordDetector { void onAvailabilityChanged(int status); /** * Called when the keyphrase is spoken. + * This implicitly stops listening for the keyphrase once it's detected. + * Clients should start a recognition again once they are done handling this + * detection. * * @param data Optional trigger audio data, if it was requested during * {@link AlwaysOnHotwordDetector#startRecognition(int)}. */ void onDetected(byte[] data); /** + * Called when the detection for the associated keyphrase starts. + * This is called as a result of a successful call to + * {@link AlwaysOnHotwordDetector#startRecognition(int)}. + */ + void onDetectionStarted(); + /** * Called when the detection for the associated keyphrase stops. + * This is called as a result of a successful call to + * {@link AlwaysOnHotwordDetector#stopRecognition()}. */ void onDetectionStopped(); + /** + * Called when the detection fails due to an error. + */ + void onError(); } /** @@ -227,78 +244,43 @@ public class AlwaysOnHotwordDetector { * @param recognitionFlags The flags to control the recognition properties. * The allowed flags are {@link #RECOGNITION_FLAG_NONE} and * {@link #RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO}. - * @return {@link #STATUS_OK} if the call succeeds, an error code otherwise. * @throws UnsupportedOperationException if the recognition isn't supported. * Callers should only call this method after a supported state callback on * {@link Callback#onAvailabilityChanged(int)} to avoid this exception. */ - public int startRecognition(int recognitionFlags) { + public void startRecognition(int recognitionFlags) { synchronized (mLock) { - return startRecognitionLocked(recognitionFlags); - } - } + // Check if we can start/stop a recognition. + if (mAvailability != STATE_KEYPHRASE_ENROLLED) { + throw new UnsupportedOperationException( + "Recognition for the given keyphrase is not supported"); + } - private int startRecognitionLocked(int recognitionFlags) { - // This method only makes sense if we can start a recognition. - if (mAvailability != STATE_KEYPHRASE_ENROLLED) { - throw new UnsupportedOperationException( - "Recognition for the given keyphrase is not supported"); + mInternalState |= FLAG_REQUESTED; + mRecognitionFlags = recognitionFlags; + updateRecognitionLocked(); } - - KeyphraseRecognitionExtra[] recognitionExtra = new KeyphraseRecognitionExtra[1]; - // TODO: Do we need to do something about the confidence level here? - recognitionExtra[0] = new KeyphraseRecognitionExtra(mKeyphraseMetadata.id, - mKeyphraseMetadata.recognitionModeFlags, new ConfidenceLevel[0]); - boolean captureTriggerAudio = - (recognitionFlags & RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO) != 0; - int code = STATUS_ERROR; - try { - code = mModelManagementService.startRecognition(mVoiceInteractionService, - mKeyphraseMetadata.id, mInternalCallback, - new RecognitionConfig( - captureTriggerAudio, recognitionExtra, null /* additional data */)); - } catch (RemoteException e) { - Slog.w(TAG, "RemoteException in startRecognition!"); - } - if (code != STATUS_OK) { - Slog.w(TAG, "startRecognition() failed with error code " + code); - } - return code; } /** * Stops recognition for the associated keyphrase. * - * @return {@link #STATUS_OK} if the call succeeds, an error code otherwise. * @throws UnsupportedOperationException if the recognition isn't supported. * Callers should only call this method after a supported state callback on * {@link Callback#onAvailabilityChanged(int)} to avoid this exception. */ - public int stopRecognition() { + public void stopRecognition() { synchronized (mLock) { - return stopRecognitionLocked(); - } - } - - private int stopRecognitionLocked() { - // This method only makes sense if we can start a recognition. - if (mAvailability != STATE_KEYPHRASE_ENROLLED) { - throw new UnsupportedOperationException( - "Recognition for the given keyphrase is not supported"); - } - - int code = STATUS_ERROR; - try { - code = mModelManagementService.stopRecognition( - mVoiceInteractionService, mKeyphraseMetadata.id, mInternalCallback); - } catch (RemoteException e) { - Slog.w(TAG, "RemoteException in stopRecognition!"); - } + // Check if we can start/stop a recognition. + if (mAvailability != STATE_KEYPHRASE_ENROLLED) { + throw new UnsupportedOperationException( + "Recognition for the given keyphrase is not supported"); + } - if (code != STATUS_OK) { - Slog.w(TAG, "stopRecognition() failed with error code " + code); + mInternalState &= ~FLAG_REQUESTED; + mRecognitionFlags = RECOGNITION_FLAG_NONE; + updateRecognitionLocked(); } - return code; } /** @@ -362,6 +344,113 @@ public class AlwaysOnHotwordDetector { } } + @SuppressWarnings("unused") + private void onCallStateChanged(boolean active) { + synchronized (mLock) { + if (active) { + mInternalState |= FLAG_CALL_ACTIVE; + } else { + mInternalState &= ~FLAG_CALL_ACTIVE; + } + + updateRecognitionLocked(); + } + } + + @SuppressWarnings("unused") + private void onMicrophoneStateChanged(boolean open) { + synchronized (mLock) { + if (open) { + mInternalState |= FLAG_MICROPHONE_OPEN; + } else { + mInternalState &= ~FLAG_MICROPHONE_OPEN; + } + + updateRecognitionLocked(); + } + } + + private void updateRecognitionLocked() { + // Don't attempt to update the recognition state if keyphrase isn't enrolled. + if (mAvailability != STATE_KEYPHRASE_ENROLLED) { + return; + } + + // Start recognition if requested and not in a call/reading from the microphone + boolean start = (mInternalState&FLAG_REQUESTED) != 0 + && (mInternalState&FLAG_CALL_ACTIVE) == 0 + && (mInternalState&FLAG_MICROPHONE_OPEN) == 0; + boolean requested = (mInternalState&FLAG_REQUESTED) != 0; + + if (start && (mInternalState&FLAG_STARTED) == 0) { + // Start recognition. + if (DBG) Slog.d(TAG, "starting recognition..."); + int status = startRecognitionLocked(); + if (status == STATUS_OK) { + mInternalState |= FLAG_STARTED; + mHandler.sendEmptyMessage(MSG_DETECTION_STARTED); + } else { + if (DBG) Slog.d(TAG, "failed to start recognition: " + status); + mHandler.sendEmptyMessage(MSG_DETECTION_ERROR); + } + // Post the callback + return; + } + + if (!start && (mInternalState&FLAG_STARTED) != 0) { + // Stop recognition + // Only notify the callback if a recognition was *not* requested. + // For internal stoppages, don't notify the callback. + if (DBG) Slog.d(TAG, "stopping recognition..."); + int status = stopRecognitionLocked(); + if (status == STATUS_OK) { + mInternalState &= ~FLAG_STARTED; + if (!requested) mHandler.sendEmptyMessage(MSG_DETECTION_STOPPED); + } else { + if (!requested) mHandler.sendEmptyMessage(MSG_DETECTION_ERROR); + if (DBG) Slog.d(TAG, "failed to stop recognition: " + status); + } + return; + } + } + + private int startRecognitionLocked() { + KeyphraseRecognitionExtra[] recognitionExtra = new KeyphraseRecognitionExtra[1]; + // TODO: Do we need to do something about the confidence level here? + recognitionExtra[0] = new KeyphraseRecognitionExtra(mKeyphraseMetadata.id, + mKeyphraseMetadata.recognitionModeFlags, new ConfidenceLevel[0]); + boolean captureTriggerAudio = + (mRecognitionFlags&RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO) != 0; + int code = STATUS_ERROR; + try { + code = mModelManagementService.startRecognition(mVoiceInteractionService, + mKeyphraseMetadata.id, mInternalCallback, + new RecognitionConfig( + captureTriggerAudio, recognitionExtra, null /* additional data */)); + } catch (RemoteException e) { + Slog.w(TAG, "RemoteException in startRecognition!"); + } + if (code != STATUS_OK) { + Slog.w(TAG, "startRecognition() failed with error code " + code); + } + return code; + } + + private int stopRecognitionLocked() { + int code = STATUS_ERROR; + try { + code = mModelManagementService.stopRecognition( + mVoiceInteractionService, mKeyphraseMetadata.id, mInternalCallback); + } catch (RemoteException e) { + Slog.w(TAG, "RemoteException in stopRecognition!"); + } + + if (code != STATUS_OK) { + Slog.w(TAG, "stopRecognition() failed with error code " + code); + } + return code; + } + private void notifyStateChangedLocked() { Message message = Message.obtain(mHandler, MSG_STATE_CHANGED); message.arg1 = mAvailability; @@ -385,9 +474,9 @@ public class AlwaysOnHotwordDetector { } @Override - public void onDetectionStopped() { - Slog.i(TAG, "onDetectionStopped"); - mHandler.sendEmptyMessage(MSG_DETECTION_STOPPED); + public void onError(int status) { + Slog.i(TAG, "onError: " + status); + mHandler.sendEmptyMessage(MSG_DETECTION_ERROR); } } @@ -401,8 +490,15 @@ public class AlwaysOnHotwordDetector { case MSG_HOTWORD_DETECTED: mExternalCallback.onDetected((byte[]) msg.obj); break; + case MSG_DETECTION_STARTED: + mExternalCallback.onDetectionStarted(); + break; case MSG_DETECTION_STOPPED: mExternalCallback.onDetectionStopped(); + break; + case MSG_DETECTION_ERROR: + mExternalCallback.onError(); + break; default: super.handleMessage(msg); } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java index fe1b92a..15ec629 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java @@ -129,7 +129,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { if (oldListener != null && oldListener.asBinder() != listener.asBinder()) { Slog.w(TAG, "Canceling previous recognition"); try { - oldListener.onDetectionStopped(); + oldListener.onError(STATUS_ERROR); } catch (RemoteException e) { Slog.w(TAG, "RemoteException in onDetectionStopped"); } @@ -235,7 +235,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { try { synchronized (this) { for (int i = 0; i < mActiveListeners.size(); i++) { - mActiveListeners.valueAt(i).onDetectionStopped(); + mActiveListeners.valueAt(i).onError(STATUS_ERROR); } } } catch (RemoteException e) { @@ -279,7 +279,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { synchronized (this) { try { for (int i = 0; i < mActiveListeners.size(); i++) { - mActiveListeners.valueAt(i).onDetectionStopped(); + mActiveListeners.valueAt(i).onError(SoundTrigger.STATUS_DEAD_OBJECT); } } catch (RemoteException e) { Slog.w(TAG, "RemoteException in onDetectionStopped"); diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java index cc710f9..49c3d0a 100644 --- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java +++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java @@ -41,9 +41,19 @@ public class MainInteractionService extends VoiceInteractionService { } @Override + public void onDetectionStarted() { + Log.i(TAG, "onDetectionStarted"); + } + + @Override public void onDetectionStopped() { Log.i(TAG, "onDetectionStopped"); } + + @Override + public void onError() { + Log.i(TAG, "onError"); + } }; private AlwaysOnHotwordDetector mHotwordDetector; @@ -89,10 +99,9 @@ public class MainInteractionService extends VoiceInteractionService { Log.i(TAG, "Need to enroll with " + enroll); break; case AlwaysOnHotwordDetector.STATE_KEYPHRASE_ENROLLED: - Log.i(TAG, "STATE_KEYPHRASE_ENROLLED"); - int status = mHotwordDetector.startRecognition( + Log.i(TAG, "STATE_KEYPHRASE_ENROLLED - starting recognition"); + mHotwordDetector.startRecognition( AlwaysOnHotwordDetector.RECOGNITION_FLAG_NONE); - Log.i(TAG, "startRecognition status = " + status); break; } } |