1a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra/** 2a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * Copyright (C) 2014 The Android Open Source Project 3a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * 4a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * Licensed under the Apache License, Version 2.0 (the "License"); 5a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * you may not use this file except in compliance with the License. 6a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * You may obtain a copy of the License at 7a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * 8a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * http://www.apache.org/licenses/LICENSE-2.0 9a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * 10a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * Unless required by applicable law or agreed to in writing, software 11a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * distributed under the License is distributed on an "AS IS" BASIS, 12a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * See the License for the specific language governing permissions and 14a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * limitations under the License. 15a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra */ 16a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra 17a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishrapackage android.media.soundtrigger; 18a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra 193fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishraimport android.annotation.IntDef; 20a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishraimport android.annotation.NonNull; 21a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishraimport android.annotation.Nullable; 22a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishraimport android.annotation.SystemApi; 23a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishraimport android.hardware.soundtrigger.IRecognitionStatusCallback; 24a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishraimport android.hardware.soundtrigger.SoundTrigger; 253fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishraimport android.hardware.soundtrigger.SoundTrigger.RecognitionConfig; 263fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishraimport android.media.AudioFormat; 27a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishraimport android.os.Handler; 283fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishraimport android.os.Looper; 293fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishraimport android.os.Message; 30a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishraimport android.os.ParcelUuid; 31a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishraimport android.os.RemoteException; 32a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishraimport android.util.Slog; 33a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra 34a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishraimport com.android.internal.app.ISoundTriggerService; 35a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra 36a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishraimport java.io.PrintWriter; 373fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishraimport java.lang.annotation.Retention; 383fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishraimport java.lang.annotation.RetentionPolicy; 39a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishraimport java.util.UUID; 40a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra 41a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra/** 42a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * A class that allows interaction with the actual sound trigger detection on the system. 43a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * Sound trigger detection refers to a detectors that match generic sound patterns that are 44a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * not voice-based. The voice-based recognition models should utilize the {@link 45a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * VoiceInteractionService} instead. Access to this class is protected by a permission 46a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * granted only to system or privileged apps. 47a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * 48a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * @hide 49a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra */ 506a8fd7935fdc67b1cd43a11dc65abd4f8e4152ceArunesh Mishra@SystemApi 51a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishrapublic final class SoundTriggerDetector { 52a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra private static final boolean DBG = false; 53a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra private static final String TAG = "SoundTriggerDetector"; 54a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra 553fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra private static final int MSG_AVAILABILITY_CHANGED = 1; 563fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra private static final int MSG_SOUND_TRIGGER_DETECTED = 2; 573fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra private static final int MSG_DETECTION_ERROR = 3; 583fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra private static final int MSG_DETECTION_PAUSE = 4; 593fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra private static final int MSG_DETECTION_RESUME = 5; 603fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra 61a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra private final Object mLock = new Object(); 62a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra 63a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra private final ISoundTriggerService mSoundTriggerService; 64a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra private final UUID mSoundModelId; 65a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra private final Callback mCallback; 66a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra private final Handler mHandler; 67a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra private final RecognitionCallback mRecognitionCallback; 68a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra 693fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra /** @hide */ 703fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra @Retention(RetentionPolicy.SOURCE) 713fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra @IntDef(flag = true, 723fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra value = { 733fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra RECOGNITION_FLAG_NONE, 743fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO, 753fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS 763fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra }) 773fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra public @interface RecognitionFlags {} 783fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra 793fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra /** 803fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * Empty flag for {@link #startRecognition(int)}. 813fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * 823fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * @hide 833fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra */ 843fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra public static final int RECOGNITION_FLAG_NONE = 0; 853fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra 863fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra /** 873fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * Recognition flag for {@link #startRecognition(int)} that indicates 883fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * whether the trigger audio for hotword needs to be captured. 893fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra */ 903fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra public static final int RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO = 0x1; 913fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra 923fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra /** 933fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * Recognition flag for {@link #startRecognition(int)} that indicates 943fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * whether the recognition should keep going on even after the 953fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * model triggers. 963fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * If this flag is specified, it's possible to get multiple 973fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * triggers after a call to {@link #startRecognition(int)}, if the model 983fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * triggers multiple times. 993fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * When this isn't specified, the default behavior is to stop recognition once the 1003fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * trigger happenss, till the caller starts recognition again. 1013fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra */ 1023fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra public static final int RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS = 0x2; 1033fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra 1043fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra /** 1053fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * Additional payload for {@link Callback#onDetected}. 1063fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra */ 1073fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra public static class EventPayload { 1083fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra private final boolean mTriggerAvailable; 1093fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra 1103fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra // Indicates if {@code captureSession} can be used to continue capturing more audio 1113fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra // from the DSP hardware. 1123fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra private final boolean mCaptureAvailable; 1133fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra // The session to use when attempting to capture more audio from the DSP hardware. 1143fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra private final int mCaptureSession; 1153fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra private final AudioFormat mAudioFormat; 1163fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra // Raw data associated with the event. 1173fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra // This is the audio that triggered the keyphrase if {@code isTriggerAudio} is true. 1183fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra private final byte[] mData; 1193fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra 1203fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra private EventPayload(boolean triggerAvailable, boolean captureAvailable, 1213fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra AudioFormat audioFormat, int captureSession, byte[] data) { 1223fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra mTriggerAvailable = triggerAvailable; 1233fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra mCaptureAvailable = captureAvailable; 1243fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra mCaptureSession = captureSession; 1253fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra mAudioFormat = audioFormat; 1263fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra mData = data; 1273fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra } 1283fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra 1293fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra /** 1303fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * Gets the format of the audio obtained using {@link #getTriggerAudio()}. 1313fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * May be null if there's no audio present. 1323fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra */ 1333fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra @Nullable 1343fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra public AudioFormat getCaptureAudioFormat() { 1353fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra return mAudioFormat; 1363fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra } 1373fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra 1383fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra /** 1393fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * Gets the raw audio that triggered the keyphrase. 1403fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * This may be null if the trigger audio isn't available. 1413fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * If non-null, the format of the audio can be obtained by calling 1423fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * {@link #getCaptureAudioFormat()}. 1433fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * 1443fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * @see AlwaysOnHotwordDetector#RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO 1453fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra */ 1463fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra @Nullable 1473fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra public byte[] getTriggerAudio() { 1483fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra if (mTriggerAvailable) { 1493fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra return mData; 1503fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra } else { 1513fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra return null; 1523fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra } 1533fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra } 1543fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra 1553fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra /** 1563fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * Gets the session ID to start a capture from the DSP. 1573fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * This may be null if streaming capture isn't possible. 1583fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * If non-null, the format of the audio that can be captured can be 1593fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * obtained using {@link #getCaptureAudioFormat()}. 1603fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * 1613fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * TODO: Candidate for Public API when the API to start capture with a session ID 1623fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * is made public. 1633fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * 1643fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * TODO: Add this to {@link #getCaptureAudioFormat()}: 1653fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * "Gets the format of the audio obtained using {@link #getTriggerAudio()} 1663fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * or {@link #getCaptureSession()}. May be null if no audio can be obtained 1673fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * for either the trigger or a streaming session." 1683fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * 1693fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * TODO: Should this return a known invalid value instead? 1703fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * 1713fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * @hide 1723fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra */ 1733fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra @Nullable 1743fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra public Integer getCaptureSession() { 1753fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra if (mCaptureAvailable) { 1763fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra return mCaptureSession; 1773fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra } else { 1783fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra return null; 1793fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra } 1803fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra } 1813fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra } 1823fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra 1833fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra public static abstract class Callback { 184a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra /** 185a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * Called when the availability of the sound model changes. 186a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra */ 187a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra public abstract void onAvailabilityChanged(int status); 188a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra 189a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra /** 190a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * Called when the sound model has triggered (such as when it matched a 191a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * given sound pattern). 192a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra */ 1933fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra public abstract void onDetected(@NonNull EventPayload eventPayload); 194a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra 195a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra /** 196a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * Called when the detection fails due to an error. 197a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra */ 198a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra public abstract void onError(); 199a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra 200a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra /** 201a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * Called when the recognition is paused temporarily for some reason. 202a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * This is an informational callback, and the clients shouldn't be doing anything here 203a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * except showing an indication on their UI if they have to. 204a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra */ 205a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra public abstract void onRecognitionPaused(); 206a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra 207a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra /** 208a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * Called when the recognition is resumed after it was temporarily paused. 209a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * This is an informational callback, and the clients shouldn't be doing anything here 210a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * except showing an indication on their UI if they have to. 211a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra */ 212a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra public abstract void onRecognitionResumed(); 213a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra } 214a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra 215a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra /** 216a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * This class should be constructed by the {@link SoundTriggerManager}. 217a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * @hide 218a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra */ 219a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra SoundTriggerDetector(ISoundTriggerService soundTriggerService, UUID soundModelId, 220a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra @NonNull Callback callback, @Nullable Handler handler) { 221a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra mSoundTriggerService = soundTriggerService; 222a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra mSoundModelId = soundModelId; 223a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra mCallback = callback; 224a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra if (handler == null) { 2253fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra mHandler = new MyHandler(); 226a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra } else { 2273fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra mHandler = new MyHandler(handler.getLooper()); 228a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra } 229a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra mRecognitionCallback = new RecognitionCallback(); 230a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra } 231a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra 232a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra /** 233a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * Starts recognition on the associated sound model. Result is indicated via the 234a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * {@link Callback}. 235a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * @return Indicates whether the call succeeded or not. 236a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra */ 2373fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra public boolean startRecognition(@RecognitionFlags int recognitionFlags) { 238a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra if (DBG) { 239a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra Slog.d(TAG, "startRecognition()"); 240a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra } 2413fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra boolean captureTriggerAudio = 2423fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra (recognitionFlags & RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO) != 0; 2433fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra 2443fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra boolean allowMultipleTriggers = 2453fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra (recognitionFlags & RECOGNITION_FLAG_ALLOW_MULTIPLE_TRIGGERS) != 0; 246a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra try { 247a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra mSoundTriggerService.startRecognition(new ParcelUuid(mSoundModelId), 2483fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra mRecognitionCallback, new RecognitionConfig(captureTriggerAudio, 2493fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra allowMultipleTriggers, null, null)); 250a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra } catch (RemoteException e) { 251a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra return false; 252a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra } 253a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra return true; 254a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra } 255a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra 256a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra /** 257a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * Stops recognition for the associated model. 258a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra */ 259a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra public boolean stopRecognition() { 260a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra try { 261a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra mSoundTriggerService.stopRecognition(new ParcelUuid(mSoundModelId), 262a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra mRecognitionCallback); 263a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra } catch (RemoteException e) { 264a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra return false; 265a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra } 266a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra return true; 267a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra } 268a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra 2696a8fd7935fdc67b1cd43a11dc65abd4f8e4152ceArunesh Mishra /** 2706a8fd7935fdc67b1cd43a11dc65abd4f8e4152ceArunesh Mishra * @hide 2716a8fd7935fdc67b1cd43a11dc65abd4f8e4152ceArunesh Mishra */ 272a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra public void dump(String prefix, PrintWriter pw) { 273a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra synchronized (mLock) { 274a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra // TODO: Dump useful debug information. 275a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra } 276a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra } 277a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra 278a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra /** 279a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * Callback that handles events from the lower sound trigger layer. 2803fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * 2813fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * Note that these callbacks will be called synchronously from the SoundTriggerService 2823fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * layer and thus should do minimal work (such as sending a message on a handler to do 2833fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra * the real work). 284a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * @hide 285a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra */ 2863fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra private class RecognitionCallback extends IRecognitionStatusCallback.Stub { 287a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra 288a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra /** 289a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * @hide 290a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra */ 291a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra @Override 292f47f173b06e2972bb376da8ff11db3a83c21d10bArunesh Mishra public void onGenericSoundTriggerDetected(SoundTrigger.GenericRecognitionEvent event) { 293f47f173b06e2972bb376da8ff11db3a83c21d10bArunesh Mishra Slog.d(TAG, "onGenericSoundTriggerDetected()" + event); 2943fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra Message.obtain(mHandler, 2953fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra MSG_SOUND_TRIGGER_DETECTED, 2963fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra new EventPayload(event.triggerInData, event.captureAvailable, 2973fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra event.captureFormat, event.captureSession, event.data)) 2983fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra .sendToTarget(); 299a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra } 300a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra 301f47f173b06e2972bb376da8ff11db3a83c21d10bArunesh Mishra @Override 302f47f173b06e2972bb376da8ff11db3a83c21d10bArunesh Mishra public void onKeyphraseDetected(SoundTrigger.KeyphraseRecognitionEvent event) { 303f47f173b06e2972bb376da8ff11db3a83c21d10bArunesh Mishra Slog.e(TAG, "Ignoring onKeyphraseDetected() called for " + event); 304f47f173b06e2972bb376da8ff11db3a83c21d10bArunesh Mishra } 305f47f173b06e2972bb376da8ff11db3a83c21d10bArunesh Mishra 306a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra /** 307a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * @hide 308a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra */ 309a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra @Override 310a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra public void onError(int status) { 3113fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra Slog.d(TAG, "onError()" + status); 3123fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra mHandler.sendEmptyMessage(MSG_DETECTION_ERROR); 313a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra } 314a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra 315a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra /** 316a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * @hide 317a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra */ 318a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra @Override 319a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra public void onRecognitionPaused() { 3203fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra Slog.d(TAG, "onRecognitionPaused()"); 3213fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra mHandler.sendEmptyMessage(MSG_DETECTION_PAUSE); 322a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra } 323a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra 324a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra /** 325a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra * @hide 326a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra */ 327a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra @Override 328a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra public void onRecognitionResumed() { 3293fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra Slog.d(TAG, "onRecognitionResumed()"); 3303fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra mHandler.sendEmptyMessage(MSG_DETECTION_RESUME); 3313fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra } 3323fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra } 3333fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra 3343fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra private class MyHandler extends Handler { 3353fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra 3363fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra MyHandler() { 3373fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra super(); 3383fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra } 3393fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra 3403fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra MyHandler(Looper looper) { 3413fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra super(looper); 3423fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra } 3433fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra 3443fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra @Override 3453fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra public void handleMessage(Message msg) { 3463fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra if (mCallback == null) { 3473fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra Slog.w(TAG, "Received message: " + msg.what + " for NULL callback."); 3483fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra return; 3493fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra } 3503fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra switch (msg.what) { 3513fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra case MSG_SOUND_TRIGGER_DETECTED: 3523fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra mCallback.onDetected((EventPayload) msg.obj); 3533fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra break; 3543fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra case MSG_DETECTION_ERROR: 3553fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra mCallback.onError(); 3563fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra break; 3573fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra case MSG_DETECTION_PAUSE: 3583fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra mCallback.onRecognitionPaused(); 3593fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra break; 3603fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra case MSG_DETECTION_RESUME: 3613fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra mCallback.onRecognitionResumed(); 3623fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra break; 3633fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra default: 3643fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra super.handleMessage(msg); 3653fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra 3663fff7f5634ca788c1c84b6f1b316819ffd4c7cb2Arunesh Mishra } 367a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra } 368a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra } 369a772e5fc062c8de48cb9c1d61755110f6b2e189bArunesh Mishra} 370