summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSandeep Siddhartha <sansid@google.com>2014-07-30 19:02:53 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2014-07-24 15:36:21 +0000
commit7f60d881b3b757106f9fe19720f42a497956299c (patch)
tree2c9160ee726b92f48d55adc10ae2ce4282ff0d52
parent73915cf2ca01d4e822249641c4b73f512da93e10 (diff)
parent1ed12ddb8c46193cc4d790b9c7d6a5d61afb3311 (diff)
downloadframeworks_base-7f60d881b3b757106f9fe19720f42a497956299c.zip
frameworks_base-7f60d881b3b757106f9fe19720f42a497956299c.tar.gz
frameworks_base-7f60d881b3b757106f9fe19720f42a497956299c.tar.bz2
Merge "Make startRecognition async" into lmp-dev
-rw-r--r--api/current.txt8
-rw-r--r--core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl6
-rw-r--r--core/java/android/service/voice/AlwaysOnHotwordDetector.java230
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java6
-rw-r--r--tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java15
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;
}
}