SoundTriggerHelperpublic class SoundTriggerHelper extends Object implements SoundTrigger.StatusListenerHelper for {@link SoundTrigger} APIs.
Currently this just acts as an abstraction over all SoundTrigger API calls. |
Fields Summary |
---|
static final String | TAG | static final boolean | DBG | public static final int | STATUS_ERRORReturn codes for {@link #startRecognition(int, KeyphraseSoundModel,
IRecognitionStatusCallback, RecognitionConfig)},
{@link #stopRecognition(int, IRecognitionStatusCallback)} | public static final int | STATUS_OK | private static final int | INVALID_VALUE | final android.hardware.soundtrigger.SoundTrigger.ModuleProperties | modulePropertiesThe {@link ModuleProperties} for the system, or null if none exists. | private android.hardware.soundtrigger.SoundTriggerModule | mModuleThe properties for the DSP module | private final Object | mLock | private final android.content.Context | mContext | private final android.telephony.TelephonyManager | mTelephonyManager | private final android.telephony.PhoneStateListener | mPhoneStateListener | private final android.os.PowerManager | mPowerManager | private android.hardware.soundtrigger.IRecognitionStatusCallback | mActiveListener | private int | mKeyphraseId | private int | mCurrentSoundModelHandle | private android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel | mCurrentSoundModel | private android.hardware.soundtrigger.SoundTrigger.RecognitionConfig | mRecognitionConfig | private boolean | mRequested | private boolean | mCallActive | private boolean | mIsPowerSaveMode | private boolean | mServiceDisabled | private boolean | mStarted | private PowerSaveModeListener | mPowerSaveModeListener |
Constructors Summary |
---|
SoundTriggerHelper(android.content.Context context)
ArrayList <ModuleProperties> modules = new ArrayList<>();
int status = SoundTrigger.listModules(modules);
mContext = context;
mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mPhoneStateListener = new MyCallStateListener();
if (status != SoundTrigger.STATUS_OK || modules.size() == 0) {
Slog.w(TAG, "listModules status=" + status + ", # of modules=" + modules.size());
moduleProperties = null;
mModule = null;
} else {
// TODO: Figure out how to determine which module corresponds to the DSP hardware.
moduleProperties = modules.get(0);
}
|
Methods Summary |
---|
void | dump(java.io.FileDescriptor fd, java.io.PrintWriter pw, java.lang.String[] args)
synchronized (mLock) {
pw.print(" module properties=");
pw.println(moduleProperties == null ? "null" : moduleProperties);
pw.print(" keyphrase ID="); pw.println(mKeyphraseId);
pw.print(" sound model handle="); pw.println(mCurrentSoundModelHandle);
pw.print(" sound model UUID=");
pw.println(mCurrentSoundModel == null ? "null" : mCurrentSoundModel.uuid);
pw.print(" current listener=");
pw.println(mActiveListener == null ? "null" : mActiveListener.asBinder());
pw.print(" requested="); pw.println(mRequested);
pw.print(" started="); pw.println(mStarted);
pw.print(" call active="); pw.println(mCallActive);
pw.print(" power save mode active="); pw.println(mIsPowerSaveMode);
pw.print(" service disabled="); pw.println(mServiceDisabled);
}
| private void | internalClearSoundModelLocked()
mCurrentSoundModelHandle = INVALID_VALUE;
mCurrentSoundModel = null;
| private void | internalClearStateLocked()
mStarted = false;
mRequested = false;
mKeyphraseId = INVALID_VALUE;
mRecognitionConfig = null;
mActiveListener = null;
// Unregister from call state changes.
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
// Unregister from power save mode changes.
if (mPowerSaveModeListener != null) {
mContext.unregisterReceiver(mPowerSaveModeListener);
mPowerSaveModeListener = null;
}
| 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 | onPowerSaveModeChangedLocked(boolean isPowerSaveMode)
if (mIsPowerSaveMode == isPowerSaveMode) {
return;
}
mIsPowerSaveMode = isPowerSaveMode;
updateRecognitionLocked(true /* notify */);
| public void | onRecognition(android.hardware.soundtrigger.SoundTrigger.RecognitionEvent event)
if (event == null || !(event instanceof KeyphraseRecognitionEvent)) {
Slog.w(TAG, "Invalid recognition event!");
return;
}
if (DBG) Slog.d(TAG, "onRecognition: " + event);
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;
}
}
| 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(android.hardware.soundtrigger.SoundTrigger.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 */);
}
| public void | onServiceDied()
Slog.e(TAG, "onServiceDied!!");
synchronized (mLock) {
onServiceDiedLocked();
}
| private void | onServiceDiedLocked()
try {
if (mActiveListener != null) {
mActiveListener.onError(SoundTrigger.STATUS_DEAD_OBJECT);
}
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException in onError", e);
} finally {
internalClearSoundModelLocked();
internalClearStateLocked();
if (mModule != null) {
mModule.detach();
mModule = null;
}
}
| public void | onServiceStateChange(int state)
if (DBG) Slog.d(TAG, "onServiceStateChange, state: " + state);
synchronized (mLock) {
onServiceStateChangedLocked(SoundTrigger.SERVICE_STATE_DISABLED == state);
}
| private void | onServiceStateChangedLocked(boolean disabled)
if (disabled == mServiceDisabled) {
return;
}
mServiceDisabled = disabled;
updateRecognitionLocked(true /* notify */);
| public void | onSoundModelUpdate(android.hardware.soundtrigger.SoundTrigger.SoundModelEvent event)
if (event == null) {
Slog.w(TAG, "Invalid sound model event!");
return;
}
if (DBG) Slog.d(TAG, "onSoundModelUpdate: " + event);
synchronized (mLock) {
onSoundModelUpdatedLocked(event);
}
| private void | onSoundModelUpdatedLocked(android.hardware.soundtrigger.SoundTrigger.SoundModelEvent event)
// TODO: Handle sound model update here.
| int | startRecognition(int keyphraseId, android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel soundModel, android.hardware.soundtrigger.IRecognitionStatusCallback listener, android.hardware.soundtrigger.SoundTrigger.RecognitionConfig recognitionConfig)Starts recognition for the given keyphraseId.
if (soundModel == null || listener == null || recognitionConfig == null) {
return STATUS_ERROR;
}
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="
+ (mCurrentSoundModel == null ? null : mCurrentSoundModel.uuid));
}
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);
// Register for power saver mode changes when the first call to start recognition
// occurs.
if (mPowerSaveModeListener == null) {
mPowerSaveModeListener = new PowerSaveModeListener();
mContext.registerReceiver(mPowerSaveModeListener,
new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
}
mIsPowerSaveMode = mPowerManager.isPowerSaveMode();
}
if (moduleProperties == null) {
Slog.w(TAG, "Attempting startRecognition without the capability");
return STATUS_ERROR;
}
if (mModule == null) {
mModule = SoundTrigger.attachModule(moduleProperties.id, this, null);
if (mModule == null) {
Slog.w(TAG, "startRecognition cannot attach to sound trigger module");
return STATUS_ERROR;
}
}
// Unload the previous model if the current one isn't invalid
// and, it's not the same as the new one.
// This helps use cache and reuse the model and just start/stop it when necessary.
if (mCurrentSoundModelHandle != INVALID_VALUE
&& !soundModel.equals(mCurrentSoundModel)) {
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);
}
internalClearSoundModelLocked();
mStarted = false;
}
// 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", e);
}
mActiveListener = null;
}
// Load the sound model if the current one is null.
int soundModelHandle = mCurrentSoundModelHandle;
if (mCurrentSoundModelHandle == INVALID_VALUE
|| mCurrentSoundModel == 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;
mCurrentSoundModel = soundModel;
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 */);
}
| void | stopAllRecognitions()Stops all recognitions active currently and clears the internal state.
synchronized (mLock) {
if (moduleProperties == null || mModule == null) {
return;
}
if (mCurrentSoundModelHandle == INVALID_VALUE) {
return;
}
mRequested = false;
int status = updateRecognitionLocked(false /* don't notify for synchronous calls */);
internalClearStateLocked();
}
| int | stopRecognition(int keyphraseId, android.hardware.soundtrigger.IRecognitionStatusCallback listener)Stops recognition for the given {@link Keyphrase} if a recognition is
currently active.
if (listener == null) {
return STATUS_ERROR;
}
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.
mRequested = false;
int status = updateRecognitionLocked(false /* don't notify for synchronous calls */);
if (status != SoundTrigger.STATUS_OK) {
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.
internalClearStateLocked();
return status;
}
| 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 && !mIsPowerSaveMode;
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;
}
|
|