Merge "Handle microphone contention/Phone calls while recognition is active" into lmp-dev

This commit is contained in:
Sandeep Siddhartha
2014-08-07 19:09:41 +00:00
committed by Android (Google) Code Review
6 changed files with 407 additions and 209 deletions

View File

@@ -27535,6 +27535,8 @@ package android.service.voice {
method public abstract void onAvailabilityChanged(int);
method public abstract void onDetected(android.service.voice.AlwaysOnHotwordDetector.EventPayload);
method public abstract void onError();
method public abstract void onRecognitionPaused();
method public abstract void onRecognitionResumed();
}
public static class AlwaysOnHotwordDetector.EventPayload {

View File

@@ -34,4 +34,12 @@ oneway interface IRecognitionStatusCallback {
* @param status The error code that was seen.
*/
void onError(int status);
/**
* Called when the recognition is paused temporarily for some reason.
*/
void onRecognitionPaused();
/**
* Called when the recognition is resumed after it was temporarily paused.
*/
void onRecognitionResumed();
}

View File

@@ -161,6 +161,8 @@ public class AlwaysOnHotwordDetector {
private static final int MSG_AVAILABILITY_CHANGED = 1;
private static final int MSG_HOTWORD_DETECTED = 2;
private static final int MSG_DETECTION_ERROR = 3;
private static final int MSG_DETECTION_PAUSE = 4;
private static final int MSG_DETECTION_RESUME = 5;
private final String mText;
private final String mLocale;
@@ -239,6 +241,18 @@ public class AlwaysOnHotwordDetector {
* Called when the detection fails due to an error.
*/
void onError();
/**
* Called when the recognition is paused temporarily for some reason.
* This is an informational callback, and the clients shouldn't be doing anything here
* except showing an indication on their UI if they have to.
*/
void onRecognitionPaused();
/**
* Called when the recognition is resumed after it was temporarily paused.
* This is an informational callback, and the clients shouldn't be doing anything here
* except showing an indication on their UI if they have to.
*/
void onRecognitionResumed();
}
/**
@@ -508,6 +522,18 @@ public class AlwaysOnHotwordDetector {
Slog.i(TAG, "onError: " + status);
mHandler.sendEmptyMessage(MSG_DETECTION_ERROR);
}
@Override
public void onRecognitionPaused() {
Slog.i(TAG, "onRecognitionPaused");
mHandler.sendEmptyMessage(MSG_DETECTION_PAUSE);
}
@Override
public void onRecognitionResumed() {
Slog.i(TAG, "onRecognitionResumed");
mHandler.sendEmptyMessage(MSG_DETECTION_RESUME);
}
}
class MyHandler extends Handler {
@@ -530,6 +556,12 @@ public class AlwaysOnHotwordDetector {
case MSG_DETECTION_ERROR:
mExternalCallback.onError();
break;
case MSG_DETECTION_PAUSE:
mExternalCallback.onRecognitionPaused();
break;
case MSG_DETECTION_RESUME:
mExternalCallback.onRecognitionResumed();
break;
default:
super.handleMessage(msg);
}

View File

@@ -28,8 +28,9 @@ import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
import android.hardware.soundtrigger.SoundTrigger.SoundModelEvent;
import android.hardware.soundtrigger.SoundTriggerModule;
import android.os.RemoteException;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Slog;
import android.util.SparseArray;
import java.util.ArrayList;
import java.util.UUID;
@@ -53,26 +54,37 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
public static final int STATUS_ERROR = SoundTrigger.STATUS_ERROR;
public static final int STATUS_OK = SoundTrigger.STATUS_OK;
private static final int INVALID_SOUND_MODEL_HANDLE = -1;
private static final int INVALID_VALUE = Integer.MIN_VALUE;
/** The {@link DspInfo} for the system, or null if none exists. */
/** The {@link ModuleProperties} for the system, or null if none exists. */
final ModuleProperties moduleProperties;
/** The properties for the DSP module */
private final SoundTriggerModule mModule;
private final Object mLock = new Object();
private final TelephonyManager mTelephonyManager;
private final PhoneStateListener mPhoneStateListener;
// Use a RemoteCallbackList here?
private final SparseArray<IRecognitionStatusCallback> mActiveListeners;
private int mCurrentSoundModelHandle = INVALID_SOUND_MODEL_HANDLE;
// TODO: Since many layers currently only deal with one recognition
// we simplify things by assuming one listener here too.
private IRecognitionStatusCallback mActiveListener;
private int mKeyphraseId = INVALID_VALUE;
private int mCurrentSoundModelHandle = INVALID_VALUE;
private UUID mCurrentSoundModelUuid = null;
// FIXME: Ideally this should not be stored if allowMultipleTriggers happens at a lower layer.
private RecognitionConfig mRecognitionConfig = null;
private boolean mRequested = false;
private boolean mCallActive = false;
// Indicates if the native sound trigger service is disabled or not.
// This is an indirect indication of the microphone being open in some other application.
private boolean mServiceDisabled = false;
private boolean mStarted = false;
SoundTriggerHelper() {
SoundTriggerHelper(TelephonyManager telephonyManager) {
ArrayList <ModuleProperties> modules = new ArrayList<>();
int status = SoundTrigger.listModules(modules);
mActiveListeners = new SparseArray<>(1);
mTelephonyManager = telephonyManager;
mPhoneStateListener = new MyCallStateListener();
if (status != SoundTrigger.STATUS_OK || modules.size() == 0) {
Slog.w(TAG, "listModules status=" + status + ", # of modules=" + modules.size());
moduleProperties = null;
@@ -84,17 +96,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
}
}
/**
* @return True, if a recognition for the given {@link Keyphrase} is active.
*/
synchronized boolean isKeyphraseActive(Keyphrase keyphrase) {
if (keyphrase == null) {
Slog.w(TAG, "isKeyphraseActive requires a non-null keyphrase");
return false;
}
return mActiveListeners.get(keyphrase.id) != null;
}
/**
* Starts recognition for the given keyphraseId.
*
@@ -104,85 +105,94 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
* @param listener The listener for the recognition events related to the given keyphrase.
* @return One of {@link #STATUS_ERROR} or {@link #STATUS_OK}.
*/
synchronized int startRecognition(int keyphraseId,
int startRecognition(int keyphraseId,
KeyphraseSoundModel soundModel,
IRecognitionStatusCallback listener,
RecognitionConfig recognitionConfig) {
if (DBG) {
Slog.d(TAG, "startRecognition for keyphraseId=" + keyphraseId
+ " soundModel=" + soundModel + ", listener=" + listener
+ ", recognitionConfig=" + recognitionConfig);
Slog.d(TAG, "moduleProperties=" + moduleProperties);
Slog.d(TAG, "# of current listeners=" + mActiveListeners.size());
Slog.d(TAG, "current SoundModel handle=" + mCurrentSoundModelHandle);
Slog.d(TAG, "current SoundModel UUID="
+ (mCurrentSoundModelUuid == null ? null : mCurrentSoundModelUuid));
}
if (moduleProperties == null || mModule == null) {
Slog.w(TAG, "Attempting startRecognition without the capability");
if (soundModel == null || listener == null || recognitionConfig == null) {
return STATUS_ERROR;
}
if (mCurrentSoundModelHandle != INVALID_SOUND_MODEL_HANDLE
&& !soundModel.uuid.equals(mCurrentSoundModelUuid)) {
Slog.w(TAG, "Unloading previous sound model");
int status = mModule.unloadSoundModel(mCurrentSoundModelHandle);
if (status != SoundTrigger.STATUS_OK) {
Slog.w(TAG, "unloadSoundModel call failed with " + status);
return status;
synchronized (mLock) {
if (DBG) {
Slog.d(TAG, "startRecognition for keyphraseId=" + keyphraseId
+ " soundModel=" + soundModel + ", listener=" + listener.asBinder()
+ ", recognitionConfig=" + recognitionConfig);
Slog.d(TAG, "moduleProperties=" + moduleProperties);
Slog.d(TAG, "current listener="
+ (mActiveListener == null ? "null" : mActiveListener.asBinder()));
Slog.d(TAG, "current SoundModel handle=" + mCurrentSoundModelHandle);
Slog.d(TAG, "current SoundModel UUID="
+ (mCurrentSoundModelUuid == null ? null : mCurrentSoundModelUuid));
}
mCurrentSoundModelHandle = INVALID_SOUND_MODEL_HANDLE;
mCurrentSoundModelUuid = null;
}
// If the previous recognition was by a different listener,
// Notify them that it was stopped.
IRecognitionStatusCallback oldListener = mActiveListeners.get(keyphraseId);
if (oldListener != null && oldListener.asBinder() != listener.asBinder()) {
Slog.w(TAG, "Canceling previous recognition");
try {
oldListener.onError(STATUS_ERROR);
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onDetectionStopped");
if (!mStarted) {
// Get the current call state synchronously for the first recognition.
mCallActive = mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE;
// Register for call state changes when the first call to start recognition occurs.
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
mActiveListeners.remove(keyphraseId);
}
// Load the sound model if the current one is null.
int soundModelHandle = mCurrentSoundModelHandle;
if (mCurrentSoundModelHandle == INVALID_SOUND_MODEL_HANDLE
|| mCurrentSoundModelUuid == null) {
int[] handle = new int[] { INVALID_SOUND_MODEL_HANDLE };
int status = mModule.loadSoundModel(soundModel, handle);
if (status != SoundTrigger.STATUS_OK) {
Slog.w(TAG, "loadSoundModel call failed with " + status);
return status;
}
if (handle[0] == INVALID_SOUND_MODEL_HANDLE) {
Slog.w(TAG, "loadSoundModel call returned invalid sound model handle");
if (moduleProperties == null || mModule == null) {
Slog.w(TAG, "Attempting startRecognition without the capability");
return STATUS_ERROR;
}
soundModelHandle = handle[0];
} else {
if (DBG) Slog.d(TAG, "Reusing previously loaded sound model");
}
// Start the recognition.
int status = mModule.startRecognition(soundModelHandle, recognitionConfig);
if (status != SoundTrigger.STATUS_OK) {
Slog.w(TAG, "startRecognition failed with " + status);
return status;
}
if (mCurrentSoundModelHandle != INVALID_VALUE
&& !soundModel.uuid.equals(mCurrentSoundModelUuid)) {
Slog.w(TAG, "Unloading previous sound model");
int status = mModule.unloadSoundModel(mCurrentSoundModelHandle);
if (status != SoundTrigger.STATUS_OK) {
Slog.w(TAG, "unloadSoundModel call failed with " + status);
}
mCurrentSoundModelHandle = INVALID_VALUE;
mCurrentSoundModelUuid = null;
mStarted = false;
}
// Everything went well!
mCurrentSoundModelHandle = soundModelHandle;
mCurrentSoundModelUuid = soundModel.uuid;
mRecognitionConfig = recognitionConfig;
// Register the new listener. This replaces the old one.
// There can only be a maximum of one active listener for a keyphrase
// at any given time.
mActiveListeners.put(keyphraseId, listener);
return STATUS_OK;
// If the previous recognition was by a different listener,
// Notify them that it was stopped.
if (mActiveListener != null && mActiveListener.asBinder() != listener.asBinder()) {
Slog.w(TAG, "Canceling previous recognition");
try {
mActiveListener.onError(STATUS_ERROR);
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onDetectionStopped");
}
mActiveListener = null;
}
// Load the sound model if the current one is null.
int soundModelHandle = mCurrentSoundModelHandle;
if (mCurrentSoundModelHandle == INVALID_VALUE
|| mCurrentSoundModelUuid == null) {
int[] handle = new int[] { INVALID_VALUE };
int status = mModule.loadSoundModel(soundModel, handle);
if (status != SoundTrigger.STATUS_OK) {
Slog.w(TAG, "loadSoundModel call failed with " + status);
return status;
}
if (handle[0] == INVALID_VALUE) {
Slog.w(TAG, "loadSoundModel call returned invalid sound model handle");
return STATUS_ERROR;
}
soundModelHandle = handle[0];
} else {
if (DBG) Slog.d(TAG, "Reusing previously loaded sound model");
}
// Start the recognition.
mRequested = true;
mKeyphraseId = keyphraseId;
mCurrentSoundModelHandle = soundModelHandle;
mCurrentSoundModelUuid = soundModel.uuid;
mRecognitionConfig = recognitionConfig;
// Register the new listener. This replaces the old one.
// There can only be a maximum of one active listener at any given time.
mActiveListener = listener;
return updateRecognitionLocked(false /* don't notify for synchronous calls */);
}
}
/**
@@ -195,173 +205,307 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener {
*
* @return One of {@link #STATUS_ERROR} or {@link #STATUS_OK}.
*/
synchronized int stopRecognition(int keyphraseId, IRecognitionStatusCallback listener) {
if (DBG) {
Slog.d(TAG, "stopRecognition for keyphraseId=" + keyphraseId
+ ", listener=" + listener);
Slog.d(TAG, "# of current listeners = " + mActiveListeners.size());
}
if (moduleProperties == null || mModule == null) {
Slog.w(TAG, "Attempting stopRecognition without the capability");
return STATUS_ERROR;
}
IRecognitionStatusCallback currentListener = mActiveListeners.get(keyphraseId);
int stopRecognition(int keyphraseId, IRecognitionStatusCallback listener) {
if (listener == null) {
Slog.w(TAG, "Attempting stopRecognition without a valid listener");
return STATUS_ERROR;
} if (currentListener == null) {
// startRecognition hasn't been called or it failed.
Slog.w(TAG, "Attempting stopRecognition without a successful startRecognition");
return STATUS_ERROR;
} else if (currentListener.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");
return STATUS_ERROR;
} else {
}
synchronized (mLock) {
if (DBG) {
Slog.d(TAG, "stopRecognition for keyphraseId=" + keyphraseId
+ ", listener=" + listener.asBinder());
Slog.d(TAG, "current listener="
+ (mActiveListener == null ? "null" : mActiveListener.asBinder()));
}
if (moduleProperties == null || mModule == null) {
Slog.w(TAG, "Attempting stopRecognition without the capability");
return STATUS_ERROR;
}
if (mActiveListener == null) {
// startRecognition hasn't been called or it failed.
Slog.w(TAG, "Attempting stopRecognition without a successful startRecognition");
return STATUS_ERROR;
}
if (mActiveListener.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");
return STATUS_ERROR;
}
// Stop recognition if it's the current one, ignore otherwise.
int status = mModule.stopRecognition(mCurrentSoundModelHandle);
mRequested = false;
int status = updateRecognitionLocked(false /* don't notify for synchronous calls */);
if (status != SoundTrigger.STATUS_OK) {
Slog.w(TAG, "stopRecognition call failed with " + status);
return status;
}
status = mModule.unloadSoundModel(mCurrentSoundModelHandle);
if (status != SoundTrigger.STATUS_OK) {
Slog.w(TAG, "unloadSoundModel call failed with " + status);
return status;
}
mCurrentSoundModelHandle = INVALID_SOUND_MODEL_HANDLE;
mCurrentSoundModelUuid = null;
mActiveListeners.remove(keyphraseId);
return STATUS_OK;
// Clear the internal state once the recognition has been stopped.
// Unload sound model call may fail in scenarios, and we'd still want
// to reload the sound model.
internalClearStateLocked();
return status;
}
}
synchronized void stopAllRecognitions() {
if (moduleProperties == null || mModule == null) {
return;
}
/**
* Stops all recognitions active currently and clears the internal state.
*/
void stopAllRecognitions() {
synchronized (mLock) {
if (moduleProperties == null || mModule == null) {
return;
}
if (mCurrentSoundModelHandle == INVALID_SOUND_MODEL_HANDLE) {
return;
}
if (mCurrentSoundModelHandle == INVALID_VALUE) {
return;
}
int status = mModule.stopRecognition(mCurrentSoundModelHandle);
if (status != SoundTrigger.STATUS_OK) {
Slog.w(TAG, "stopRecognition call failed with " + status);
}
status = mModule.unloadSoundModel(mCurrentSoundModelHandle);
if (status != SoundTrigger.STATUS_OK) {
Slog.w(TAG, "unloadSoundModel call failed with " + status);
}
mRequested = false;
int status = updateRecognitionLocked(false /* don't notify for synchronous calls */);
status = mModule.unloadSoundModel(mCurrentSoundModelHandle);
if (status != SoundTrigger.STATUS_OK) {
Slog.w(TAG, "unloadSoundModel call failed with " + status);
}
mCurrentSoundModelHandle = INVALID_SOUND_MODEL_HANDLE;
mCurrentSoundModelUuid = null;
mActiveListeners.clear();
internalClearStateLocked();
}
}
//---- SoundTrigger.StatusListener methods
@Override
public void onRecognition(RecognitionEvent event) {
if (event == null) {
if (event == null || !(event instanceof KeyphraseRecognitionEvent)) {
Slog.w(TAG, "Invalid recognition event!");
return;
}
if (DBG) Slog.d(TAG, "onRecognition: " + event);
switch (event.status) {
// Fire aborts/failures to all listeners since it's not tied to a keyphrase.
case SoundTrigger.RECOGNITION_STATUS_ABORT: // fall-through
case SoundTrigger.RECOGNITION_STATUS_FAILURE:
try {
synchronized (this) {
for (int i = 0; i < mActiveListeners.size(); i++) {
mActiveListeners.valueAt(i).onError(STATUS_ERROR);
}
}
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onDetectionStopped");
}
break;
case SoundTrigger.RECOGNITION_STATUS_SUCCESS:
if (!(event instanceof KeyphraseRecognitionEvent)) {
Slog.w(TAG, "Invalid recognition event!");
return;
}
KeyphraseRecognitionExtra[] keyphraseExtras =
((KeyphraseRecognitionEvent) event).keyphraseExtras;
if (keyphraseExtras == null || keyphraseExtras.length == 0) {
Slog.w(TAG, "Invalid keyphrase recognition event!");
return;
}
// TODO: Handle more than one keyphrase extras.
int keyphraseId = keyphraseExtras[0].id;
try {
synchronized(this) {
// Check which keyphrase triggered, and fire the appropriate event.
IRecognitionStatusCallback listener = mActiveListeners.get(keyphraseId);
if (listener != null) {
listener.onDetected((KeyphraseRecognitionEvent) event);
} else {
Slog.w(TAG, "received onRecognition event without any listener for it");
return;
}
// FIXME: Remove this block if the lower layer supports multiple triggers.
if (mRecognitionConfig != null
&& mRecognitionConfig.allowMultipleTriggers) {
int status = mModule.startRecognition(
mCurrentSoundModelHandle, mRecognitionConfig);
if (status != STATUS_OK) {
Slog.w(TAG, "Error in restarting recognition after a trigger");
listener.onError(status);
}
}
}
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onDetectionStopped");
}
break;
synchronized (mLock) {
if (mActiveListener == null) {
Slog.w(TAG, "received onRecognition event without any listener for it");
return;
}
switch (event.status) {
// Fire aborts/failures to all listeners since it's not tied to a keyphrase.
case SoundTrigger.RECOGNITION_STATUS_ABORT:
onRecognitionAbortLocked();
break;
case SoundTrigger.RECOGNITION_STATUS_FAILURE:
onRecognitionFailureLocked();
break;
case SoundTrigger.RECOGNITION_STATUS_SUCCESS:
onRecognitionSuccessLocked((KeyphraseRecognitionEvent) event);
break;
}
}
}
@Override
public void onSoundModelUpdate(SoundModelEvent event) {
if (event == null) {
Slog.w(TAG, "Invalid sound model event!");
return;
}
if (DBG) Slog.d(TAG, "onSoundModelUpdate: " + event);
//TODO: implement sound model update
synchronized (mLock) {
onSoundModelUpdatedLocked(event);
}
}
@Override
public void onServiceStateChange(int state) {
if (DBG) Slog.d(TAG, "onServiceStateChange, state: " + state);
//TODO: implement service state update
synchronized (mLock) {
onServiceStateChangedLocked(SoundTrigger.SERVICE_STATE_DISABLED == state);
}
}
@Override
public void onServiceDied() {
synchronized (this) {
try {
for (int i = 0; i < mActiveListeners.size(); i++) {
mActiveListeners.valueAt(i).onError(SoundTrigger.STATUS_DEAD_OBJECT);
}
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onDetectionStopped");
}
mCurrentSoundModelHandle = INVALID_SOUND_MODEL_HANDLE;
mCurrentSoundModelUuid = null;
// Remove all listeners.
mActiveListeners.clear();
Slog.e(TAG, "onServiceDied!!");
synchronized (mLock) {
onServiceDiedLocked();
}
}
class MyCallStateListener extends PhoneStateListener {
@Override
public void onCallStateChanged(int state, String arg1) {
if (DBG) Slog.d(TAG, "onCallStateChanged: " + state);
synchronized (mLock) {
onCallStateChangedLocked(TelephonyManager.CALL_STATE_IDLE != state);
}
}
}
private void onCallStateChangedLocked(boolean callActive) {
if (mCallActive == callActive) {
// We consider multiple call states as being active
// so we check if something really changed or not here.
return;
}
mCallActive = callActive;
updateRecognitionLocked(true /* notify */);
}
private void onSoundModelUpdatedLocked(SoundModelEvent event) {
// TODO: Handle sound model update here.
}
private void onServiceStateChangedLocked(boolean disabled) {
if (disabled == mServiceDisabled) {
return;
}
mServiceDisabled = disabled;
updateRecognitionLocked(true /* notify */);
}
private void onRecognitionAbortLocked() {
Slog.w(TAG, "Recognition aborted");
// No-op
// This is handled via service state changes instead.
}
private void onRecognitionFailureLocked() {
Slog.w(TAG, "Recognition failure");
try {
if (mActiveListener != null) {
mActiveListener.onError(STATUS_ERROR);
}
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onError", e);
} finally {
internalClearStateLocked();
}
}
private void onRecognitionSuccessLocked(KeyphraseRecognitionEvent event) {
Slog.i(TAG, "Recognition success");
KeyphraseRecognitionExtra[] keyphraseExtras =
((KeyphraseRecognitionEvent) event).keyphraseExtras;
if (keyphraseExtras == null || keyphraseExtras.length == 0) {
Slog.w(TAG, "Invalid keyphrase recognition event!");
return;
}
// TODO: Handle more than one keyphrase extras.
if (mKeyphraseId != keyphraseExtras[0].id) {
Slog.w(TAG, "received onRecognition event for a different keyphrase");
return;
}
try {
if (mActiveListener != null) {
mActiveListener.onDetected((KeyphraseRecognitionEvent) event);
}
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onDetected", e);
}
mStarted = false;
mRequested = mRecognitionConfig.allowMultipleTriggers;
// TODO: Remove this block if the lower layer supports multiple triggers.
if (mRequested) {
updateRecognitionLocked(true /* notify */);
}
}
private void onServiceDiedLocked() {
try {
if (mActiveListener != null) {
mActiveListener.onError(SoundTrigger.STATUS_DEAD_OBJECT);
}
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onError", e);
} finally {
internalClearStateLocked();
}
}
private int updateRecognitionLocked(boolean notify) {
if (mModule == null || moduleProperties == null
|| mCurrentSoundModelHandle == INVALID_VALUE || mActiveListener == null) {
// Nothing to do here.
return STATUS_OK;
}
boolean start = mRequested && !mCallActive && !mServiceDisabled;
if (start == mStarted) {
// No-op.
return STATUS_OK;
}
// See if the recognition needs to be started.
if (start) {
// Start recognition.
int status = mModule.startRecognition(mCurrentSoundModelHandle, mRecognitionConfig);
if (status != SoundTrigger.STATUS_OK) {
Slog.w(TAG, "startRecognition failed with " + status);
// Notify of error if needed.
if (notify) {
try {
mActiveListener.onError(status);
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onError", e);
}
}
} else {
mStarted = true;
// Notify of resume if needed.
if (notify) {
try {
mActiveListener.onRecognitionResumed();
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onRecognitionResumed", e);
}
}
}
return status;
} else {
// Stop recognition.
int status = mModule.stopRecognition(mCurrentSoundModelHandle);
if (status != SoundTrigger.STATUS_OK) {
Slog.w(TAG, "stopRecognition call failed with " + status);
if (notify) {
try {
mActiveListener.onError(status);
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onError", e);
}
}
} else {
mStarted = false;
// Notify of pause if needed.
if (notify) {
try {
mActiveListener.onRecognitionPaused();
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onRecognitionPaused", e);
}
}
}
return status;
}
}
private void internalClearStateLocked() {
mStarted = false;
mRequested = false;
mKeyphraseId = INVALID_VALUE;
mCurrentSoundModelHandle = INVALID_VALUE;
mCurrentSoundModelUuid = null;
mRecognitionConfig = null;
mActiveListener = null;
// Unregister from call state changes.
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
}
}

View File

@@ -38,6 +38,7 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.service.voice.IVoiceInteractionService;
import android.service.voice.IVoiceInteractionSession;
import android.telephony.TelephonyManager;
import android.util.Slog;
import com.android.internal.app.IVoiceInteractionManagerService;
@@ -67,7 +68,8 @@ public class VoiceInteractionManagerService extends SystemService {
mContext = context;
mResolver = context.getContentResolver();
mDbHelper = new DatabaseHelper(context);
mSoundTriggerHelper = new SoundTriggerHelper();
mSoundTriggerHelper = new SoundTriggerHelper(
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE));
}
@Override

View File

@@ -45,6 +45,16 @@ public class MainInteractionService extends VoiceInteractionService {
public void onError() {
Log.i(TAG, "onError");
}
@Override
public void onRecognitionPaused() {
Log.i(TAG, "onRecognitionPaused");
}
@Override
public void onRecognitionResumed() {
Log.i(TAG, "onRecognitionResumed");
}
};
private AlwaysOnHotwordDetector mHotwordDetector;