Merge "SoundTriggerHelper: improve in call behavior" into rvc-dev
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index 6c0f2b0..edbdd4e 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -40,6 +40,9 @@
import android.hardware.soundtrigger.SoundTriggerModule;
import android.os.Binder;
import android.os.DeadObjectException;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.RemoteException;
@@ -113,6 +116,13 @@
private PowerSaveModeListener mPowerSaveModeListener;
+ // Handler to process call state changes will delay to allow time for the audio
+ // and sound trigger HALs to process the end of call notifications
+ // before we re enable pending recognition requests.
+ private final Handler mHandler;
+ private static final int MSG_CALL_STATE_CHANGED = 0;
+ private static final int CALL_INACTIVE_MSG_DELAY_MS = 1000;
+
SoundTriggerHelper(Context context) {
ArrayList <ModuleProperties> modules = new ArrayList<>();
int status = SoundTrigger.listModules(modules);
@@ -130,6 +140,31 @@
// TODO: Figure out how to determine which module corresponds to the DSP hardware.
mModuleProperties = modules.get(0);
}
+
+ Looper looper = Looper.myLooper();
+ if (looper == null) {
+ looper = Looper.getMainLooper();
+ }
+ if (looper != null) {
+ mHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_CALL_STATE_CHANGED:
+ synchronized (mLock) {
+ onCallStateChangedLocked(
+ TelephonyManager.CALL_STATE_OFFHOOK == msg.arg1);
+ }
+ break;
+ default:
+ Slog.e(TAG, "unknown message in handler:" + msg.what);
+ break;
+ }
+ }
+ };
+ } else {
+ mHandler = null;
+ }
}
/**
@@ -227,6 +262,37 @@
return status;
}
+ private int prepareForRecognition(ModelData modelData) {
+ if (mModule == null) {
+ mModule = SoundTrigger.attachModule(mModuleProperties.getId(), this, null);
+ if (mModule == null) {
+ Slog.w(TAG, "prepareForRecognition: cannot attach to sound trigger module");
+ return STATUS_ERROR;
+ }
+ }
+ // Load the model if it is not loaded.
+ if (!modelData.isModelLoaded()) {
+ // Before we try and load this model, we should first make sure that any other
+ // models that don't have an active recognition/dead callback are unloaded. Since
+ // there is a finite limit on the number of models that the hardware may be able to
+ // have loaded, we want to make sure there's room for our model.
+ stopAndUnloadDeadModelsLocked();
+ int[] handle = new int[] { 0 };
+ int status = mModule.loadSoundModel(modelData.getSoundModel(), handle);
+ if (status != SoundTrigger.STATUS_OK) {
+ Slog.w(TAG, "prepareForRecognition: loadSoundModel failed with status: " + status);
+ return status;
+ }
+ modelData.setHandle(handle[0]);
+ modelData.setLoaded();
+ if (DBG) {
+ Slog.d(TAG, "prepareForRecognition: Sound model loaded with handle:" + handle[0]);
+ }
+ }
+ return STATUS_OK;
+ }
+
+
/**
* Starts recognition for the given sound model. A single routine for both keyphrase and
* generic sound models.
@@ -248,12 +314,17 @@
Slog.w(TAG, "Attempting startRecognition without the capability");
return STATUS_ERROR;
}
- if (mModule == null) {
- mModule = SoundTrigger.attachModule(mModuleProperties.getId(), this, null);
- if (mModule == null) {
- Slog.w(TAG, "startRecognition cannot attach to sound trigger module");
- return STATUS_ERROR;
+
+ IRecognitionStatusCallback oldCallback = modelData.getCallback();
+ if (oldCallback != null && oldCallback.asBinder() != callback.asBinder()) {
+ Slog.w(TAG, "Canceling previous recognition for model id: "
+ + modelData.getModelId());
+ try {
+ oldCallback.onError(STATUS_ERROR);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "RemoteException in onDetectionStopped", e);
}
+ modelData.clearCallback();
}
// If the existing SoundModel is different (for the same UUID for Generic and same
@@ -287,48 +358,25 @@
}
}
- IRecognitionStatusCallback oldCallback = modelData.getCallback();
- if (oldCallback != null && oldCallback.asBinder() != callback.asBinder()) {
- Slog.w(TAG, "Canceling previous recognition for model id: " +
- modelData.getModelId());
- try {
- oldCallback.onError(STATUS_ERROR);
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException in onDetectionStopped", e);
- }
- modelData.clearCallback();
- }
-
- // Load the model if it is not loaded.
- if (!modelData.isModelLoaded()) {
- // Before we try and load this model, we should first make sure that any other
- // models that don't have an active recognition/dead callback are unloaded. Since
- // there is a finite limit on the number of models that the hardware may be able to
- // have loaded, we want to make sure there's room for our model.
- stopAndUnloadDeadModelsLocked();
- 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;
- }
- modelData.setHandle(handle[0]);
- modelData.setLoaded();
- Slog.d(TAG, "Sound model loaded with handle:" + handle[0]);
- }
modelData.setCallback(callback);
modelData.setRequested(true);
modelData.setRecognitionConfig(recognitionConfig);
modelData.setSoundModel(soundModel);
- int status = startRecognitionLocked(modelData,
+ if (!isRecognitionAllowed()) {
+ initializeTelephonyAndPowerStateListeners();
+ return STATUS_OK;
+ }
+
+ int status = prepareForRecognition(modelData);
+ if (status != STATUS_OK) {
+ Slog.w(TAG, "startRecognition failed to prepare model for recognition");
+ return status;
+ }
+ status = startRecognitionLocked(modelData,
false /* Don't notify for synchronous calls */);
- // Initialize power save, call active state monitoring logic.
+ // Initialize power save, call active state monitoring logic.
if (status == STATUS_OK) {
initializeTelephonyAndPowerStateListeners();
}
@@ -398,7 +446,8 @@
if (DBG) {
Slog.d(TAG, "stopRecognition for keyphraseId=" + keyphraseId + ", callback =" +
callback.asBinder());
- Slog.d(TAG, "current callback=" + (modelData == null ? "null" :
+ Slog.d(TAG, "current callback="
+ + ((modelData == null || modelData.getCallback() == null) ? "null" :
modelData.getCallback().asBinder()));
}
int status = stopRecognition(modelData, callback);
@@ -507,8 +556,8 @@
synchronized (mLock) {
MetricsLogger.count(mContext, "sth_unload_keyphrase_sound_model", 1);
ModelData modelData = getKeyphraseModelDataLocked(keyphraseId);
- if (mModule == null || modelData == null || modelData.getHandle() == INVALID_VALUE ||
- !modelData.isKeyphraseModel()) {
+ if (mModule == null || modelData == null || !modelData.isModelLoaded()
+ || !modelData.isKeyphraseModel()) {
return STATUS_ERROR;
}
@@ -943,6 +992,10 @@
return STATUS_OK;
}
if (start) {
+ int status = prepareForRecognition(model);
+ if (status != STATUS_OK) {
+ return status;
+ }
return startRecognitionLocked(model, notify);
} else {
return stopRecognitionLocked(model, notify);
@@ -992,8 +1045,15 @@
@Override
public void onCallStateChanged(int state, String arg1) {
if (DBG) Slog.d(TAG, "onCallStateChanged: " + state);
- synchronized (mLock) {
- onCallStateChangedLocked(TelephonyManager.CALL_STATE_OFFHOOK == state);
+
+ if (mHandler != null) {
+ synchronized (mLock) {
+ mHandler.removeMessages(MSG_CALL_STATE_CHANGED);
+ Message msg = mHandler.obtainMessage(MSG_CALL_STATE_CHANGED, state, 0);
+ mHandler.sendMessageDelayed(
+ msg, (TelephonyManager.CALL_STATE_OFFHOOK == state) ? 0
+ : CALL_INACTIVE_MSG_DELAY_MS);
+ }
}
}
}
@@ -1216,9 +1276,8 @@
// models.
private int startRecognitionLocked(ModelData modelData, boolean notify) {
IRecognitionStatusCallback callback = modelData.getCallback();
- int handle = modelData.getHandle();
RecognitionConfig config = modelData.getRecognitionConfig();
- if (callback == null || handle == INVALID_VALUE || config == null) {
+ if (callback == null || !modelData.isModelLoaded() || config == null) {
// Nothing to do here.
Slog.w(TAG, "startRecognition: Bad data passed in.");
MetricsLogger.count(mContext, "sth_start_recognition_error", 1);
@@ -1235,7 +1294,7 @@
if (mModule == null) {
return STATUS_ERROR;
}
- int status = mModule.startRecognition(handle, config);
+ int status = mModule.startRecognition(modelData.getHandle(), config);
if (status != SoundTrigger.STATUS_OK) {
Slog.w(TAG, "startRecognition failed with " + status);
MetricsLogger.count(mContext, "sth_start_recognition_error", 1);
@@ -1376,7 +1435,7 @@
// Model handle is an integer used by the HAL as an identifier for sound
// models.
- private int mModelHandle = INVALID_VALUE;
+ private int mModelHandle;
// The SoundModel instance, one of KeyphraseSoundModel or GenericSoundModel.
private SoundModel mSoundModel = null;
@@ -1436,7 +1495,6 @@
synchronized void clearState() {
mModelState = MODEL_NOTLOADED;
- mModelHandle = INVALID_VALUE;
mRecognitionConfig = null;
mRequested = false;
mCallback = null;