diff options
author | 2023-04-21 18:35:40 -0700 | |
---|---|---|
committer | 2023-05-03 15:40:58 -0700 | |
commit | 749650c2cb0c1ab68221ec86f86027d5bd48d634 (patch) | |
tree | f9335502ddc7994b3d23923c70524e0c624f123c | |
parent | 70b1371bbc01e18b4a245b94ea998c95adc2237d (diff) |
Add recognition session tokens
A recognition event can race with a start/stop/start in quick
succession. Attributing the event to the incorrect startReco leads to
state mismatch between lower/upper layers.
- Associate each startRecognition with an IBinder token at the STModule
layer
- Invalidate the token field on downwards stop calls, and drop callbacks
when no session is active
- Add the token as a field to framework recognition event, so async
clients can drop stale events
- Additionally, add protection against a startRecognition while the lock
is dropped in stopRecognition
- Call new startReco version in STService, and drop callbacks for stale
sessions
- Drop wtf on stale callback in the HAL enforcer, since we handle it
gracefully at a higher layer
Bug: 236826280
Fixes: 275080257
Test: SoundTriggerManagerTest#
testStartTriggerStopRecognitionRace_doesNotFail
Test: CtsVoiceInteractionTestCases
Test: FrameworksVoiceInteractionTests
Test: Smoke tests
Change-Id: I8a613b5f6821636e545309c09e6dfbb67626ea2b
15 files changed, 157 insertions, 40 deletions
diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java index 21fe686fa2e3..5c07fa48d466 100644 --- a/core/java/android/hardware/soundtrigger/ConversionUtil.java +++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java @@ -232,7 +232,8 @@ public class ConversionUtil { recognitionEvent.captureAvailable, captureSession, recognitionEvent.captureDelayMs, recognitionEvent.capturePreambleMs, recognitionEvent.triggerInData, audioFormat, recognitionEvent.data, - recognitionEvent.recognitionStillActive, aidlEvent.halEventReceivedMillis); + recognitionEvent.recognitionStillActive, aidlEvent.halEventReceivedMillis, + aidlEvent.token); } public static SoundTrigger.RecognitionEvent aidl2apiPhraseRecognitionEvent( @@ -254,7 +255,8 @@ public class ConversionUtil { recognitionEvent.common.captureDelayMs, recognitionEvent.common.capturePreambleMs, recognitionEvent.common.triggerInData, audioFormat, - recognitionEvent.common.data, apiExtras, aidlEvent.halEventReceivedMillis); + recognitionEvent.common.data, apiExtras, aidlEvent.halEventReceivedMillis, + aidlEvent.token); } // In case of a null input returns a non-null valid output. diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java index 6d43ddf7fe94..301b412e6ce2 100644 --- a/core/java/android/hardware/soundtrigger/SoundTrigger.java +++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java @@ -63,6 +63,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Locale; +import java.util.Objects; import java.util.UUID; /** @@ -1226,6 +1227,14 @@ public class SoundTrigger { @ElapsedRealtimeLong public final long halEventReceivedMillis; + /** + * Binder token returned by {@link SoundTriggerModule#startRecognitionWithToken( + * int soundModelHandle, SoundTrigger.RecognitionConfig config)} + * @hide + */ + public final IBinder token; + + /** @hide */ @TestApi @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @@ -1235,14 +1244,16 @@ public class SoundTrigger { @ElapsedRealtimeLong long halEventReceivedMillis) { this(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs, capturePreambleMs, triggerInData, captureFormat, - data, status == RECOGNITION_STATUS_GET_STATE_RESPONSE, halEventReceivedMillis); + data, status == RECOGNITION_STATUS_GET_STATE_RESPONSE, halEventReceivedMillis, + null); } /** @hide */ public RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable, int captureSession, int captureDelayMs, int capturePreambleMs, boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data, - boolean recognitionStillActive, @ElapsedRealtimeLong long halEventReceivedMillis) { + boolean recognitionStillActive, @ElapsedRealtimeLong long halEventReceivedMillis, + IBinder token) { this.status = status; this.soundModelHandle = soundModelHandle; this.captureAvailable = captureAvailable; @@ -1254,6 +1265,7 @@ public class SoundTrigger { this.data = data != null ? data : new byte[0]; this.recognitionStillActive = recognitionStillActive; this.halEventReceivedMillis = halEventReceivedMillis; + this.token = token; } /** @@ -1311,6 +1323,16 @@ public class SoundTrigger { return halEventReceivedMillis; } + /** + * Get token associated with this recognition session returned by + *{@link SoundTriggerModule#startRecognitionWithToken( + * int soundModelHandle, SoundTrigger.RecognitionConfig config)} + * @hide + */ + public IBinder getToken() { + return token; + } + /** @hide */ public static final @android.annotation.NonNull Parcelable.Creator<RecognitionEvent> CREATOR = new Parcelable.Creator<RecognitionEvent>() { @@ -1346,9 +1368,10 @@ public class SoundTrigger { byte[] data = in.readBlob(); boolean recognitionStillActive = in.readBoolean(); long halEventReceivedMillis = in.readLong(); + IBinder token = in.readStrongBinder(); return new RecognitionEvent(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs, capturePreambleMs, triggerInData, captureFormat, data, - recognitionStillActive, halEventReceivedMillis); + recognitionStillActive, halEventReceivedMillis, token); } /** @hide */ @@ -1376,6 +1399,7 @@ public class SoundTrigger { dest.writeBlob(data); dest.writeBoolean(recognitionStillActive); dest.writeLong(halEventReceivedMillis); + dest.writeStrongBinder(token); } @Override public int hashCode() { @@ -1396,6 +1420,7 @@ public class SoundTrigger { result = prime * result + status; result = result + (recognitionStillActive ? 1289 : 1291); result = prime * result + Long.hashCode(halEventReceivedMillis); + result = prime * result + Objects.hashCode(token); return result; } @@ -1425,6 +1450,9 @@ public class SoundTrigger { if (halEventReceivedMillis != other.halEventReceivedMillis) { return false; } + if (!Objects.equals(token, other.token)) { + return false; + } if (status != other.status) return false; if (triggerInData != other.triggerInData) @@ -1462,8 +1490,8 @@ public class SoundTrigger { + ", data=" + (data == null ? 0 : data.length) + ", recognitionStillActive=" + recognitionStillActive + ", halEventReceivedMillis=" + halEventReceivedMillis - + "]"; - } + + ", token=" + token + + "]"; } } /** @@ -1886,10 +1914,12 @@ public class SoundTrigger { int captureSession, int captureDelayMs, int capturePreambleMs, boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data, @Nullable KeyphraseRecognitionExtra[] keyphraseExtras, - @ElapsedRealtimeLong long halEventReceivedMillis) { + @ElapsedRealtimeLong long halEventReceivedMillis, + IBinder token) { this(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs, capturePreambleMs, triggerInData, captureFormat, data, keyphraseExtras, - status == RECOGNITION_STATUS_GET_STATE_RESPONSE, halEventReceivedMillis); + status == RECOGNITION_STATUS_GET_STATE_RESPONSE, halEventReceivedMillis, + token); } public KeyphraseRecognitionEvent(int status, int soundModelHandle, @@ -1897,10 +1927,11 @@ public class SoundTrigger { int captureSession, int captureDelayMs, int capturePreambleMs, boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data, @Nullable KeyphraseRecognitionExtra[] keyphraseExtras, - boolean recognitionStillActive, @ElapsedRealtimeLong long halEventReceivedMillis) { + boolean recognitionStillActive, @ElapsedRealtimeLong long halEventReceivedMillis, + IBinder token) { super(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs, capturePreambleMs, triggerInData, captureFormat, - data, recognitionStillActive, halEventReceivedMillis); + data, recognitionStillActive, halEventReceivedMillis, token); this.keyphraseExtras = keyphraseExtras != null ? keyphraseExtras : new KeyphraseRecognitionExtra[0]; } @@ -1938,12 +1969,13 @@ public class SoundTrigger { byte[] data = in.readBlob(); boolean recognitionStillActive = in.readBoolean(); long halEventReceivedMillis = in.readLong(); + IBinder token = in.readStrongBinder(); KeyphraseRecognitionExtra[] keyphraseExtras = in.createTypedArray(KeyphraseRecognitionExtra.CREATOR); return new KeyphraseRecognitionEvent(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs, capturePreambleMs, triggerInData, captureFormat, data, keyphraseExtras, recognitionStillActive, - halEventReceivedMillis); + halEventReceivedMillis, token); } @Override @@ -1966,6 +1998,7 @@ public class SoundTrigger { dest.writeBlob(data); dest.writeBoolean(recognitionStillActive); dest.writeLong(halEventReceivedMillis); + dest.writeStrongBinder(token); dest.writeTypedArray(keyphraseExtras, flags); } @@ -2015,6 +2048,7 @@ public class SoundTrigger { + ", data=" + (data == null ? 0 : data.length) + ", recognitionStillActive=" + recognitionStillActive + ", halEventReceivedMillis=" + halEventReceivedMillis + + ", token=" + token + "]"; } } @@ -2030,20 +2064,23 @@ public class SoundTrigger { public GenericRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable, int captureSession, int captureDelayMs, int capturePreambleMs, boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data, - @ElapsedRealtimeLong long halEventReceivedMillis) { + @ElapsedRealtimeLong long halEventReceivedMillis, + IBinder token) { this(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs, capturePreambleMs, triggerInData, captureFormat, data, - status == RECOGNITION_STATUS_GET_STATE_RESPONSE, halEventReceivedMillis); + status == RECOGNITION_STATUS_GET_STATE_RESPONSE, + halEventReceivedMillis, token); } public GenericRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable, int captureSession, int captureDelayMs, int capturePreambleMs, boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data, - boolean recognitionStillActive, @ElapsedRealtimeLong long halEventReceivedMillis) { + boolean recognitionStillActive, @ElapsedRealtimeLong long halEventReceivedMillis, + IBinder token) { super(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs, capturePreambleMs, triggerInData, captureFormat, - data, recognitionStillActive, halEventReceivedMillis); + data, recognitionStillActive, halEventReceivedMillis, token); } public static final @android.annotation.NonNull Parcelable.Creator<GenericRecognitionEvent> CREATOR @@ -2062,7 +2099,7 @@ public class SoundTrigger { return new GenericRecognitionEvent(event.status, event.soundModelHandle, event.captureAvailable, event.captureSession, event.captureDelayMs, event.capturePreambleMs, event.triggerInData, event.captureFormat, event.data, - event.recognitionStillActive, event.halEventReceivedMillis); + event.recognitionStillActive, event.halEventReceivedMillis, event.token); } @Override @@ -2092,7 +2129,7 @@ public class SoundTrigger { * * @hide */ - static int handleException(Exception e) { + public static int handleException(Exception e) { Log.w(TAG, "Exception caught", e); if (e instanceof RemoteException) { return STATUS_DEAD_OBJECT; diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java index 5cdbe233aa3b..48d4ea40fecd 100644 --- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java +++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java @@ -247,6 +247,16 @@ public class SoundTriggerModule { } /** + * Same as above, but return a binder token associated with the session. + * @hide + */ + public synchronized IBinder startRecognitionWithToken(int soundModelHandle, + SoundTrigger.RecognitionConfig config) throws RemoteException { + return mService.startRecognition(soundModelHandle, + ConversionUtil.api2aidlRecognitionConfig(config)); + } + + /** * Stop listening to all key phrases in a {@link SoundTrigger.SoundModel} * @param soundModelHandle The sound model handle to stop listening to * @return - {@link SoundTrigger#STATUS_OK} in case of success diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index 91c350aa9aba..5fa1a1ebc315 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -970,7 +970,8 @@ public class AlwaysOnHotwordDetector extends AbstractDetector { new KeyphraseRecognitionEvent(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs, capturePreambleMs, triggerInData, captureFormat, data, keyphraseRecognitionExtras.toArray( - new KeyphraseRecognitionExtra[0]), halEventReceivedMillis), + new KeyphraseRecognitionExtra[0]), halEventReceivedMillis, + new Binder()), mInternalCallback); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); diff --git a/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl index 18688ce7d48d..4bdefd011e48 100644 --- a/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl +++ b/media/aidl/android/media/soundtrigger_middleware/ISoundTriggerModule.aidl @@ -79,8 +79,9 @@ interface ISoundTriggerModule { * * May throw a ServiceSpecificException with an RESOURCE_CONTENTION status to indicate that * resources required for starting the model are currently consumed by other clients. + * @return - A token delivered along with future recognition events. */ - void startRecognition(int modelHandle, in RecognitionConfig config); + IBinder startRecognition(int modelHandle, in RecognitionConfig config); /** * Stop a recognition of a previously active recognition. Will NOT generate a recognition event. diff --git a/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionEventSys.aidl b/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionEventSys.aidl index 6c912ed7e056..d9d16ec30bff 100644 --- a/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionEventSys.aidl +++ b/media/aidl/android/media/soundtrigger_middleware/PhraseRecognitionEventSys.aidl @@ -33,4 +33,9 @@ parcelable PhraseRecognitionEventSys { */ // @ElapsedRealtimeLong long halEventReceivedMillis = -1; + /** + * Token relating this event to a particular recognition session, returned by + * {@link ISoundTriggerModule.startRecognition(int, RecognitionConfig} + */ + IBinder token; } diff --git a/media/aidl/android/media/soundtrigger_middleware/RecognitionEventSys.aidl b/media/aidl/android/media/soundtrigger_middleware/RecognitionEventSys.aidl index 84e327d5df8c..20ec8c236dcc 100644 --- a/media/aidl/android/media/soundtrigger_middleware/RecognitionEventSys.aidl +++ b/media/aidl/android/media/soundtrigger_middleware/RecognitionEventSys.aidl @@ -33,4 +33,9 @@ parcelable RecognitionEventSys { */ // @ElapsedRealtimeLong long halEventReceivedMillis = -1; + /** + * Token relating this event to a particular recognition session, returned by + * {@link ISoundTriggerModule.startRecognition(int, RecognitionConfig} + */ + IBinder token; } diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/SoundTriggerTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/SoundTriggerTest.java index e6a1be8f018d..35170b3516b7 100644 --- a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/SoundTriggerTest.java +++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/SoundTriggerTest.java @@ -28,6 +28,7 @@ import android.os.Parcel; import android.test.InstrumentationTestCase; import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.SmallTest; +import android.os.Binder; import java.util.Arrays; import java.util.Locale; @@ -346,7 +347,8 @@ public class SoundTriggerTest extends InstrumentationTestCase { .build(), null /* data */, null /* keyphraseExtras */, - 12345678 /* halEventReceivedMillis */); + 12345678 /* halEventReceivedMillis */, + new Binder() /* token */); // Write to a parcel Parcel parcel = Parcel.obtain(); @@ -379,7 +381,8 @@ public class SoundTriggerTest extends InstrumentationTestCase { .build(), new byte[1] /* data */, kpExtra, - 12345678 /* halEventReceivedMillis */); + 12345678 /* halEventReceivedMillis */, + new Binder() /* token */); // Write to a parcel Parcel parcel = Parcel.obtain(); @@ -428,7 +431,8 @@ public class SoundTriggerTest extends InstrumentationTestCase { .build(), data, kpExtra, - 12345678 /* halEventReceivedMillis */); + 12345678 /* halEventReceivedMillis */, + new Binder() /* token */); // Write to a parcel Parcel parcel = Parcel.obtain(); diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java index c485501e4509..4404ae6b2307 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java @@ -40,6 +40,7 @@ import android.hardware.soundtrigger.SoundTriggerModule; import android.os.Binder; import android.os.DeadObjectException; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.RemoteException; @@ -769,6 +770,10 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { return; } ModelData model = getModelDataForLocked(event.soundModelHandle); + if (!Objects.equals(event.getToken(), model.getToken())) { + // Stale event, do nothing + return; + } if (model == null || !model.isGenericModel()) { Slog.w(TAG, "Generic recognition event: Model does not exist for handle: " + event.soundModelHandle); @@ -851,6 +856,10 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { Slog.w(TAG, "Recognition aborted"); MetricsLogger.count(mContext, "sth_recognition_aborted", 1); ModelData modelData = getModelDataForLocked(event.soundModelHandle); + if (!Objects.equals(event.getToken(), modelData.getToken())) { + // Stale event, do nothing + return; + } if (modelData != null && modelData.isModelStarted()) { modelData.setStopped(); try { @@ -888,6 +897,10 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { MetricsLogger.count(mContext, "sth_keyphrase_recognition_event", 1); int keyphraseId = getKeyphraseIdFromEvent(event); ModelData modelData = getKeyphraseModelDataLocked(keyphraseId); + if (!Objects.equals(event.getToken(), modelData.getToken())) { + // Stale event, do nothing + return; + } if (modelData == null || !modelData.isKeyphraseModel()) { Slog.e(TAG, "Keyphase model data does not exist for ID:" + keyphraseId); @@ -1184,7 +1197,12 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { if (mModule == null) { return STATUS_ERROR; } - int status = mModule.startRecognition(modelData.getHandle(), config); + int status = STATUS_OK; + try { + modelData.setToken(mModule.startRecognitionWithToken(modelData.getHandle(), config)); + } catch (Exception e) { + status = SoundTrigger.handleException(e); + } if (status != SoundTrigger.STATUS_OK) { Slog.w(TAG, "startRecognition failed with " + status); MetricsLogger.count(mContext, "sth_start_recognition_error", 1); @@ -1339,6 +1357,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { // The SoundModel instance, one of KeyphraseSoundModel or GenericSoundModel. private SoundModel mSoundModel = null; + // Token used to disambiguate recognition sessions. + private IBinder mRecognitionToken = null; + private ModelData(UUID modelId, int modelType) { mModelId = modelId; // Private constructor, since we require modelType to be one of TYPE_GENERIC, @@ -1381,6 +1402,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } synchronized void setStopped() { + // If we are moving to the stopped state, we should clear out our + // startRecognition token + mRecognitionToken = null; mModelState = MODEL_LOADED; } @@ -1452,6 +1476,14 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { return mSoundModel; } + synchronized IBinder getToken() { + return mRecognitionToken; + } + + synchronized void setToken(IBinder token) { + mRecognitionToken = token; + } + synchronized int getModelType() { return mModelType; } diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java index bac24669696c..c3e0a3cd0292 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java @@ -256,7 +256,7 @@ public class SoundTriggerHalEnforcer implements ISoundTriggerHal { public void recognitionCallback(int model, RecognitionEventSys event) { synchronized (mModelStates) { ModelState state = mModelStates.get(model); - if (state == null || state == ModelState.INACTIVE) { + if (state == null) { Log.wtfStack(TAG, "Unexpected recognition event for model: " + model); reboot(); return; @@ -282,7 +282,7 @@ public class SoundTriggerHalEnforcer implements ISoundTriggerHal { public void phraseRecognitionCallback(int model, PhraseRecognitionEventSys event) { synchronized (mModelStates) { ModelState state = mModelStates.get(model); - if (state == null || state == ModelState.INACTIVE) { + if (state == null) { Log.wtfStack(TAG, "Unexpected recognition event for model: " + model); reboot(); return; diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java index 2ee4e3cff02c..ecd65ae9fa2f 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java @@ -241,13 +241,14 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt } @Override - public void startRecognition(int modelHandle, RecognitionConfig config) + public IBinder startRecognition(int modelHandle, RecognitionConfig config) throws RemoteException { try { - mDelegate.startRecognition(modelHandle, config); - mEventLogger.enqueue(SessionEvent.createForVoid( - START_RECOGNITION, modelHandle, config) + var result = mDelegate.startRecognition(modelHandle, config); + mEventLogger.enqueue(SessionEvent.createForReturn( + START_RECOGNITION, result, modelHandle, config) .printLog(ALOGI, TAG)); + return result; } catch (Exception e) { mEventLogger.enqueue(SessionEvent.createForException( START_RECOGNITION, e, modelHandle, config) diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java index 00b894e1c6b5..6b724de73488 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java @@ -241,10 +241,10 @@ public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddleware } @Override - public void startRecognition(int modelHandle, @NonNull RecognitionConfig config) + public IBinder startRecognition(int modelHandle, @NonNull RecognitionConfig config) throws RemoteException { enforcePermissions(); - mDelegate.startRecognition(modelHandle, config); + return mDelegate.startRecognition(modelHandle, config); } @Override diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java index 91e546696971..1558acf547d1 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java @@ -34,6 +34,7 @@ import android.media.soundtrigger_middleware.ISoundTriggerInjection; import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService; import android.media.soundtrigger_middleware.ISoundTriggerModule; import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor; +import android.os.IBinder; import android.os.RemoteException; import com.android.server.SystemService; @@ -176,10 +177,10 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic } @Override - public void startRecognition(int modelHandle, RecognitionConfig config) + public IBinder startRecognition(int modelHandle, RecognitionConfig config) throws RemoteException { try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { - mDelegate.startRecognition(modelHandle, config); + return mDelegate.startRecognition(modelHandle, config); } } diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java index f208c03024b2..2924c124f22e 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java @@ -434,7 +434,7 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware } @Override - public void startRecognition(int modelHandle, @NonNull RecognitionConfig config) { + public IBinder startRecognition(int modelHandle, @NonNull RecognitionConfig config) { // Input validation. ValidationUtil.validateRecognitionConfig(config); @@ -458,9 +458,10 @@ public class SoundTriggerMiddlewareValidation implements ISoundTriggerMiddleware // From here on, every exception isn't client's fault. try { - mDelegate.startRecognition(modelHandle, config); + var result = mDelegate.startRecognition(modelHandle, config); modelState.config = config; modelState.activityState = ModelState.Activity.ACTIVE; + return result; } catch (Exception e) { throw handleException(e); } diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java index 177b4da8fc75..083211c29283 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java @@ -303,10 +303,10 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo } @Override - public void startRecognition(int modelHandle, @NonNull RecognitionConfig config) { + public IBinder startRecognition(int modelHandle, @NonNull RecognitionConfig config) { synchronized (SoundTriggerModule.this) { checkValid(); - mLoadedModels.get(modelHandle).startRecognition(config); + return mLoadedModels.get(modelHandle).startRecognition(config); } } @@ -385,6 +385,8 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo public int mHandle; private ModelState mState = ModelState.INIT; private SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession mSession; + private IBinder mRecognitionToken = null; + private boolean mIsStopping = false; private @NonNull ModelState getState() { @@ -425,10 +427,15 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo return mSession.mSessionHandle; } - private void startRecognition(@NonNull RecognitionConfig config) { + private IBinder startRecognition(@NonNull RecognitionConfig config) { + if (mIsStopping == true) { + throw new RecoverableException(Status.INTERNAL_ERROR, "Race occurred"); + } mHalService.startRecognition(mHandle, mSession.mDeviceHandle, mSession.mIoHandle, config); + mRecognitionToken = new Binder(); setState(ModelState.ACTIVE); + return mRecognitionToken; } private void stopRecognition() { @@ -437,9 +444,12 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo // This call is idempotent in order to avoid races. return; } + mRecognitionToken = null; + mIsStopping = true; } mHalService.stopRecognition(mHandle); synchronized (SoundTriggerModule.this) { + mIsStopping = false; setState(ModelState.LOADED); } } @@ -474,9 +484,13 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo @NonNull RecognitionEventSys event) { ISoundTriggerCallback callback; synchronized (SoundTriggerModule.this) { + if (mRecognitionToken == null) { + return; + } if (!event.recognitionEvent.recognitionStillActive) { setState(ModelState.LOADED); } + event.token = mRecognitionToken; callback = mCallback; } // The callback must be invoked outside of the lock. @@ -495,12 +509,15 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo @NonNull PhraseRecognitionEventSys event) { ISoundTriggerCallback callback; synchronized (SoundTriggerModule.this) { + if (mRecognitionToken == null) { + return; + } if (!event.phraseRecognitionEvent.common.recognitionStillActive) { setState(ModelState.LOADED); } + event.token = mRecognitionToken; callback = mCallback; } - // The callback must be invoked outside of the lock. try { if (callback != null) { |