summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorSandeep Siddhartha <sansid@google.com>2014-07-20 12:22:56 -0700
committerSandeep Siddhartha <sansid@google.com>2014-07-20 16:53:09 -0700
commit110f569b47bc21fb38ec25b6110ee302ce137e06 (patch)
treeb596841dbf21aaf5d23e1d905cf2914f5f6d53e4 /core
parent8ca1fdc10b9afb831636ce00e48b9692476413c5 (diff)
downloadframeworks_base-110f569b47bc21fb38ec25b6110ee302ce137e06.zip
frameworks_base-110f569b47bc21fb38ec25b6110ee302ce137e06.tar.gz
frameworks_base-110f569b47bc21fb38ec25b6110ee302ce137e06.tar.bz2
Fix synchronization issues in AlwaysOnHotwordDetector
- Remove unnecessary recognition status from AlwaysOnHotwordDetector - Remove unnecessary recognition started callback from IRecognitionStatusCallback - Fix a bug around the fact that we weren't picking up enrollment at runtime because we were storing the availability at instantiation time. - Handle 0-length arrays in SoundTrigger classes while parceling/unparceling - Fix issue in SoundTrigger helper where we were not comparing binders for start/stop calls - Unload the previous model when starting a new recognition - Add more debug logging Change-Id: Icc56d7f3dd1ffa49a8cfeea49080e3ab4d342c32
Diffstat (limited to 'core')
-rw-r--r--core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl4
-rw-r--r--core/java/android/hardware/soundtrigger/SoundTrigger.java24
-rw-r--r--core/java/android/service/voice/AlwaysOnHotwordDetector.java129
3 files changed, 84 insertions, 73 deletions
diff --git a/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl b/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl
index 5738909..038d7ef 100644
--- a/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl
+++ b/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl
@@ -29,10 +29,6 @@ oneway interface IRecognitionStatusCallback {
*/
void onDetected(in byte[] data);
/**
- * Called when the detection for the associated keyphrase starts.
- */
- void onDetectionStarted();
- /**
* Called when the detection for the associated keyphrase stops.
*/
void onDetectionStopped();
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 7b0a678..9a5cd9b 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -247,7 +247,7 @@ public class SoundTrigger {
String text = in.readString();
int[] users = null;
int numUsers = in.readInt();
- if (numUsers > 0) {
+ if (numUsers >= 0) {
users = new int[numUsers];
in.readIntArray(users);
}
@@ -264,7 +264,7 @@ public class SoundTrigger {
dest.writeInt(users.length);
dest.writeIntArray(users);
} else {
- dest.writeInt(0);
+ dest.writeInt(-1);
}
}
@@ -349,7 +349,7 @@ public class SoundTrigger {
UUID uuid = UUID.fromString(in.readString());
byte[] data = null;
int dataLength = in.readInt();
- if (dataLength > 0) {
+ if (dataLength >= 0) {
data = new byte[dataLength];
in.readByteArray(data);
}
@@ -369,10 +369,16 @@ public class SoundTrigger {
dest.writeInt(data.length);
dest.writeByteArray(data);
} else {
- dest.writeInt(0);
+ dest.writeInt(-1);
}
dest.writeTypedArray(keyphrases, 0);
}
+
+ @Override
+ public String toString() {
+ return "KeyphraseSoundModel [keyphrases=" + Arrays.toString(keyphrases) + ", uuid="
+ + uuid + ", type=" + type + ", data? " + (data != null) + "]";
+ }
}
/**
@@ -471,7 +477,7 @@ public class SoundTrigger {
in.createTypedArray(KeyphraseRecognitionExtra.CREATOR);
byte[] data = null;
int dataLength = in.readInt();
- if (dataLength > 0) {
+ if (dataLength >= 0) {
data = new byte[dataLength];
in.readByteArray(data);
}
@@ -486,7 +492,7 @@ public class SoundTrigger {
dest.writeInt(data.length);
dest.writeByteArray(data);
} else {
- dest.writeInt(0);
+ dest.writeInt(-1);
}
}
@@ -494,6 +500,12 @@ public class SoundTrigger {
public int describeContents() {
return 0;
}
+
+ @Override
+ public String toString() {
+ return "RecognitionConfig [captureRequested=" + captureRequested + ", keyphrases="
+ + Arrays.toString(keyphrases) + ", data? " + (data != null) + "]";
+ }
}
/**
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index c4ed8c5..ad30f44 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -27,6 +27,8 @@ 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.os.Handler;
+import android.os.Message;
import android.os.RemoteException;
import android.util.Slog;
@@ -72,18 +74,6 @@ public class AlwaysOnHotwordDetector {
public static final int STATUS_ERROR = SoundTrigger.STATUS_ERROR;
public static final int STATUS_OK = SoundTrigger.STATUS_OK;
- //---- Keyphrase recognition status ----//
- /** Indicates that recognition is not available. */
- public static final int RECOGNITION_STATUS_NOT_AVAILABLE = 0x01;
- /** Indicates that recognition has not been requested. */
- public static final int RECOGNITION_STATUS_NOT_REQUESTED = 0x02;
- /** Indicates that recognition has been requested. */
- public static final int RECOGNITION_STATUS_REQUESTED = 0x04;
- /** Indicates that recognition has been temporarily disabled. */
- public static final int RECOGNITION_STATUS_DISABLED_TEMPORARILY = 0x08;
- /** Indicates that recognition is currently active . */
- public static final int RECOGNITION_STATUS_ACTIVE = 0x10;
-
//-- Flags for startRecogntion ----//
/** Empty flag for {@link #startRecognition(int)}. */
public static final int RECOGNITION_FLAG_NONE = 0;
@@ -96,15 +86,22 @@ public class AlwaysOnHotwordDetector {
//---- Recognition mode flags ----//
// Must be kept in sync with the related attribute defined as searchKeyphraseRecognitionFlags.
- /** Simple recognition of the key phrase. Returned by {@link #getRecognitionStatus()} */
+ /**
+ * Simple recognition of the key phrase. Returned by {@link #getSupportedRecognitionModes()}
+ */
public static final int RECOGNITION_MODE_VOICE_TRIGGER
= SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER;
- /** Trigger only if one user is identified. Returned by {@link #getRecognitionStatus()} */
+ /**
+ * Trigger only if one user is identified. Returned by {@link #getSupportedRecognitionModes()}
+ */
public static final int RECOGNITION_MODE_USER_IDENTIFICATION
= SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION;
static final String TAG = "AlwaysOnHotwordDetector";
+ private static final int MSG_HOTWORD_DETECTED = 1;
+ private static final int MSG_DETECTION_STOPPED = 2;
+
private final String mText;
private final String mLocale;
/**
@@ -118,12 +115,11 @@ public class AlwaysOnHotwordDetector {
*/
private final KeyphraseSoundModel mEnrolledSoundModel;
private final KeyphraseEnrollmentInfo mKeyphraseEnrollmentInfo;
- private final int mAvailability;
private final IVoiceInteractionService mVoiceInteractionService;
private final IVoiceInteractionManagerService mModelManagementService;
private final SoundTriggerListener mInternalCallback;
-
- private int mRecognitionState;
+ private final Callback mExternalCallback;
+ private final boolean mDisabled;
/**
* Callbacks for always-on hotword detection.
@@ -137,10 +133,6 @@ public class AlwaysOnHotwordDetector {
*/
void onDetected(byte[] data);
/**
- * Called when the detection for the associated keyphrase starts.
- */
- void onDetectionStarted();
- /**
* Called when the detection for the associated keyphrase stops.
*/
void onDetectionStopped();
@@ -163,7 +155,8 @@ public class AlwaysOnHotwordDetector {
mLocale = locale;
mKeyphraseEnrollmentInfo = keyphraseEnrollmentInfo;
mKeyphraseMetadata = mKeyphraseEnrollmentInfo.getKeyphraseMetadata(text, locale);
- mInternalCallback = new SoundTriggerListener(callback);
+ mExternalCallback = callback;
+ mInternalCallback = new SoundTriggerListener(new MyHandler());
mVoiceInteractionService = voiceInteractionService;
mModelManagementService = modelManagementService;
if (mKeyphraseMetadata != null) {
@@ -171,7 +164,9 @@ public class AlwaysOnHotwordDetector {
} else {
mEnrolledSoundModel = null;
}
- mAvailability = internalGetAvailability();
+ int initialAvailability = internalGetAvailabilityLocked();
+ mDisabled = (initialAvailability == KEYPHRASE_HARDWARE_UNAVAILABLE)
+ || (initialAvailability == KEYPHRASE_UNSUPPORTED);
}
/**
@@ -186,7 +181,9 @@ public class AlwaysOnHotwordDetector {
* {@link #KEYPHRASE_ENROLLED}.
*/
public int getAvailability() {
- return mAvailability;
+ synchronized (this) {
+ return internalGetAvailabilityLocked();
+ }
}
/**
@@ -197,8 +194,13 @@ public class AlwaysOnHotwordDetector {
* before calling this method to avoid this exception.
*/
public int getSupportedRecognitionModes() {
- if (mAvailability == KEYPHRASE_HARDWARE_UNAVAILABLE
- || mAvailability == KEYPHRASE_UNSUPPORTED) {
+ synchronized (this) {
+ return getSupportedRecognitionModesLocked();
+ }
+ }
+
+ private int getSupportedRecognitionModesLocked() {
+ if (mDisabled) {
throw new UnsupportedOperationException(
"Getting supported recognition modes for the keyphrase is not supported");
}
@@ -207,17 +209,6 @@ public class AlwaysOnHotwordDetector {
}
/**
- * Gets the status of the recognition.
- * @return A flag comprised of {@link #RECOGNITION_STATUS_NOT_AVAILABLE},
- * {@link #RECOGNITION_STATUS_NOT_REQUESTED}, {@link #RECOGNITION_STATUS_REQUESTED},
- * {@link #RECOGNITION_STATUS_DISABLED_TEMPORARILY} and
- * {@link #RECOGNITION_STATUS_ACTIVE}.
- */
- public int getRecognitionStatus() {
- return mRecognitionState;
- }
-
- /**
* Starts recognition for the associated keyphrase.
*
* @param recognitionFlags The flags to control the recognition properties.
@@ -229,16 +220,19 @@ public class AlwaysOnHotwordDetector {
* before calling this method to avoid this exception.
*/
public int startRecognition(int recognitionFlags) {
- if (mAvailability != KEYPHRASE_ENROLLED
- || (mRecognitionState&RECOGNITION_STATUS_NOT_AVAILABLE) != 0) {
+ synchronized (this) {
+ return startRecognitionLocked(recognitionFlags);
+ }
+ }
+
+ private int startRecognitionLocked(int recognitionFlags) {
+ if (internalGetAvailabilityLocked() != KEYPHRASE_ENROLLED) {
throw new UnsupportedOperationException(
"Recognition for the given keyphrase is not supported");
}
- mRecognitionState &= RECOGNITION_STATUS_REQUESTED;
KeyphraseRecognitionExtra[] recognitionExtra = new KeyphraseRecognitionExtra[1];
// TODO: Do we need to do something about the confidence level here?
- // TODO: Take in captureTriggerAudio as a method param here.
recognitionExtra[0] = new KeyphraseRecognitionExtra(mKeyphraseMetadata.id,
mKeyphraseMetadata.recognitionModeFlags, new ConfidenceLevel[0]);
boolean captureTriggerAudio =
@@ -267,12 +261,17 @@ public class AlwaysOnHotwordDetector {
* before calling this method to avoid this exception.
*/
public int stopRecognition() {
- if (mAvailability != KEYPHRASE_ENROLLED) {
+ synchronized (this) {
+ return stopRecognitionLocked();
+ }
+ }
+
+ private synchronized int stopRecognitionLocked() {
+ if (internalGetAvailabilityLocked() != KEYPHRASE_ENROLLED) {
throw new UnsupportedOperationException(
"Recognition for the given keyphrase is not supported");
}
- mRecognitionState &= ~RECOGNITION_STATUS_NOT_REQUESTED;
int code = STATUS_ERROR;
try {
code = mModelManagementService.stopRecognition(
@@ -299,8 +298,7 @@ public class AlwaysOnHotwordDetector {
* before calling this method to avoid this exception.
*/
public Intent getManageIntent(int action) {
- if (mAvailability == KEYPHRASE_HARDWARE_UNAVAILABLE
- || mAvailability == KEYPHRASE_UNSUPPORTED) {
+ if (mDisabled) {
throw new UnsupportedOperationException(
"Managing the given keyphrase is not supported");
}
@@ -313,7 +311,7 @@ public class AlwaysOnHotwordDetector {
return mKeyphraseEnrollmentInfo.getManageKeyphraseIntent(action, mText, mLocale);
}
- private int internalGetAvailability() {
+ private int internalGetAvailabilityLocked() {
ModuleProperties dspModuleProperties = null;
try {
dspModuleProperties =
@@ -323,21 +321,16 @@ public class AlwaysOnHotwordDetector {
}
// No DSP available
if (dspModuleProperties == null) {
- mRecognitionState = RECOGNITION_STATUS_NOT_AVAILABLE;
return KEYPHRASE_HARDWARE_UNAVAILABLE;
}
// No enrollment application supports this keyphrase/locale
if (mKeyphraseMetadata == null) {
- mRecognitionState = RECOGNITION_STATUS_NOT_AVAILABLE;
return KEYPHRASE_UNSUPPORTED;
}
// This keyphrase hasn't been enrolled.
if (mEnrolledSoundModel == null) {
- mRecognitionState = RECOGNITION_STATUS_NOT_AVAILABLE;
return KEYPHRASE_UNENROLLED;
}
- // Mark recognition as available
- mRecognitionState &= ~RECOGNITION_STATUS_NOT_AVAILABLE;
return KEYPHRASE_ENROLLED;
}
@@ -358,7 +351,6 @@ public class AlwaysOnHotwordDetector {
continue;
}
for (Keyphrase keyphrase : soundModel.keyphrases) {
- // TODO: Check the user handle here to only load a model for the current user.
if (keyphrase.id == keyphraseId) {
return soundModel;
}
@@ -372,28 +364,39 @@ public class AlwaysOnHotwordDetector {
/** @hide */
static final class SoundTriggerListener extends IRecognitionStatusCallback.Stub {
- private final Callback mCallback;
+ private final Handler mHandler;
- public SoundTriggerListener(Callback callback) {
- this.mCallback = callback;
+ public SoundTriggerListener(Handler handler) {
+ mHandler = handler;
}
@Override
public void onDetected(byte[] data) {
- Slog.i(TAG, "onKeyphraseSpoken");
- mCallback.onDetected(data);
+ Slog.i(TAG, "onDetected");
+ Message message = Message.obtain(mHandler, MSG_HOTWORD_DETECTED);
+ message.obj = data;
+ message.sendToTarget();
}
@Override
- public void onDetectionStarted() {
- // TODO: Set the RECOGNITION_STATUS_ACTIVE flag here.
- mCallback.onDetectionStarted();
+ public void onDetectionStopped() {
+ Slog.i(TAG, "onDetectionStopped");
+ mHandler.sendEmptyMessage(MSG_DETECTION_STOPPED);
}
+ }
+ class MyHandler extends Handler {
@Override
- public void onDetectionStopped() {
- // TODO: Unset the RECOGNITION_STATUS_ACTIVE flag here.
- mCallback.onDetectionStopped();
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_HOTWORD_DETECTED:
+ mExternalCallback.onDetected((byte[]) msg.obj);
+ break;
+ case MSG_DETECTION_STOPPED:
+ mExternalCallback.onDetectionStopped();
+ default:
+ super.handleMessage(msg);
+ }
}
}
}