• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.hardware.soundtrigger;
18 
19 import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD;
20 import static android.Manifest.permission.RECORD_AUDIO;
21 import static android.Manifest.permission.SOUNDTRIGGER_DELEGATE_IDENTITY;
22 import static android.system.OsConstants.EBUSY;
23 import static android.system.OsConstants.EINVAL;
24 import static android.system.OsConstants.ENODEV;
25 import static android.system.OsConstants.ENOSYS;
26 import static android.system.OsConstants.EPERM;
27 import static android.system.OsConstants.EPIPE;
28 
29 import static java.util.Objects.requireNonNull;
30 
31 import android.annotation.ElapsedRealtimeLong;
32 import android.annotation.FlaggedApi;
33 import android.annotation.IntDef;
34 import android.annotation.IntRange;
35 import android.annotation.NonNull;
36 import android.annotation.Nullable;
37 import android.annotation.RequiresPermission;
38 import android.annotation.SuppressLint;
39 import android.annotation.SystemApi;
40 import android.annotation.TestApi;
41 import android.compat.annotation.UnsupportedAppUsage;
42 import android.content.Context;
43 import android.media.AudioFormat;
44 import android.media.permission.Identity;
45 import android.media.soundtrigger.Status;
46 import android.media.soundtrigger_middleware.ISoundTriggerInjection;
47 import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
48 import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
49 import android.os.Build;
50 import android.os.Handler;
51 import android.os.IBinder;
52 import android.os.Looper;
53 import android.os.Parcel;
54 import android.os.Parcelable;
55 import android.os.RemoteException;
56 import android.os.ServiceManager;
57 import android.os.ServiceSpecificException;
58 import android.os.SystemClock;
59 import android.util.Log;
60 
61 import java.lang.annotation.Retention;
62 import java.lang.annotation.RetentionPolicy;
63 import java.util.ArrayList;
64 import java.util.Arrays;
65 import java.util.Collection;
66 import java.util.List;
67 import java.util.Locale;
68 import java.util.Objects;
69 import java.util.UUID;
70 
71 /**
72  * The SoundTrigger class provides access to the service managing the sound trigger HAL.
73  *
74  * @hide
75  */
76 @SystemApi
77 public class SoundTrigger {
78     private static final String TAG = "SoundTrigger";
79 
SoundTrigger()80     private SoundTrigger() {
81     }
82 
83     /**
84      * Model architecture associated with a fake STHAL which can be injected.
85      * Used for testing purposes.
86      * @hide
87      */
88     public static final String FAKE_HAL_ARCH = ISoundTriggerInjection.FAKE_HAL_ARCH;
89 
90     /**
91      * Status code used when the operation succeeded
92      */
93     public static final int STATUS_OK = 0;
94     /** @hide */
95     public static final int STATUS_ERROR = Integer.MIN_VALUE;
96     /** @hide */
97     public static final int STATUS_PERMISSION_DENIED = -EPERM;
98     /** @hide */
99     public static final int STATUS_NO_INIT = -ENODEV;
100     /** @hide */
101     public static final int STATUS_BAD_VALUE = -EINVAL;
102     /** @hide */
103     public static final int STATUS_DEAD_OBJECT = -EPIPE;
104     /** @hide */
105     public static final int STATUS_INVALID_OPERATION = -ENOSYS;
106     /** @hide */
107     public static final int STATUS_BUSY = -EBUSY;
108 
109     /*****************************************************************************
110      * A ModuleProperties describes a given sound trigger hardware module
111      * managed by the native sound trigger service. Each module has a unique
112      * ID used to target any API call to this paricular module. Module
113      * properties are returned by listModules() method.
114      *
115      ****************************************************************************/
116     public static final class ModuleProperties implements Parcelable {
117 
118         /**
119          * Bit field values of AudioCapabilities supported by the implemented HAL
120          * driver.
121          * @hide
122          */
123         @Retention(RetentionPolicy.SOURCE)
124         @IntDef(flag = true, prefix = { "AUDIO_CAPABILITY_" }, value = {
125                 AUDIO_CAPABILITY_ECHO_CANCELLATION,
126                 AUDIO_CAPABILITY_NOISE_SUPPRESSION
127         })
128         public @interface AudioCapabilities {}
129 
130         /**
131          * If set the underlying module supports AEC.
132          * Describes bit field {@link ModuleProperties#mAudioCapabilities}
133          */
134         public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 0x1;
135         /**
136          * If set, the underlying module supports noise suppression.
137          * Describes bit field {@link ModuleProperties#mAudioCapabilities}
138          */
139         public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = 0x2;
140 
141         private final int mId;
142         @NonNull
143         private final String mImplementor;
144         @NonNull
145         private final String mDescription;
146         @NonNull
147         private final UUID mUuid;
148         private final int mVersion;
149         @NonNull
150         private final String mSupportedModelArch;
151         private final int mMaxSoundModels;
152         private final int mMaxKeyphrases;
153         private final int mMaxUsers;
154         @RecognitionModes
155         private final int mRecognitionModes;
156         private final boolean mSupportsCaptureTransition;
157         private final int mMaxBufferMillis;
158         private final boolean mSupportsConcurrentCapture;
159         private final int mPowerConsumptionMw;
160         private final boolean mReturnsTriggerInEvent;
161         @AudioCapabilities
162         private final int mAudioCapabilities;
163 
164         /** @hide */
165         @TestApi
ModuleProperties(int id, @NonNull String implementor, @NonNull String description, @NonNull String uuid, int version, @NonNull String supportedModelArch, int maxSoundModels, int maxKeyphrases, int maxUsers, @RecognitionModes int recognitionModes, boolean supportsCaptureTransition, int maxBufferMs, boolean supportsConcurrentCapture, int powerConsumptionMw, boolean returnsTriggerInEvent, int audioCapabilities)166         public ModuleProperties(int id, @NonNull String implementor, @NonNull String description,
167                 @NonNull String uuid, int version, @NonNull String supportedModelArch,
168                 int maxSoundModels, int maxKeyphrases, int maxUsers,
169                 @RecognitionModes int recognitionModes, boolean supportsCaptureTransition,
170                 int maxBufferMs, boolean supportsConcurrentCapture, int powerConsumptionMw,
171                 boolean returnsTriggerInEvent, int audioCapabilities) {
172             this.mId = id;
173             this.mImplementor = requireNonNull(implementor);
174             this.mDescription = requireNonNull(description);
175             this.mUuid = UUID.fromString(requireNonNull(uuid));
176             this.mVersion = version;
177             this.mSupportedModelArch = requireNonNull(supportedModelArch);
178             this.mMaxSoundModels = maxSoundModels;
179             this.mMaxKeyphrases = maxKeyphrases;
180             this.mMaxUsers = maxUsers;
181             this.mRecognitionModes = recognitionModes;
182             this.mSupportsCaptureTransition = supportsCaptureTransition;
183             this.mMaxBufferMillis = maxBufferMs;
184             this.mSupportsConcurrentCapture = supportsConcurrentCapture;
185             this.mPowerConsumptionMw = powerConsumptionMw;
186             this.mReturnsTriggerInEvent = returnsTriggerInEvent;
187             this.mAudioCapabilities = audioCapabilities;
188         }
189 
190         /** Unique module ID provided by the native service */
getId()191         public int getId() {
192             return mId;
193         }
194 
195         /** human readable voice detection engine implementor */
196         @NonNull
getImplementor()197         public String getImplementor() {
198             return mImplementor;
199         }
200 
201         /** human readable voice detection engine description */
202         @NonNull
getDescription()203         public String getDescription() {
204             return mDescription;
205         }
206 
207         /** Unique voice engine Id (changes with each version) */
208         @NonNull
getUuid()209         public UUID getUuid() {
210             return mUuid;
211         }
212 
213         /** Voice detection engine version */
getVersion()214         public int getVersion() {
215             return mVersion;
216         }
217 
218         /**
219          * String naming the architecture used for running the supported models.
220          * (eg. a platform running models on a DSP could implement this string to convey the DSP
221          * architecture used)
222          */
223         @NonNull
getSupportedModelArch()224         public String getSupportedModelArch() {
225             return mSupportedModelArch;
226         }
227 
228         /** Maximum number of active sound models */
getMaxSoundModels()229         public int getMaxSoundModels() {
230             return mMaxSoundModels;
231         }
232 
233         /** Maximum number of key phrases */
getMaxKeyphrases()234         public int getMaxKeyphrases() {
235             return mMaxKeyphrases;
236         }
237 
238         /** Maximum number of users per key phrase */
getMaxUsers()239         public int getMaxUsers() {
240             return mMaxUsers;
241         }
242 
243         /** Supported recognition modes (bit field, RECOGNITION_MODE_VOICE_TRIGGER ...) */
244         @RecognitionModes
getRecognitionModes()245         public int getRecognitionModes() {
246             return mRecognitionModes;
247         }
248 
249         /** Supports seamless transition to capture mode after recognition */
isCaptureTransitionSupported()250         public boolean isCaptureTransitionSupported() {
251             return mSupportsCaptureTransition;
252         }
253 
254         /** Maximum buffering capacity in ms if supportsCaptureTransition() is true */
getMaxBufferMillis()255         public int getMaxBufferMillis() {
256             return mMaxBufferMillis;
257         }
258 
259         /** Supports capture by other use cases while detection is active */
isConcurrentCaptureSupported()260         public boolean isConcurrentCaptureSupported() {
261             return mSupportsConcurrentCapture;
262         }
263 
264         /** Rated power consumption when detection is active with TDB silence/sound/speech ratio */
getPowerConsumptionMw()265         public int getPowerConsumptionMw() {
266             return mPowerConsumptionMw;
267         }
268 
269         /** Returns the trigger (key phrase) capture in the binary data of the
270          * recognition callback event */
isTriggerReturnedInEvent()271         public boolean isTriggerReturnedInEvent() {
272             return mReturnsTriggerInEvent;
273         }
274 
275         /**
276          * Bit field encoding of the AudioCapabilities
277          * supported by the firmware.
278          */
279         @AudioCapabilities
getAudioCapabilities()280         public int getAudioCapabilities() {
281             return mAudioCapabilities;
282         }
283 
284         public static final @android.annotation.NonNull Parcelable.Creator<ModuleProperties> CREATOR
285                 = new Parcelable.Creator<ModuleProperties>() {
286             public ModuleProperties createFromParcel(Parcel in) {
287                 return ModuleProperties.fromParcel(in);
288             }
289 
290             public ModuleProperties[] newArray(int size) {
291                 return new ModuleProperties[size];
292             }
293         };
294 
fromParcel(Parcel in)295         private static ModuleProperties fromParcel(Parcel in) {
296             int id = in.readInt();
297             String implementor = in.readString();
298             String description = in.readString();
299             String uuid = in.readString();
300             int version = in.readInt();
301             String supportedModelArch = in.readString();
302             int maxSoundModels = in.readInt();
303             int maxKeyphrases = in.readInt();
304             int maxUsers = in.readInt();
305             int recognitionModes = in.readInt();
306             boolean supportsCaptureTransition = in.readByte() == 1;
307             int maxBufferMs = in.readInt();
308             boolean supportsConcurrentCapture = in.readByte() == 1;
309             int powerConsumptionMw = in.readInt();
310             boolean returnsTriggerInEvent = in.readByte() == 1;
311             int audioCapabilities = in.readInt();
312             return new ModuleProperties(id, implementor, description, uuid, version,
313                     supportedModelArch, maxSoundModels, maxKeyphrases, maxUsers, recognitionModes,
314                     supportsCaptureTransition, maxBufferMs, supportsConcurrentCapture,
315                     powerConsumptionMw, returnsTriggerInEvent, audioCapabilities);
316         }
317 
318         @Override
writeToParcel(@uppressLint"MissingNullability") Parcel dest, int flags)319         public void writeToParcel(@SuppressLint("MissingNullability") Parcel dest, int flags) {
320             dest.writeInt(getId());
321             dest.writeString(getImplementor());
322             dest.writeString(getDescription());
323             dest.writeString(getUuid().toString());
324             dest.writeInt(getVersion());
325             dest.writeString(getSupportedModelArch());
326             dest.writeInt(getMaxSoundModels());
327             dest.writeInt(getMaxKeyphrases());
328             dest.writeInt(getMaxUsers());
329             dest.writeInt(getRecognitionModes());
330             dest.writeByte((byte) (isCaptureTransitionSupported() ? 1 : 0));
331             dest.writeInt(getMaxBufferMillis());
332             dest.writeByte((byte) (isConcurrentCaptureSupported() ? 1 : 0));
333             dest.writeInt(getPowerConsumptionMw());
334             dest.writeByte((byte) (isTriggerReturnedInEvent() ? 1 : 0));
335             dest.writeInt(getAudioCapabilities());
336         }
337 
338         @Override
describeContents()339         public int describeContents() {
340             return 0;
341         }
342 
343         @Override
equals(@ullable Object obj)344         public boolean equals(@Nullable Object obj) {
345             if (this == obj) {
346                 return true;
347             }
348             if (obj == null) {
349                 return false;
350             }
351             if (!(obj instanceof ModuleProperties)) {
352                 return false;
353             }
354             ModuleProperties other = (ModuleProperties) obj;
355             if (mId != other.mId) {
356                 return false;
357             }
358             if (!mImplementor.equals(other.mImplementor)) {
359                 return false;
360             }
361             if (!mDescription.equals(other.mDescription)) {
362                 return false;
363             }
364             if (!mUuid.equals(other.mUuid)) {
365                 return false;
366             }
367             if (mVersion != other.mVersion) {
368                 return false;
369             }
370             if (!mSupportedModelArch.equals(other.mSupportedModelArch)) {
371                 return false;
372             }
373             if (mMaxSoundModels != other.mMaxSoundModels) {
374                 return false;
375             }
376             if (mMaxKeyphrases != other.mMaxKeyphrases) {
377                 return false;
378             }
379             if (mMaxUsers != other.mMaxUsers) {
380                 return false;
381             }
382             if (mRecognitionModes != other.mRecognitionModes) {
383                 return false;
384             }
385             if (mSupportsCaptureTransition != other.mSupportsCaptureTransition) {
386                 return false;
387             }
388             if (mMaxBufferMillis != other.mMaxBufferMillis) {
389                 return false;
390             }
391             if (mSupportsConcurrentCapture != other.mSupportsConcurrentCapture) {
392                 return false;
393             }
394             if (mPowerConsumptionMw != other.mPowerConsumptionMw) {
395                 return false;
396             }
397             if (mReturnsTriggerInEvent != other.mReturnsTriggerInEvent) {
398                 return false;
399             }
400             if (mAudioCapabilities != other.mAudioCapabilities) {
401                 return false;
402             }
403             return true;
404         }
405 
406         @Override
hashCode()407         public int hashCode() {
408             final int prime = 31;
409             int result = 1;
410             result = prime * result + mId;
411             result = prime * result + mImplementor.hashCode();
412             result = prime * result + mDescription.hashCode();
413             result = prime * result + mUuid.hashCode();
414             result = prime * result + mVersion;
415             result = prime * result + mSupportedModelArch.hashCode();
416             result = prime * result + mMaxSoundModels;
417             result = prime * result + mMaxKeyphrases;
418             result = prime * result + mMaxUsers;
419             result = prime * result + mRecognitionModes;
420             result = prime * result + (mSupportsCaptureTransition ? 1 : 0);
421             result = prime * result + mMaxBufferMillis;
422             result = prime * result + (mSupportsConcurrentCapture ? 1 : 0);
423             result = prime * result + mPowerConsumptionMw;
424             result = prime * result + (mReturnsTriggerInEvent ? 1 : 0);
425             result = prime * result + mAudioCapabilities;
426             return result;
427         }
428 
429         @Override
toString()430         public String toString() {
431             return "ModuleProperties [id=" + getId() + ", implementor=" + getImplementor()
432                     + ", description=" + getDescription() + ", uuid=" + getUuid()
433                     + ", version=" + getVersion() + " , supportedModelArch="
434                     + getSupportedModelArch() + ", maxSoundModels=" + getMaxSoundModels()
435                     + ", maxKeyphrases=" + getMaxKeyphrases() + ", maxUsers=" + getMaxUsers()
436                     + ", recognitionModes=" + getRecognitionModes() + ", supportsCaptureTransition="
437                     + isCaptureTransitionSupported() + ", maxBufferMs=" + getMaxBufferMillis()
438                     + ", supportsConcurrentCapture=" + isConcurrentCaptureSupported()
439                     + ", powerConsumptionMw=" + getPowerConsumptionMw()
440                     + ", returnsTriggerInEvent=" + isTriggerReturnedInEvent()
441                     + ", audioCapabilities=" + getAudioCapabilities() + "]";
442         }
443     }
444 
445     /**
446      * A SoundModel describes the attributes and contains the binary data used by the hardware
447      * implementation to detect a particular sound pattern.
448      * A specialized version {@link KeyphraseSoundModel} is defined for key phrase
449      * sound models.
450      */
451     public static class SoundModel {
452 
453         /** @hide */
454         @Retention(RetentionPolicy.SOURCE)
455         @IntDef({
456                 TYPE_GENERIC_SOUND,
457                 TYPE_KEYPHRASE,
458                 TYPE_UNKNOWN,
459         })
460         public @interface SoundModelType {}
461 
462         /**
463          * Undefined sound model type
464          * @hide
465          */
466         public static final int TYPE_UNKNOWN = -1;
467 
468         /** Keyphrase sound model */
469         public static final int TYPE_KEYPHRASE = 0;
470 
471         /**
472          * A generic sound model. Use this type only for non-keyphrase sound models such as
473          * ones that match a particular sound pattern.
474          */
475         public static final int TYPE_GENERIC_SOUND = 1;
476 
477         @NonNull
478         private final UUID mUuid;
479         @SoundModelType
480         private final int mType;
481         @NonNull
482         private final UUID mVendorUuid;
483         private final int mVersion;
484         @NonNull
485         private final byte[] mData;
486 
487         /** @hide */
SoundModel(@onNull UUID uuid, @Nullable UUID vendorUuid, @SoundModelType int type, @Nullable byte[] data, int version)488         public SoundModel(@NonNull UUID uuid, @Nullable UUID vendorUuid, @SoundModelType int type,
489                 @Nullable byte[] data, int version) {
490             this.mUuid = requireNonNull(uuid);
491             this.mVendorUuid = vendorUuid != null ? vendorUuid : new UUID(0, 0);
492             this.mType = type;
493             this.mVersion = version;
494             this.mData = data != null ? data : new byte[0];
495         }
496 
497         /** Unique sound model identifier */
498         @NonNull
getUuid()499         public UUID getUuid() {
500             return mUuid;
501         }
502 
503         /** Sound model type (e.g. TYPE_KEYPHRASE); */
504         @SoundModelType
getType()505         public int getType() {
506             return mType;
507         }
508 
509         /** Unique sound model vendor identifier */
510         @NonNull
getVendorUuid()511         public UUID getVendorUuid() {
512             return mVendorUuid;
513         }
514 
515         /** vendor specific version number of the model */
getVersion()516         public int getVersion() {
517             return mVersion;
518         }
519 
520         /** Opaque data. For use by vendor implementation and enrollment application */
521         @NonNull
getData()522         public byte[] getData() {
523             return mData;
524         }
525 
526         @Override
hashCode()527         public int hashCode() {
528             final int prime = 31;
529             int result = 1;
530             result = prime * result + getVersion();
531             result = prime * result + Arrays.hashCode(getData());
532             result = prime * result + getType();
533             result = prime * result + ((getUuid() == null) ? 0 : getUuid().hashCode());
534             result = prime * result + ((getVendorUuid() == null) ? 0 : getVendorUuid().hashCode());
535             return result;
536         }
537 
538         @Override
equals(@ullable Object obj)539         public boolean equals(@Nullable Object obj) {
540             if (this == obj) {
541                 return true;
542             }
543             if (obj == null) {
544                 return false;
545             }
546             if (!(obj instanceof SoundModel)) {
547                 return false;
548             }
549             SoundModel other = (SoundModel) obj;
550             if (getType() != other.getType()) {
551                 return false;
552             }
553             if (getUuid() == null) {
554                 if (other.getUuid() != null) {
555                     return false;
556                 }
557             } else if (!getUuid().equals(other.getUuid())) {
558                 return false;
559             }
560             if (getVendorUuid() == null) {
561                 if (other.getVendorUuid() != null) {
562                     return false;
563                 }
564             } else if (!getVendorUuid().equals(other.getVendorUuid())) {
565                 return false;
566             }
567             if (!Arrays.equals(getData(), other.getData())) {
568                 return false;
569             }
570             if (getVersion() != other.getVersion()) {
571                 return false;
572             }
573             return true;
574         }
575     }
576 
577     /**
578      * A Keyphrase describes a key phrase that can be detected by a
579      * {@link KeyphraseSoundModel}
580      */
581     public static final class Keyphrase implements Parcelable {
582 
583         private final int mId;
584         @RecognitionModes
585         private final int mRecognitionModes;
586         @NonNull
587         private final Locale mLocale;
588         @NonNull
589         private final String mText;
590         @NonNull
591         private final int[] mUsers;
592 
593         /**
594          * Constructor for Keyphrase describes a key phrase that can be detected by a
595          * {@link KeyphraseSoundModel}
596          *
597          * @param id Unique keyphrase identifier for this keyphrase
598          * @param recognitionModes Bit field representation of recognition modes this keyphrase
599          *                         supports
600          * @param locale Locale of the keyphrase
601          * @param text Key phrase text
602          * @param users Users this key phrase has been trained for.
603          */
Keyphrase(int id, @RecognitionModes int recognitionModes, @NonNull Locale locale, @NonNull String text, @Nullable int[] users)604         public Keyphrase(int id, @RecognitionModes int recognitionModes, @NonNull Locale locale,
605                 @NonNull String text, @Nullable int[] users) {
606             this.mId = id;
607             this.mRecognitionModes = recognitionModes;
608             this.mLocale = requireNonNull(locale);
609             this.mText = requireNonNull(text);
610             this.mUsers = users != null ? users : new int[0];
611         }
612 
613         /** Unique identifier for this keyphrase */
getId()614         public int getId() {
615             return mId;
616         }
617 
618         /**
619          * Recognition modes supported for this key phrase in the model
620          *
621          * @see #RECOGNITION_MODE_VOICE_TRIGGER
622          * @see #RECOGNITION_MODE_USER_IDENTIFICATION
623          * @see #RECOGNITION_MODE_USER_AUTHENTICATION
624          * @see #RECOGNITION_MODE_GENERIC
625          */
626         @RecognitionModes
getRecognitionModes()627         public int getRecognitionModes() {
628             return mRecognitionModes;
629         }
630 
631         /** Locale of the keyphrase. */
632         @NonNull
getLocale()633         public Locale getLocale() {
634             return mLocale;
635         }
636 
637         /** Key phrase text */
638         @NonNull
getText()639         public String getText() {
640             return mText;
641         }
642 
643         /**
644          * Users this key phrase has been trained for. countains sound trigger specific user IDs
645          * derived from system user IDs {@link android.os.UserHandle#getIdentifier()}.
646          */
647         @NonNull
getUsers()648         public int[] getUsers() {
649             return mUsers;
650         }
651 
652         public static final @NonNull Parcelable.Creator<Keyphrase> CREATOR =
653                 new Parcelable.Creator<Keyphrase>() {
654             @NonNull
655             public Keyphrase createFromParcel(@NonNull Parcel in) {
656                 return Keyphrase.readFromParcel(in);
657             }
658 
659             @NonNull
660             public Keyphrase[] newArray(int size) {
661                 return new Keyphrase[size];
662             }
663         };
664 
665         /**
666          * Read from Parcel to generate keyphrase
667          */
668         @NonNull
readFromParcel(@onNull Parcel in)669         public static Keyphrase readFromParcel(@NonNull Parcel in) {
670             int id = in.readInt();
671             int recognitionModes = in.readInt();
672             Locale locale = Locale.forLanguageTag(in.readString());
673             String text = in.readString();
674             int[] users = null;
675             int numUsers = in.readInt();
676             if (numUsers >= 0) {
677                 users = new int[numUsers];
678                 in.readIntArray(users);
679             }
680             return new Keyphrase(id, recognitionModes, locale, text, users);
681         }
682 
683         @Override
writeToParcel(@onNull Parcel dest, int flags)684         public void writeToParcel(@NonNull Parcel dest, int flags) {
685             dest.writeInt(getId());
686             dest.writeInt(getRecognitionModes());
687             dest.writeString(getLocale().toLanguageTag());
688             dest.writeString(getText());
689             if (getUsers() != null) {
690                 dest.writeInt(getUsers().length);
691                 dest.writeIntArray(getUsers());
692             } else {
693                 dest.writeInt(-1);
694             }
695         }
696 
697         /** @hide */
698         @Override
describeContents()699         public int describeContents() {
700             return 0;
701         }
702 
703         @Override
hashCode()704         public int hashCode() {
705             final int prime = 31;
706             int result = 1;
707             result = prime * result + ((getText() == null) ? 0 : getText().hashCode());
708             result = prime * result + getId();
709             result = prime * result + ((getLocale() == null) ? 0 : getLocale().hashCode());
710             result = prime * result + getRecognitionModes();
711             result = prime * result + Arrays.hashCode(getUsers());
712             return result;
713         }
714 
715         @Override
equals(@ullable Object obj)716         public boolean equals(@Nullable Object obj) {
717             if (this == obj) {
718                 return true;
719             }
720             if (obj == null) {
721                 return false;
722             }
723             if (getClass() != obj.getClass()) {
724                 return false;
725             }
726             Keyphrase other = (Keyphrase) obj;
727             if (getText() == null) {
728                 if (other.getText() != null) {
729                     return false;
730                 }
731             } else if (!getText().equals(other.getText())) {
732                 return false;
733             }
734             if (getId() != other.getId()) {
735                 return false;
736             }
737             if (getLocale() == null) {
738                 if (other.getLocale() != null) {
739                     return false;
740                 }
741             } else if (!getLocale().equals(other.getLocale())) {
742                 return false;
743             }
744             if (getRecognitionModes() != other.getRecognitionModes()) {
745                 return false;
746             }
747             if (!Arrays.equals(getUsers(), other.getUsers())) {
748                 return false;
749             }
750             return true;
751         }
752 
753         @Override
toString()754         public String toString() {
755             return "Keyphrase [id=" + getId() + ", recognitionModes=" + getRecognitionModes()
756                     + ", locale=" + getLocale().toLanguageTag() + ", text=" + getText()
757                     + ", users=" + Arrays.toString(getUsers()) + "]";
758         }
759     }
760 
761     /**
762      * A KeyphraseSoundModel is a specialized {@link SoundModel} for key phrases.
763      * It contains data needed by the hardware to detect a certain number of key phrases
764      * and the list of corresponding {@link Keyphrase} descriptors.
765      */
766     public static final class KeyphraseSoundModel extends SoundModel implements Parcelable {
767 
768         @NonNull
769         private final Keyphrase[] mKeyphrases;
770 
KeyphraseSoundModel( @onNull UUID uuid, @NonNull UUID vendorUuid, @Nullable byte[] data, @Nullable Keyphrase[] keyphrases, int version)771         public KeyphraseSoundModel(
772                 @NonNull UUID uuid, @NonNull UUID vendorUuid, @Nullable byte[] data,
773                 @Nullable Keyphrase[] keyphrases, int version) {
774             super(uuid, vendorUuid, TYPE_KEYPHRASE, data, version);
775             this.mKeyphrases = keyphrases != null ? keyphrases : new Keyphrase[0];
776         }
777 
KeyphraseSoundModel(@onNull UUID uuid, @NonNull UUID vendorUuid, @Nullable byte[] data, @Nullable Keyphrase[] keyphrases)778         public KeyphraseSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid,
779                 @Nullable byte[] data, @Nullable Keyphrase[] keyphrases) {
780             this(uuid, vendorUuid, data, keyphrases, -1);
781         }
782 
783         /** Key phrases in this sound model */
784         @NonNull
getKeyphrases()785         public Keyphrase[] getKeyphrases() {
786             return mKeyphrases;
787         }
788 
789         public static final @NonNull Parcelable.Creator<KeyphraseSoundModel> CREATOR =
790                 new Parcelable.Creator<KeyphraseSoundModel>() {
791             @NonNull
792             public KeyphraseSoundModel createFromParcel(@NonNull Parcel in) {
793                 return KeyphraseSoundModel.readFromParcel(in);
794             }
795 
796             @NonNull
797             public KeyphraseSoundModel[] newArray(int size) {
798                 return new KeyphraseSoundModel[size];
799             }
800         };
801 
802         /**
803          * Read from Parcel to generate KeyphraseSoundModel
804          */
805         @NonNull
readFromParcel(@onNull Parcel in)806         public static KeyphraseSoundModel readFromParcel(@NonNull Parcel in) {
807             UUID uuid = UUID.fromString(in.readString());
808             UUID vendorUuid = null;
809             int length = in.readInt();
810             if (length >= 0) {
811                 vendorUuid = UUID.fromString(in.readString());
812             }
813             int version = in.readInt();
814             byte[] data = in.readBlob();
815             Keyphrase[] keyphrases = in.createTypedArray(Keyphrase.CREATOR);
816             return new KeyphraseSoundModel(uuid, vendorUuid, data, keyphrases, version);
817         }
818 
819         /** @hide */
820         @Override
describeContents()821         public int describeContents() {
822             return 0;
823         }
824 
825         @Override
writeToParcel(@onNull Parcel dest, int flags)826         public void writeToParcel(@NonNull Parcel dest, int flags) {
827             dest.writeString(getUuid().toString());
828             if (getVendorUuid() == null) {
829                 dest.writeInt(-1);
830             } else {
831                 dest.writeInt(getVendorUuid().toString().length());
832                 dest.writeString(getVendorUuid().toString());
833             }
834             dest.writeInt(getVersion());
835             dest.writeBlob(getData());
836             dest.writeTypedArray(getKeyphrases(), flags);
837         }
838 
839         @Override
toString()840         public String toString() {
841             return "KeyphraseSoundModel [keyphrases=" + Arrays.toString(getKeyphrases())
842                     + ", uuid=" + getUuid() + ", vendorUuid=" + getVendorUuid()
843                     + ", type=" + getType()
844                     + ", data=" + (getData() == null ? 0 : getData().length)
845                     + ", version=" + getVersion() + "]";
846         }
847 
848         @Override
hashCode()849         public int hashCode() {
850             final int prime = 31;
851             int result = super.hashCode();
852             result = prime * result + Arrays.hashCode(getKeyphrases());
853             return result;
854         }
855 
856         @Override
equals(@ullable Object obj)857         public boolean equals(@Nullable Object obj) {
858             if (this == obj) {
859                 return true;
860             }
861             if (!super.equals(obj)) {
862                 return false;
863             }
864             if (!(obj instanceof KeyphraseSoundModel)) {
865                 return false;
866             }
867             KeyphraseSoundModel other = (KeyphraseSoundModel) obj;
868             if (!Arrays.equals(getKeyphrases(), other.getKeyphrases())) {
869                 return false;
870             }
871             return true;
872         }
873     }
874 
875 
876     /*****************************************************************************
877      * A GenericSoundModel is a specialized {@link SoundModel} for non-voice sound
878      * patterns.
879      ****************************************************************************/
880     @FlaggedApi(android.media.soundtrigger.Flags.FLAG_GENERIC_MODEL_API)
881     public static final class GenericSoundModel extends SoundModel implements Parcelable {
882 
883         public static final @android.annotation.NonNull Parcelable.Creator<GenericSoundModel> CREATOR
884                 = new Parcelable.Creator<GenericSoundModel>() {
885             public GenericSoundModel createFromParcel(Parcel in) {
886                 return GenericSoundModel.fromParcel(in);
887             }
888 
889             public GenericSoundModel[] newArray(int size) {
890                 return new GenericSoundModel[size];
891             }
892         };
893 
894         /**
895          * Constructor for {@link GenericSoundModel} with version.
896          *
897          * @param uuid Unique identifier for this sound model.
898          * @param vendorUuid Unique vendor identifier for this sound model.
899          * @param data Opaque data for this sound model.
900          * @param version Vendor-specific version number of this sound model.
901          */
GenericSoundModel(@onNull UUID uuid, @NonNull UUID vendorUuid, @Nullable byte[] data, int version)902         public GenericSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid,
903                 @Nullable byte[] data, int version) {
904             super(uuid, vendorUuid, TYPE_GENERIC_SOUND, data, version);
905         }
906 
907         /**
908          * Constructor for {@link GenericSoundModel} without version. The version is set to -1.
909          *
910          * @param uuid Unique identifier for this sound model.
911          * @param vendorUuid Unique vendor identifier for this sound model.
912          * @param data Opaque data for this sound model.
913          */
914         @UnsupportedAppUsage
GenericSoundModel(@onNull UUID uuid, @NonNull UUID vendorUuid, @Nullable byte[] data)915         public GenericSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid,
916                 @Nullable byte[] data) {
917             this(uuid, vendorUuid, data, -1);
918         }
919 
920         @Override
describeContents()921         public int describeContents() {
922             return 0;
923         }
924 
fromParcel(Parcel in)925         private static GenericSoundModel fromParcel(Parcel in) {
926             UUID uuid = UUID.fromString(in.readString());
927             UUID vendorUuid = null;
928             int length = in.readInt();
929             if (length >= 0) {
930                 vendorUuid = UUID.fromString(in.readString());
931             }
932             byte[] data = in.readBlob();
933             int version = in.readInt();
934             return new GenericSoundModel(uuid, vendorUuid, data, version);
935         }
936 
937         @Override
writeToParcel(@onNull Parcel dest, int flags)938         public void writeToParcel(@NonNull Parcel dest, int flags) {
939             dest.writeString(getUuid().toString());
940             if (getVendorUuid() == null) {
941                 dest.writeInt(-1);
942             } else {
943                 dest.writeInt(getVendorUuid().toString().length());
944                 dest.writeString(getVendorUuid().toString());
945             }
946             dest.writeBlob(getData());
947             dest.writeInt(getVersion());
948         }
949 
950         @Override
toString()951         public String toString() {
952             return "GenericSoundModel [uuid=" + getUuid() + ", vendorUuid=" + getVendorUuid()
953                     + ", type=" + getType()
954                     + ", data=" + (getData() == null ? 0 : getData().length)
955                     + ", version=" + getVersion() + "]";
956         }
957     }
958 
959     /**
960      * A ModelParamRange is a representation of supported parameter range for a
961      * given loaded model.
962      */
963     public static final class ModelParamRange implements Parcelable {
964 
965         /**
966          * The inclusive start of supported range.
967          */
968         private final int mStart;
969 
970         /**
971          * The inclusive end of supported range.
972          */
973         private final int mEnd;
974 
975         /** @hide */
976         @TestApi
ModelParamRange(int start, int end)977         public ModelParamRange(int start, int end) {
978             this.mStart = start;
979             this.mEnd = end;
980         }
981 
982         /** @hide */
ModelParamRange(@onNull Parcel in)983         private ModelParamRange(@NonNull Parcel in) {
984             this.mStart = in.readInt();
985             this.mEnd = in.readInt();
986         }
987 
988         /**
989          * Get the beginning of the param range
990          *
991          * @return The inclusive start of the supported range.
992          */
getStart()993         public int getStart() {
994             return mStart;
995         }
996 
997         /**
998          * Get the end of the param range
999          *
1000          * @return The inclusive end of the supported range.
1001          */
getEnd()1002         public int getEnd() {
1003             return mEnd;
1004         }
1005 
1006         @NonNull
1007         public static final Creator<ModelParamRange> CREATOR =
1008                 new Creator<ModelParamRange>() {
1009                     @Override
1010                     @NonNull
1011                     public ModelParamRange createFromParcel(@NonNull Parcel in) {
1012                         return new ModelParamRange(in);
1013                     }
1014 
1015                     @Override
1016                     @NonNull
1017                     public ModelParamRange[] newArray(int size) {
1018                         return new ModelParamRange[size];
1019                     }
1020                 };
1021 
1022         /** @hide */
1023         @Override
describeContents()1024         public int describeContents() {
1025             return 0;
1026         }
1027 
1028         /** @hide */
1029         @Override
hashCode()1030         public int hashCode() {
1031             final int prime = 31;
1032             int result = 1;
1033             result = prime * result + (mStart);
1034             result = prime * result + (mEnd);
1035             return result;
1036         }
1037 
1038         @Override
equals(@ullable Object obj)1039         public boolean equals(@Nullable Object obj) {
1040             if (this == obj) {
1041                 return true;
1042             }
1043             if (obj == null) {
1044                 return false;
1045             }
1046             if (getClass() != obj.getClass()) {
1047                 return false;
1048             }
1049             ModelParamRange other = (ModelParamRange) obj;
1050             if (mStart != other.mStart) {
1051                 return false;
1052             }
1053             if (mEnd != other.mEnd) {
1054                 return false;
1055             }
1056             return true;
1057         }
1058 
1059         @Override
writeToParcel(@onNull Parcel dest, int flags)1060         public void writeToParcel(@NonNull Parcel dest, int flags) {
1061             dest.writeInt(mStart);
1062             dest.writeInt(mEnd);
1063         }
1064 
1065         @Override
1066         @NonNull
toString()1067         public String toString() {
1068             return "ModelParamRange [start=" + mStart + ", end=" + mEnd + "]";
1069         }
1070     }
1071     /**
1072      * SoundTrigger model parameter types.
1073      * @hide
1074      */
1075     @Retention(RetentionPolicy.SOURCE)
1076     @IntDef(flag = true, prefix = { "MODEL_PARAM" }, value = {
1077             MODEL_PARAM_INVALID,
1078             MODEL_PARAM_THRESHOLD_FACTOR
1079     })
1080     public @interface ModelParamTypes {}
1081 
1082     /**
1083      * See {@link ModelParams.INVALID}
1084      * @hide
1085      */
1086     @TestApi
1087     public static final int MODEL_PARAM_INVALID = ModelParams.INVALID;
1088     /**
1089      * See {@link ModelParams.THRESHOLD_FACTOR}
1090      * @hide
1091      */
1092     @TestApi
1093     public static final int MODEL_PARAM_THRESHOLD_FACTOR = ModelParams.THRESHOLD_FACTOR;
1094 
1095     /**
1096      * Modes for key phrase recognition
1097      * @hide
1098      */
1099     @Retention(RetentionPolicy.SOURCE)
1100     @IntDef(flag = true, prefix = { "RECOGNITION_MODE_" }, value = {
1101             RECOGNITION_MODE_VOICE_TRIGGER,
1102             RECOGNITION_MODE_USER_IDENTIFICATION,
1103             RECOGNITION_MODE_USER_AUTHENTICATION,
1104             RECOGNITION_MODE_GENERIC
1105     })
1106     public @interface RecognitionModes {}
1107 
1108     /**
1109      * Trigger on recognition of a key phrase
1110      */
1111     public static final int RECOGNITION_MODE_VOICE_TRIGGER = 0x1;
1112     /**
1113      * Trigger only if one user is identified
1114      */
1115     public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 0x2;
1116     /**
1117      * Trigger only if one user is authenticated
1118      */
1119     public static final int RECOGNITION_MODE_USER_AUTHENTICATION = 0x4;
1120     /**
1121      * Generic (non-speech) recognition.
1122      */
1123     public static final int RECOGNITION_MODE_GENERIC = 0x8;
1124 
1125     /**
1126      *  Status codes for {@link RecognitionEvent}
1127      */
1128     /**
1129      * Recognition success
1130      *
1131      * @hide
1132      */
1133     public static final int RECOGNITION_STATUS_SUCCESS = 0;
1134     /**
1135      * Recognition aborted (e.g. capture preempted by anotehr use case
1136      *
1137      * @hide
1138      */
1139     public static final int RECOGNITION_STATUS_ABORT = 1;
1140     /**
1141      * Recognition failure
1142      *
1143      * @hide
1144      */
1145     public static final int RECOGNITION_STATUS_FAILURE = 2;
1146     /**
1147      * Recognition event was triggered by a getModelState request, not by the
1148      * DSP.
1149      *
1150      * @hide
1151      */
1152     public static final int RECOGNITION_STATUS_GET_STATE_RESPONSE = 3;
1153 
1154     /**
1155      *  A RecognitionEvent is provided by the
1156      *  {@code StatusListener#onRecognition(RecognitionEvent)}
1157      *  callback upon recognition success or failure.
1158      */
1159     public static class RecognitionEvent {
1160         /**
1161          * Recognition status e.g RECOGNITION_STATUS_SUCCESS
1162          *
1163          * @hide
1164          */
1165         @UnsupportedAppUsage
1166         public final int status;
1167         /**
1168          *
1169          * Sound Model corresponding to this event callback
1170          *
1171          * @hide
1172          */
1173         @UnsupportedAppUsage
1174         public final int soundModelHandle;
1175         /**
1176          * True if it is possible to capture audio from this utterance buffered by the hardware
1177          *
1178          * @hide
1179          */
1180         @UnsupportedAppUsage
1181         public final boolean captureAvailable;
1182         /**
1183          * Audio session ID to be used when capturing the utterance with an AudioRecord
1184          * if captureAvailable() is true.
1185          *
1186          * @hide
1187          */
1188         @UnsupportedAppUsage
1189         public final int captureSession;
1190         /**
1191          * Delay in ms between end of model detection and start of audio available for capture.
1192          * A negative value is possible (e.g. if keyphrase is also available for capture)
1193          *
1194          * @hide
1195          */
1196         public final int captureDelayMs;
1197         /**
1198          * Duration in ms of audio captured before the start of the trigger. 0 if none.
1199          *
1200          * @hide
1201          */
1202         public final int capturePreambleMs;
1203         /**
1204          * True if  the trigger (key phrase capture is present in binary data
1205          *
1206          * @hide
1207          */
1208         public final boolean triggerInData;
1209         /**
1210          * Audio format of either the trigger in event data or to use for capture of the
1211          * rest of the utterance
1212          *
1213          * @hide
1214          */
1215         @NonNull
1216         public final AudioFormat captureFormat;
1217         /**
1218          * Opaque data for use by system applications who know about voice engine internals,
1219          * typically during enrollment.
1220          *
1221          * @hide
1222          */
1223         @UnsupportedAppUsage
1224         @NonNull
1225         public final byte[] data;
1226         /**
1227          * Is recognition still active after this event.
1228          * @hide
1229          */
1230         public final boolean recognitionStillActive;
1231         /**
1232          * Timestamp of when the trigger event from SoundTriggerHal was received by the
1233          * framework.
1234          *
1235          * <p>Clock monotonic including suspend time or its equivalent on the system,
1236          * in the same units and timebase as {@link SystemClock#elapsedRealtime()}.
1237          *
1238          * <p>Value represents elapsed realtime in milliseconds when the event was received from the
1239          * HAL. The value will be -1 if the event was not generated from the HAL.
1240          *
1241          * @hide
1242          */
1243         @ElapsedRealtimeLong
1244         public final long halEventReceivedMillis;
1245 
1246         /**
1247          * Binder token returned by {@link SoundTriggerModule#startRecognitionWithToken(
1248          * int soundModelHandle, SoundTrigger.RecognitionConfig config)}
1249          * @hide
1250          */
1251         public final IBinder token;
1252 
1253 
1254         /** @hide */
1255         @TestApi
1256         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable, int captureSession, int captureDelayMs, int capturePreambleMs, boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data, @ElapsedRealtimeLong long halEventReceivedMillis)1257         public RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
1258                 int captureSession, int captureDelayMs, int capturePreambleMs,
1259                 boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data,
1260                 @ElapsedRealtimeLong long halEventReceivedMillis) {
1261             this(status, soundModelHandle, captureAvailable,
1262                     captureSession, captureDelayMs, capturePreambleMs, triggerInData, captureFormat,
1263                     data, status == RECOGNITION_STATUS_GET_STATE_RESPONSE, halEventReceivedMillis,
1264                     null);
1265         }
1266 
1267         /** @hide */
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, IBinder token)1268         public RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
1269                 int captureSession, int captureDelayMs, int capturePreambleMs,
1270                 boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data,
1271                 boolean recognitionStillActive, @ElapsedRealtimeLong long halEventReceivedMillis,
1272                 IBinder token) {
1273             this.status = status;
1274             this.soundModelHandle = soundModelHandle;
1275             this.captureAvailable = captureAvailable;
1276             this.captureSession = captureSession;
1277             this.captureDelayMs = captureDelayMs;
1278             this.capturePreambleMs = capturePreambleMs;
1279             this.triggerInData = triggerInData;
1280             this.captureFormat = requireNonNull(captureFormat);
1281             this.data = data != null ? data : new byte[0];
1282             this.recognitionStillActive = recognitionStillActive;
1283             this.halEventReceivedMillis = halEventReceivedMillis;
1284             this.token = token;
1285         }
1286 
1287         /**
1288          * Check if is possible to capture audio from this utterance buffered by the hardware.
1289          *
1290          * @return {@code true} iff a capturing is possible
1291          */
isCaptureAvailable()1292         public boolean isCaptureAvailable() {
1293             return captureAvailable;
1294         }
1295 
1296         /**
1297          * Get the audio format of either the trigger in event data or to use for capture of the
1298          * rest of the utterance
1299          *
1300          * @return the audio format
1301          */
getCaptureFormat()1302         @Nullable public AudioFormat getCaptureFormat() {
1303             return captureFormat;
1304         }
1305 
1306         /**
1307          * Get Audio session ID to be used when capturing the utterance with an {@link AudioRecord}
1308          * if {@link #isCaptureAvailable()} is true.
1309          *
1310          * @return The id of the capture session
1311          */
getCaptureSession()1312         public int getCaptureSession() {
1313             return captureSession;
1314         }
1315 
1316         /**
1317          * Get the opaque data for use by system applications who know about voice engine
1318          * internals, typically during enrollment.
1319          *
1320          * @return The data of the event
1321          */
1322         @SuppressLint("MissingNullability")
getData()1323         public byte[] getData() {
1324             return data;
1325         }
1326 
1327         /**
1328          * Timestamp of when the trigger event from SoundTriggerHal was received by the
1329          * framework.
1330          *
1331          * Clock monotonic including suspend time or its equivalent on the system,
1332          * in the same units and timebase as {@link SystemClock#elapsedRealtime()}.
1333          *
1334          * @return Elapsed realtime in milliseconds when the event was received from the HAL.
1335          *      Returns -1 if the event was not generated from the HAL.
1336          */
1337         @ElapsedRealtimeLong
getHalEventReceivedMillis()1338         public long getHalEventReceivedMillis() {
1339             return halEventReceivedMillis;
1340         }
1341 
1342         /**
1343          * Get token associated with this recognition session returned by
1344          *{@link SoundTriggerModule#startRecognitionWithToken(
1345          * int soundModelHandle, SoundTrigger.RecognitionConfig config)}
1346          * @hide
1347          */
getToken()1348         public IBinder getToken() {
1349             return token;
1350         }
1351 
1352         /** @hide */
1353         public static final @android.annotation.NonNull Parcelable.Creator<RecognitionEvent> CREATOR
1354                 = new Parcelable.Creator<RecognitionEvent>() {
1355             public RecognitionEvent createFromParcel(Parcel in) {
1356                 return RecognitionEvent.fromParcel(in);
1357             }
1358 
1359             public RecognitionEvent[] newArray(int size) {
1360                 return new RecognitionEvent[size];
1361             }
1362         };
1363 
1364         /** @hide */
fromParcel(Parcel in)1365         protected static RecognitionEvent fromParcel(Parcel in) {
1366             int status = in.readInt();
1367             int soundModelHandle = in.readInt();
1368             boolean captureAvailable = in.readByte() == 1;
1369             int captureSession = in.readInt();
1370             int captureDelayMs = in.readInt();
1371             int capturePreambleMs = in.readInt();
1372             boolean triggerInData = in.readByte() == 1;
1373             AudioFormat captureFormat = null;
1374             if (in.readByte() == 1) {
1375                 int sampleRate = in.readInt();
1376                 int encoding = in.readInt();
1377                 int channelMask = in.readInt();
1378                 captureFormat = (new AudioFormat.Builder())
1379                         .setChannelMask(channelMask)
1380                         .setEncoding(encoding)
1381                         .setSampleRate(sampleRate)
1382                         .build();
1383             }
1384             byte[] data = in.readBlob();
1385             boolean recognitionStillActive = in.readBoolean();
1386             long halEventReceivedMillis = in.readLong();
1387             IBinder token = in.readStrongBinder();
1388             return new RecognitionEvent(status, soundModelHandle, captureAvailable, captureSession,
1389                     captureDelayMs, capturePreambleMs, triggerInData, captureFormat, data,
1390                     recognitionStillActive, halEventReceivedMillis, token);
1391         }
1392 
1393         /** @hide */
describeContents()1394         public int describeContents() {
1395             return 0;
1396         }
1397 
1398         /** @hide */
writeToParcel(Parcel dest, int flags)1399         public void writeToParcel(Parcel dest, int flags) {
1400             dest.writeInt(status);
1401             dest.writeInt(soundModelHandle);
1402             dest.writeByte((byte) (captureAvailable ? 1 : 0));
1403             dest.writeInt(captureSession);
1404             dest.writeInt(captureDelayMs);
1405             dest.writeInt(capturePreambleMs);
1406             dest.writeByte((byte) (triggerInData ? 1 : 0));
1407             if (captureFormat != null) {
1408                 dest.writeByte((byte)1);
1409                 dest.writeInt(captureFormat.getSampleRate());
1410                 dest.writeInt(captureFormat.getEncoding());
1411                 dest.writeInt(captureFormat.getChannelMask());
1412             } else {
1413                 dest.writeByte((byte)0);
1414             }
1415             dest.writeBlob(data);
1416             dest.writeBoolean(recognitionStillActive);
1417             dest.writeLong(halEventReceivedMillis);
1418             dest.writeStrongBinder(token);
1419         }
1420         @Override
hashCode()1421         public int hashCode() {
1422             final int prime = 31;
1423             int result = 1;
1424             result = prime * result + (captureAvailable ? 1231 : 1237);
1425             result = prime * result + captureDelayMs;
1426             result = prime * result + capturePreambleMs;
1427             result = prime * result + captureSession;
1428             result = prime * result + (triggerInData ? 1231 : 1237);
1429             if (captureFormat != null) {
1430                 result = prime * result + captureFormat.getSampleRate();
1431                 result = prime * result + captureFormat.getEncoding();
1432                 result = prime * result + captureFormat.getChannelMask();
1433             }
1434             result = prime * result + Arrays.hashCode(data);
1435             result = prime * result + soundModelHandle;
1436             result = prime * result + status;
1437             result = result + (recognitionStillActive ? 1289 : 1291);
1438             result = prime * result + Long.hashCode(halEventReceivedMillis);
1439             result = prime * result +  Objects.hashCode(token);
1440             return result;
1441         }
1442 
1443         @Override
equals(@ullable Object obj)1444         public boolean equals(@Nullable Object obj) {
1445             if (this == obj)
1446                 return true;
1447             if (obj == null)
1448                 return false;
1449             if (getClass() != obj.getClass())
1450                 return false;
1451             RecognitionEvent other = (RecognitionEvent) obj;
1452             if (captureAvailable != other.captureAvailable)
1453                 return false;
1454             if (captureDelayMs != other.captureDelayMs)
1455                 return false;
1456             if (capturePreambleMs != other.capturePreambleMs)
1457                 return false;
1458             if (captureSession != other.captureSession)
1459                 return false;
1460             if (!Arrays.equals(data, other.data))
1461                 return false;
1462             if (recognitionStillActive != other.recognitionStillActive)
1463                 return false;
1464             if (soundModelHandle != other.soundModelHandle)
1465                 return false;
1466             if (halEventReceivedMillis != other.halEventReceivedMillis) {
1467                 return false;
1468             }
1469             if (!Objects.equals(token, other.token)) {
1470                 return false;
1471             }
1472             if (status != other.status)
1473                 return false;
1474             if (triggerInData != other.triggerInData)
1475                 return false;
1476             if (captureFormat == null) {
1477                 if (other.captureFormat != null)
1478                     return false;
1479             } else {
1480                 if (other.captureFormat == null)
1481                     return false;
1482                 if (captureFormat.getSampleRate() != other.captureFormat.getSampleRate())
1483                     return false;
1484                 if (captureFormat.getEncoding() != other.captureFormat.getEncoding())
1485                     return false;
1486                 if (captureFormat.getChannelMask() != other.captureFormat.getChannelMask())
1487                     return false;
1488             }
1489             return true;
1490         }
1491 
1492         @NonNull
1493         @Override
toString()1494         public String toString() {
1495             return "RecognitionEvent [status=" + status + ", soundModelHandle=" + soundModelHandle
1496                     + ", captureAvailable=" + captureAvailable + ", captureSession="
1497                     + captureSession + ", captureDelayMs=" + captureDelayMs
1498                     + ", capturePreambleMs=" + capturePreambleMs
1499                     + ", triggerInData=" + triggerInData
1500                     + ((captureFormat == null) ? "" :
1501                         (", sampleRate=" + captureFormat.getSampleRate()))
1502                     + ((captureFormat == null) ? "" :
1503                         (", encoding=" + captureFormat.getEncoding()))
1504                     + ((captureFormat == null) ? "" :
1505                         (", channelMask=" + captureFormat.getChannelMask()))
1506                     + ", data=" + (data == null ? 0 : data.length)
1507                     + ", recognitionStillActive=" + recognitionStillActive
1508                     + ", halEventReceivedMillis=" + halEventReceivedMillis
1509                     + ", token=" + token
1510                     + "]"; }
1511     }
1512 
1513     /**
1514      *  A RecognitionConfig is provided to
1515      *  {@link SoundTriggerModule#startRecognition(int, RecognitionConfig)} to configure the
1516      *  recognition request.
1517      */
1518     @FlaggedApi(android.media.soundtrigger.Flags.FLAG_MANAGER_API)
1519     public static final class RecognitionConfig implements Parcelable {
1520         private final boolean mCaptureRequested;
1521         private final boolean mMultipleTriggersAllowed;
1522         private final KeyphraseRecognitionExtra mKeyphrases[];
1523         private final byte[] mData;
1524         private final @ModuleProperties.AudioCapabilities int mAudioCapabilities;
1525 
1526         /**
1527          * Constructor for {@link RecognitionConfig} with {@code audioCapabilities} describes a
1528          * config that can be used by
1529          * {@link SoundTriggerModule#startRecognition(int, RecognitionConfig)}
1530          *
1531          * @param captureRequested Whether the DSP should capture the trigger sound.
1532          * @param multipleTriggersAllowed Whether the service should restart listening after the DSP
1533          *                              triggers.
1534          * @param keyphrases List of keyphrases in the sound model.
1535          * @param data Opaque data for use by system applications who know about voice engine
1536          *             internals, typically during enrollment.
1537          * @param audioCapabilities Bit field encoding of the AudioCapabilities. See
1538          *                          {@link ModuleProperties.AudioCapabilities} for details.
1539          */
RecognitionConfig(boolean captureRequested, boolean multipleTriggersAllowed, @Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data, @ModuleProperties.AudioCapabilities int audioCapabilities)1540         private RecognitionConfig(boolean captureRequested, boolean multipleTriggersAllowed,
1541                 @Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data,
1542                 @ModuleProperties.AudioCapabilities int audioCapabilities) {
1543             this.mCaptureRequested = captureRequested;
1544             this.mMultipleTriggersAllowed = multipleTriggersAllowed;
1545             this.mKeyphrases = keyphrases != null ? keyphrases : new KeyphraseRecognitionExtra[0];
1546             this.mData = data != null ? data : new byte[0];
1547             this.mAudioCapabilities = audioCapabilities;
1548         }
1549 
1550         /**
1551          * Constructor for {@link RecognitionConfig} without audioCapabilities. The
1552          * audioCapabilities is set to 0.
1553          *
1554          * @deprecated Use {@link Builder} instead.
1555          * @param captureRequested Whether the DSP should capture the trigger sound.
1556          * @param multipleTriggersAllowed Whether the service should restart listening after the DSP
1557          *                              triggers.
1558          * @param keyphrases List of keyphrases in the sound model.
1559          * @param data Opaque data for use by system applications.
1560          *
1561          * @hide
1562          */
1563         @UnsupportedAppUsage
1564         @Deprecated
1565         @TestApi
RecognitionConfig(boolean captureRequested, boolean multipleTriggersAllowed, @Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data)1566         public RecognitionConfig(boolean captureRequested, boolean multipleTriggersAllowed,
1567                 @Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data) {
1568             this(captureRequested, multipleTriggersAllowed, keyphrases, data, 0);
1569         }
1570 
1571         public static final @android.annotation.NonNull Parcelable.Creator<RecognitionConfig> CREATOR
1572                 = new Parcelable.Creator<RecognitionConfig>() {
1573             public RecognitionConfig createFromParcel(Parcel in) {
1574                 return RecognitionConfig.fromParcel(in);
1575             }
1576 
1577             public RecognitionConfig[] newArray(int size) {
1578                 return new RecognitionConfig[size];
1579             }
1580         };
1581 
1582         /**
1583          * Returns whether the DSP should capture the trigger sound and make it available for
1584          * further capture.
1585          */
isCaptureRequested()1586         public boolean isCaptureRequested() {
1587             return mCaptureRequested;
1588         }
1589 
1590         /**
1591          * Returns whether the service should restart listening after the DSP triggers.
1592          *
1593          * <p><b>Note:</b> This config flag is currently used at the service layer rather than by
1594          * the DSP.
1595          */
isMultipleTriggersAllowed()1596         public boolean isMultipleTriggersAllowed() {
1597             return mMultipleTriggersAllowed;
1598         }
1599 
1600         /**
1601          * Gets all keyphrases in the sound model for which recognition should be performed with
1602          * options for each keyphrase.
1603          */
1604         @NonNull
getKeyphrases()1605         public List<KeyphraseRecognitionExtra> getKeyphrases() {
1606             return Arrays.asList(mKeyphrases);
1607         }
1608 
1609         /**
1610          * Opaque data.
1611          *
1612          * <p>For use by system applications who knows about voice engine internals, typically
1613          * during enrollment.
1614          */
1615         @NonNull
getData()1616         public byte[] getData() {
1617             return mData;
1618         }
1619 
1620         /**
1621          * Bit field encoding of the AudioCapabilities supported by the firmware. See
1622          * {@link ModuleProperties.AudioCapabilities} for details.
1623          */
getAudioCapabilities()1624         public @ModuleProperties.AudioCapabilities int getAudioCapabilities() {
1625             return mAudioCapabilities;
1626         }
1627 
fromParcel(Parcel in)1628         private static RecognitionConfig fromParcel(Parcel in) {
1629             boolean captureRequested = in.readBoolean();
1630             boolean multipleTriggersAllowed = in.readBoolean();
1631             KeyphraseRecognitionExtra[] keyphrases =
1632                     in.createTypedArray(KeyphraseRecognitionExtra.CREATOR);
1633             byte[] data = in.createByteArray();
1634             int audioCapabilities = in.readInt();
1635             return new RecognitionConfig(captureRequested, multipleTriggersAllowed, keyphrases,
1636                     data, audioCapabilities);
1637         }
1638 
1639         @Override
writeToParcel(@onNull Parcel dest, int flags)1640         public void writeToParcel(@NonNull Parcel dest, int flags) {
1641             dest.writeBoolean(mCaptureRequested);
1642             dest.writeBoolean(mMultipleTriggersAllowed);
1643             dest.writeTypedArray(mKeyphrases, flags);
1644             dest.writeByteArray(mData);
1645             dest.writeInt(mAudioCapabilities);
1646         }
1647 
1648         @Override
describeContents()1649         public int describeContents() {
1650             return 0;
1651         }
1652 
1653         @Override
toString()1654         public String toString() {
1655             return "RecognitionConfig [captureRequested=" + mCaptureRequested
1656                     + ", multipleTriggersAllowed=" + mMultipleTriggersAllowed + ", keyphrases="
1657                     + Arrays.toString(mKeyphrases) + ", data=" + Arrays.toString(mData)
1658                     + ", audioCapabilities=" + Integer.toHexString(mAudioCapabilities) + "]";
1659         }
1660 
1661         @Override
equals(Object obj)1662         public final boolean equals(Object obj) {
1663             if (this == obj)
1664                 return true;
1665             if (obj == null)
1666                 return false;
1667             if (!(obj instanceof RecognitionConfig))
1668                 return false;
1669             RecognitionConfig other = (RecognitionConfig) obj;
1670             if (mCaptureRequested != other.mCaptureRequested) {
1671                 return false;
1672             }
1673             if (mMultipleTriggersAllowed != other.mMultipleTriggersAllowed) {
1674                 return false;
1675             }
1676             if (!Arrays.equals(mKeyphrases, other.mKeyphrases)) {
1677                 return false;
1678             }
1679             if (!Arrays.equals(mData, other.mData)) {
1680                 return false;
1681             }
1682             if (mAudioCapabilities != other.mAudioCapabilities) {
1683                 return false;
1684             }
1685             return true;
1686         }
1687 
1688         @Override
hashCode()1689         public final int hashCode() {
1690             final int prime = 31;
1691             int result = 1;
1692             result = prime * result + (mCaptureRequested ? 1 : 0);
1693             result = prime * result + (mMultipleTriggersAllowed ? 1 : 0);
1694             result = prime * result + Arrays.hashCode(mKeyphrases);
1695             result = prime * result + Arrays.hashCode(mData);
1696             result = prime * result + mAudioCapabilities;
1697             return result;
1698         }
1699 
1700         /**
1701          * Builder class for {@link RecognitionConfig} objects.
1702          */
1703         public static final class Builder {
1704             private boolean mCaptureRequested;
1705             private boolean mMultipleTriggersAllowed;
1706             @Nullable private KeyphraseRecognitionExtra[] mKeyphrases;
1707             @Nullable private byte[] mData;
1708             private @ModuleProperties.AudioCapabilities int mAudioCapabilities;
1709 
1710             /**
1711              * Constructs a new Builder with the default values.
1712              */
Builder()1713             public Builder() {
1714             }
1715 
1716             /**
1717              * Sets capture requested state.
1718              * @param captureRequested Whether the DSP should capture the trigger sound.
1719              * @return the same Builder instance.
1720              */
setCaptureRequested(boolean captureRequested)1721             public @NonNull Builder setCaptureRequested(boolean captureRequested) {
1722                 mCaptureRequested = captureRequested;
1723                 return this;
1724             }
1725 
1726             /**
1727              * Sets allow multiple triggers state.
1728              * @param multipleTriggersAllowed Whether the service should restart listening after the
1729              *                              DSP triggers.
1730              * @return the same Builder instance.
1731              */
setMultipleTriggersAllowed(boolean multipleTriggersAllowed)1732             public @NonNull Builder setMultipleTriggersAllowed(boolean multipleTriggersAllowed) {
1733                 mMultipleTriggersAllowed = multipleTriggersAllowed;
1734                 return this;
1735             }
1736 
1737             /**
1738              * Sets the keyphrases field.
1739              * @param keyphrases The list of keyphrase specific data associated with this
1740              *                   recognition session.
1741              * @return the same Builder instance.
1742              */
setKeyphrases( @onNull Collection<KeyphraseRecognitionExtra> keyphrases)1743             public @NonNull Builder setKeyphrases(
1744                     @NonNull Collection<KeyphraseRecognitionExtra> keyphrases) {
1745                 mKeyphrases = keyphrases.toArray(new KeyphraseRecognitionExtra[keyphrases.size()]);
1746                 return this;
1747             }
1748 
1749             /**
1750              * Sets the data field.
1751              * @param data Opaque data provided to the DSP associated with this recognition session,
1752              *             which is used by system applications who know about voice engine
1753              *             internals, typically during enrollment.
1754              * @return the same Builder instance.
1755              */
setData(@onNull byte[] data)1756             public @NonNull Builder setData(@NonNull byte[] data) {
1757                 mData = requireNonNull(data, "Data must not be null");
1758                 return this;
1759             }
1760 
1761             /**
1762              * Sets the audio capabilities field.
1763              * @param audioCapabilities The bit field encoding of the audio capabilities associated
1764              *                          with this recognition session. See
1765              *                          {@link ModuleProperties.AudioCapabilities} for details.
1766              * @return the same Builder instance.
1767              */
setAudioCapabilities( @oduleProperties.AudioCapabilities int audioCapabilities)1768             public @NonNull Builder setAudioCapabilities(
1769                 @ModuleProperties.AudioCapabilities int audioCapabilities) {
1770                 mAudioCapabilities = audioCapabilities;
1771                 return this;
1772             }
1773 
1774             /**
1775              * Combines all of the parameters that have been set and return a new
1776              * {@link RecognitionConfig} object.
1777              * @return a new {@link RecognitionConfig} object
1778              */
build()1779             public @NonNull RecognitionConfig build() {
1780                 RecognitionConfig config = new RecognitionConfig(
1781                         /* captureRequested= */ mCaptureRequested,
1782                         /* multipleTriggersAllowed= */ mMultipleTriggersAllowed,
1783                         /* keyphrases= */ mKeyphrases,
1784                         /* data= */ mData,
1785                         /* audioCapabilities= */ mAudioCapabilities);
1786                 return config;
1787             }
1788         };
1789     }
1790 
1791     /**
1792      * Confidence level for users defined in a keyphrase.
1793      * - The confidence level is expressed in percent (0% -100%).
1794      * When used in a {@link KeyphraseRecognitionEvent} it indicates the detected confidence level
1795      * When used in a {@link RecognitionConfig} it indicates the minimum confidence level that
1796      * should trigger a recognition.
1797      * - The user ID is derived from the system ID {@link android.os.UserHandle#getIdentifier()}.
1798      *
1799      * @hide
1800      */
1801     public static class ConfidenceLevel implements Parcelable {
1802         @UnsupportedAppUsage
1803         public final int userId;
1804         @UnsupportedAppUsage
1805         public final int confidenceLevel;
1806 
1807         @UnsupportedAppUsage
ConfidenceLevel(int userId, int confidenceLevel)1808         public ConfidenceLevel(int userId, int confidenceLevel) {
1809             this.userId = userId;
1810             this.confidenceLevel = confidenceLevel;
1811         }
1812 
1813         public static final @android.annotation.NonNull Parcelable.Creator<ConfidenceLevel> CREATOR
1814                 = new Parcelable.Creator<ConfidenceLevel>() {
1815             public ConfidenceLevel createFromParcel(Parcel in) {
1816                 return ConfidenceLevel.fromParcel(in);
1817             }
1818 
1819             public ConfidenceLevel[] newArray(int size) {
1820                 return new ConfidenceLevel[size];
1821             }
1822         };
1823 
fromParcel(Parcel in)1824         private static ConfidenceLevel fromParcel(Parcel in) {
1825             int userId = in.readInt();
1826             int confidenceLevel = in.readInt();
1827             return new ConfidenceLevel(userId, confidenceLevel);
1828         }
1829 
1830         @Override
writeToParcel(Parcel dest, int flags)1831         public void writeToParcel(Parcel dest, int flags) {
1832             dest.writeInt(userId);
1833             dest.writeInt(confidenceLevel);
1834         }
1835 
1836         @Override
describeContents()1837         public int describeContents() {
1838             return 0;
1839         }
1840 
1841         @Override
hashCode()1842         public int hashCode() {
1843             final int prime = 31;
1844             int result = 1;
1845             result = prime * result + confidenceLevel;
1846             result = prime * result + userId;
1847             return result;
1848         }
1849 
1850         @Override
equals(@ullable Object obj)1851         public boolean equals(@Nullable Object obj) {
1852             if (this == obj)
1853                 return true;
1854             if (obj == null)
1855                 return false;
1856             if (getClass() != obj.getClass())
1857                 return false;
1858             ConfidenceLevel other = (ConfidenceLevel) obj;
1859             if (confidenceLevel != other.confidenceLevel)
1860                 return false;
1861             if (userId != other.userId)
1862                 return false;
1863             return true;
1864         }
1865 
1866         @Override
toString()1867         public String toString() {
1868             return "ConfidenceLevel [userId=" + userId
1869                     + ", confidenceLevel=" + confidenceLevel + "]";
1870         }
1871     }
1872 
1873     /**
1874      * Additional data conveyed by a {@link KeyphraseRecognitionEvent}
1875      * for a key phrase detection.
1876      */
1877     public static final class KeyphraseRecognitionExtra implements Parcelable {
1878         /**
1879          * The keyphrase ID
1880          *
1881          * @hide
1882          */
1883         @UnsupportedAppUsage
1884         public final int id;
1885 
1886         /**
1887          * Recognition modes matched for this event
1888          *
1889          * @hide
1890          */
1891         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
1892         public final int recognitionModes;
1893 
1894         /**
1895          * Confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER when user identification
1896          * is not performed
1897          *
1898          * @hide
1899          */
1900         @UnsupportedAppUsage
1901         public final int coarseConfidenceLevel;
1902 
1903         /**
1904          * Confidence levels for all users recognized (KeyphraseRecognitionEvent) or to
1905          * be recognized (RecognitionConfig)
1906          *
1907          * @hide
1908          */
1909         @UnsupportedAppUsage
1910         @NonNull
1911         public final ConfidenceLevel[] confidenceLevels;
1912 
1913 
1914         /**
1915          * @hide
1916          */
1917         @TestApi
KeyphraseRecognitionExtra(int id, @RecognitionModes int recognitionModes, int coarseConfidenceLevel)1918         public KeyphraseRecognitionExtra(int id, @RecognitionModes int recognitionModes,
1919                 int coarseConfidenceLevel) {
1920             this(id, recognitionModes, coarseConfidenceLevel, new ConfidenceLevel[0]);
1921         }
1922 
1923         /**
1924          * @hide
1925          */
1926         @UnsupportedAppUsage
KeyphraseRecognitionExtra(int id, int recognitionModes, @IntRange(from = 0, to = 100) int coarseConfidenceLevel, @Nullable ConfidenceLevel[] confidenceLevels)1927         public KeyphraseRecognitionExtra(int id, int recognitionModes,
1928                 @IntRange(from = 0, to = 100) int coarseConfidenceLevel,
1929                 @Nullable ConfidenceLevel[] confidenceLevels) {
1930             this.id = id;
1931             this.recognitionModes = recognitionModes;
1932             this.coarseConfidenceLevel = coarseConfidenceLevel;
1933             this.confidenceLevels =
1934                     confidenceLevels != null ? confidenceLevels : new ConfidenceLevel[0];
1935         }
1936 
1937         /**
1938          * The keyphrase ID associated with this class' additional data
1939          */
getKeyphraseId()1940         public int getKeyphraseId() {
1941             return id;
1942         }
1943 
1944         /**
1945          * Recognition modes matched for this event
1946          */
1947         @RecognitionModes
getRecognitionModes()1948         public int getRecognitionModes() {
1949             return recognitionModes;
1950         }
1951 
1952         /**
1953          * Confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER when user identification
1954          * is not performed
1955          *
1956          * <p>The confidence level is expressed in percent (0% -100%).
1957          */
1958         @IntRange(from = 0, to = 100)
getCoarseConfidenceLevel()1959         public int getCoarseConfidenceLevel() {
1960             return coarseConfidenceLevel;
1961         }
1962 
1963         /**
1964          * Detected confidence level for users defined in a keyphrase.
1965          *
1966          * <p>The confidence level is expressed in percent (0% -100%).
1967          *
1968          * <p>The user ID is derived from the system ID
1969          * {@link android.os.UserHandle#getIdentifier()}.
1970          *
1971          * @hide
1972          */
1973         @NonNull
getConfidenceLevels()1974         public Collection<ConfidenceLevel> getConfidenceLevels() {
1975             return Arrays.asList(confidenceLevels);
1976         }
1977 
1978         public static final @NonNull Parcelable.Creator<KeyphraseRecognitionExtra> CREATOR
1979                 = new Parcelable.Creator<KeyphraseRecognitionExtra>() {
1980             public KeyphraseRecognitionExtra createFromParcel(Parcel in) {
1981                 return KeyphraseRecognitionExtra.fromParcel(in);
1982             }
1983 
1984             public KeyphraseRecognitionExtra[] newArray(int size) {
1985                 return new KeyphraseRecognitionExtra[size];
1986             }
1987         };
1988 
fromParcel(Parcel in)1989         private static KeyphraseRecognitionExtra fromParcel(Parcel in) {
1990             int id = in.readInt();
1991             int recognitionModes = in.readInt();
1992             int coarseConfidenceLevel = in.readInt();
1993             ConfidenceLevel[] confidenceLevels = in.createTypedArray(ConfidenceLevel.CREATOR);
1994             return new KeyphraseRecognitionExtra(id, recognitionModes, coarseConfidenceLevel,
1995                     confidenceLevels);
1996         }
1997 
1998         @Override
writeToParcel(@onNull Parcel dest, int flags)1999         public void writeToParcel(@NonNull Parcel dest, int flags) {
2000             dest.writeInt(id);
2001             dest.writeInt(recognitionModes);
2002             dest.writeInt(coarseConfidenceLevel);
2003             dest.writeTypedArray(confidenceLevels, flags);
2004         }
2005 
2006         @Override
describeContents()2007         public int describeContents() {
2008             return 0;
2009         }
2010 
2011         @Override
hashCode()2012         public int hashCode() {
2013             final int prime = 31;
2014             int result = 1;
2015             result = prime * result + Arrays.hashCode(confidenceLevels);
2016             result = prime * result + id;
2017             result = prime * result + recognitionModes;
2018             result = prime * result + coarseConfidenceLevel;
2019             return result;
2020         }
2021 
2022         @Override
equals(@ullable Object obj)2023         public boolean equals(@Nullable Object obj) {
2024             if (this == obj) {
2025                 return true;
2026             }
2027             if (obj == null) {
2028                 return false;
2029             }
2030             if (getClass() != obj.getClass()) {
2031                 return false;
2032             }
2033             KeyphraseRecognitionExtra other = (KeyphraseRecognitionExtra) obj;
2034             if (!Arrays.equals(confidenceLevels, other.confidenceLevels)) {
2035                 return false;
2036             }
2037             if (id != other.id) {
2038                 return false;
2039             }
2040             if (recognitionModes != other.recognitionModes) {
2041                 return false;
2042             }
2043             if (coarseConfidenceLevel != other.coarseConfidenceLevel) {
2044                 return false;
2045             }
2046             return true;
2047         }
2048 
2049         @Override
toString()2050         public String toString() {
2051             return "KeyphraseRecognitionExtra [id=" + id + ", recognitionModes=" + recognitionModes
2052                     + ", coarseConfidenceLevel=" + coarseConfidenceLevel
2053                     + ", confidenceLevels=" + Arrays.toString(confidenceLevels) + "]";
2054         }
2055     }
2056 
2057     /**
2058      *  Specialized {@link RecognitionEvent} for a key phrase detection.
2059      *
2060      *  @hide
2061      */
2062     public static class KeyphraseRecognitionEvent extends RecognitionEvent implements Parcelable {
2063         /** Indicates if the key phrase is present in the buffered audio available for capture */
2064         @UnsupportedAppUsage
2065         @NonNull
2066         public final KeyphraseRecognitionExtra[] keyphraseExtras;
2067 
2068         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
KeyphraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable, int captureSession, int captureDelayMs, int capturePreambleMs, boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data, @Nullable KeyphraseRecognitionExtra[] keyphraseExtras, @ElapsedRealtimeLong long halEventReceivedMillis, IBinder token)2069         public KeyphraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
2070                 int captureSession, int captureDelayMs, int capturePreambleMs,
2071                 boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data,
2072                 @Nullable KeyphraseRecognitionExtra[] keyphraseExtras,
2073                 @ElapsedRealtimeLong long halEventReceivedMillis,
2074                 IBinder token) {
2075             this(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs,
2076                     capturePreambleMs, triggerInData, captureFormat, data, keyphraseExtras,
2077                     status == RECOGNITION_STATUS_GET_STATE_RESPONSE, halEventReceivedMillis,
2078                     token);
2079         }
2080 
KeyphraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable, int captureSession, int captureDelayMs, int capturePreambleMs, boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data, @Nullable KeyphraseRecognitionExtra[] keyphraseExtras, boolean recognitionStillActive, @ElapsedRealtimeLong long halEventReceivedMillis, IBinder token)2081         public KeyphraseRecognitionEvent(int status, int soundModelHandle,
2082                 boolean captureAvailable,
2083                 int captureSession, int captureDelayMs, int capturePreambleMs,
2084                 boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data,
2085                 @Nullable KeyphraseRecognitionExtra[] keyphraseExtras,
2086                 boolean recognitionStillActive, @ElapsedRealtimeLong long halEventReceivedMillis,
2087                 IBinder token) {
2088             super(status, soundModelHandle, captureAvailable,
2089                     captureSession, captureDelayMs, capturePreambleMs, triggerInData, captureFormat,
2090                     data, recognitionStillActive, halEventReceivedMillis, token);
2091             this.keyphraseExtras =
2092                     keyphraseExtras != null ? keyphraseExtras : new KeyphraseRecognitionExtra[0];
2093         }
2094 
2095         public static final @NonNull Parcelable.Creator<KeyphraseRecognitionEvent> CREATOR
2096                 = new Parcelable.Creator<KeyphraseRecognitionEvent>() {
2097             public KeyphraseRecognitionEvent createFromParcel(Parcel in) {
2098                 return KeyphraseRecognitionEvent.fromParcelForKeyphrase(in);
2099             }
2100 
2101             public KeyphraseRecognitionEvent[] newArray(int size) {
2102                 return new KeyphraseRecognitionEvent[size];
2103             }
2104         };
2105 
fromParcelForKeyphrase(Parcel in)2106         private static KeyphraseRecognitionEvent fromParcelForKeyphrase(Parcel in) {
2107             int status = in.readInt();
2108             int soundModelHandle = in.readInt();
2109             boolean captureAvailable = in.readByte() == 1;
2110             int captureSession = in.readInt();
2111             int captureDelayMs = in.readInt();
2112             int capturePreambleMs = in.readInt();
2113             boolean triggerInData = in.readByte() == 1;
2114             AudioFormat captureFormat = null;
2115             if (in.readByte() == 1) {
2116                 int sampleRate = in.readInt();
2117                 int encoding = in.readInt();
2118                 int channelMask = in.readInt();
2119                 captureFormat = (new AudioFormat.Builder())
2120                     .setChannelMask(channelMask)
2121                     .setEncoding(encoding)
2122                     .setSampleRate(sampleRate)
2123                     .build();
2124             }
2125             byte[] data = in.readBlob();
2126             boolean recognitionStillActive = in.readBoolean();
2127             long halEventReceivedMillis = in.readLong();
2128             IBinder token = in.readStrongBinder();
2129             KeyphraseRecognitionExtra[] keyphraseExtras =
2130                     in.createTypedArray(KeyphraseRecognitionExtra.CREATOR);
2131             return new KeyphraseRecognitionEvent(status, soundModelHandle,
2132                     captureAvailable, captureSession, captureDelayMs, capturePreambleMs,
2133                     triggerInData, captureFormat, data, keyphraseExtras, recognitionStillActive,
2134                     halEventReceivedMillis, token);
2135         }
2136 
2137         @Override
writeToParcel(Parcel dest, int flags)2138         public void writeToParcel(Parcel dest, int flags) {
2139             dest.writeInt(status);
2140             dest.writeInt(soundModelHandle);
2141             dest.writeByte((byte) (captureAvailable ? 1 : 0));
2142             dest.writeInt(captureSession);
2143             dest.writeInt(captureDelayMs);
2144             dest.writeInt(capturePreambleMs);
2145             dest.writeByte((byte) (triggerInData ? 1 : 0));
2146             if (captureFormat != null) {
2147                 dest.writeByte((byte)1);
2148                 dest.writeInt(captureFormat.getSampleRate());
2149                 dest.writeInt(captureFormat.getEncoding());
2150                 dest.writeInt(captureFormat.getChannelMask());
2151             } else {
2152                 dest.writeByte((byte)0);
2153             }
2154             dest.writeBlob(data);
2155             dest.writeBoolean(recognitionStillActive);
2156             dest.writeLong(halEventReceivedMillis);
2157             dest.writeStrongBinder(token);
2158             dest.writeTypedArray(keyphraseExtras, flags);
2159         }
2160 
2161         @Override
describeContents()2162         public int describeContents() {
2163             return 0;
2164         }
2165 
2166         @Override
hashCode()2167         public int hashCode() {
2168             final int prime = 31;
2169             int result = super.hashCode();
2170             result = prime * result + Arrays.hashCode(keyphraseExtras);
2171             return result;
2172         }
2173 
2174         @Override
equals(@ullable Object obj)2175         public boolean equals(@Nullable Object obj) {
2176             if (this == obj)
2177                 return true;
2178             if (!super.equals(obj))
2179                 return false;
2180             if (getClass() != obj.getClass())
2181                 return false;
2182             KeyphraseRecognitionEvent other = (KeyphraseRecognitionEvent) obj;
2183             if (!Arrays.equals(keyphraseExtras, other.keyphraseExtras))
2184                 return false;
2185             return true;
2186         }
2187 
2188         @Override
toString()2189         public String toString() {
2190             return "KeyphraseRecognitionEvent [keyphraseExtras=" + Arrays.toString(keyphraseExtras)
2191                     + ", status=" + status
2192                     + ", soundModelHandle=" + soundModelHandle
2193                     + ", captureAvailable=" + captureAvailable
2194                     + ", captureSession=" + captureSession
2195                     + ", captureDelayMs=" + captureDelayMs
2196                     + ", capturePreambleMs=" + capturePreambleMs
2197                     + ", triggerInData=" + triggerInData
2198                     + ((captureFormat == null) ? "" :
2199                         (", sampleRate=" + captureFormat.getSampleRate()))
2200                     + ((captureFormat == null) ? "" :
2201                         (", encoding=" + captureFormat.getEncoding()))
2202                     + ((captureFormat == null) ? "" :
2203                         (", channelMask=" + captureFormat.getChannelMask()))
2204                     + ", data=" + (data == null ? 0 : data.length)
2205                     + ", recognitionStillActive=" + recognitionStillActive
2206                     + ", halEventReceivedMillis=" + halEventReceivedMillis
2207                     + ", token=" + token
2208                     + "]";
2209         }
2210     }
2211 
2212     /**
2213      * Sub-class of RecognitionEvent specifically for sound-trigger based sound
2214      * models(non-keyphrase). Currently does not contain any additional fields.
2215      *
2216      * @hide
2217      */
2218     public static class GenericRecognitionEvent extends RecognitionEvent implements Parcelable {
2219         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
GenericRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable, int captureSession, int captureDelayMs, int capturePreambleMs, boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data, @ElapsedRealtimeLong long halEventReceivedMillis, IBinder token)2220         public GenericRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
2221                 int captureSession, int captureDelayMs, int capturePreambleMs,
2222                 boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data,
2223                 @ElapsedRealtimeLong long halEventReceivedMillis,
2224                 IBinder token) {
2225             this(status, soundModelHandle, captureAvailable,
2226                     captureSession, captureDelayMs,
2227                     capturePreambleMs, triggerInData, captureFormat, data,
2228                     status == RECOGNITION_STATUS_GET_STATE_RESPONSE,
2229                     halEventReceivedMillis, token);
2230         }
2231 
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, IBinder token)2232         public GenericRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
2233                 int captureSession, int captureDelayMs, int capturePreambleMs,
2234                 boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data,
2235                 boolean recognitionStillActive, @ElapsedRealtimeLong long halEventReceivedMillis,
2236                 IBinder token) {
2237             super(status, soundModelHandle, captureAvailable,
2238                     captureSession, captureDelayMs, capturePreambleMs, triggerInData, captureFormat,
2239                     data, recognitionStillActive, halEventReceivedMillis, token);
2240         }
2241 
2242         public static final @android.annotation.NonNull Parcelable.Creator<GenericRecognitionEvent> CREATOR
2243                 = new Parcelable.Creator<GenericRecognitionEvent>() {
2244             public GenericRecognitionEvent createFromParcel(Parcel in) {
2245                 return GenericRecognitionEvent.fromParcelForGeneric(in);
2246             }
2247 
2248             public GenericRecognitionEvent[] newArray(int size) {
2249                 return new GenericRecognitionEvent[size];
2250             }
2251         };
2252 
fromParcelForGeneric(Parcel in)2253         private static GenericRecognitionEvent fromParcelForGeneric(Parcel in) {
2254             RecognitionEvent event = RecognitionEvent.fromParcel(in);
2255             return new GenericRecognitionEvent(event.status, event.soundModelHandle,
2256                     event.captureAvailable, event.captureSession, event.captureDelayMs,
2257                     event.capturePreambleMs, event.triggerInData, event.captureFormat, event.data,
2258                     event.recognitionStillActive, event.halEventReceivedMillis, event.token);
2259         }
2260 
2261         @Override
equals(@ullable Object obj)2262         public boolean equals(@Nullable Object obj) {
2263             if (this == obj)
2264                 return true;
2265             if (obj == null)
2266                 return false;
2267             if (getClass() != obj.getClass()) return false;
2268             RecognitionEvent other = (RecognitionEvent) obj;
2269             return super.equals(obj);
2270         }
2271 
2272         @Override
toString()2273         public String toString() {
2274             return "GenericRecognitionEvent ::" + super.toString();
2275         }
2276     }
2277 
2278     private static Object mServiceLock = new Object();
2279 
2280     /**
2281      * Translate an exception thrown from interaction with the underlying service to an error code.
2282      * Throws a runtime exception for unexpected conditions.
2283      * @param e The caught exception.
2284      * @return The error code.
2285      *
2286      * @hide
2287      */
handleException(Exception e)2288     public static int handleException(Exception e) {
2289         Log.w(TAG, "Exception caught", e);
2290         if (e instanceof RemoteException) {
2291             return STATUS_DEAD_OBJECT;
2292         }
2293         if (e instanceof ServiceSpecificException) {
2294             switch (((ServiceSpecificException) e).errorCode) {
2295                 case Status.OPERATION_NOT_SUPPORTED:
2296                     return STATUS_INVALID_OPERATION;
2297                 case Status.TEMPORARY_PERMISSION_DENIED:
2298                     return STATUS_PERMISSION_DENIED;
2299                 case Status.DEAD_OBJECT:
2300                     return STATUS_DEAD_OBJECT;
2301                 case Status.INTERNAL_ERROR:
2302                     return STATUS_ERROR;
2303                 case Status.RESOURCE_CONTENTION:
2304                     return STATUS_BUSY;
2305             }
2306             return STATUS_ERROR;
2307         }
2308         if (e instanceof SecurityException) {
2309             return STATUS_PERMISSION_DENIED;
2310         }
2311         if (e instanceof IllegalStateException) {
2312             return STATUS_INVALID_OPERATION;
2313         }
2314         if (e instanceof IllegalArgumentException || e instanceof NullPointerException) {
2315             return STATUS_BAD_VALUE;
2316         }
2317         // This is not one of the conditions represented by our error code, escalate to a
2318         // RuntimeException.
2319         Log.e(TAG, "Escalating unexpected exception: ", e);
2320         throw new RuntimeException(e);
2321     }
2322 
2323     /**
2324      * Returns a list of descriptors for all hardware modules loaded.
2325      * @param modules A ModuleProperties array where the list will be returned.
2326      * @return - {@link #STATUS_OK} in case of success
2327      *         - {@link #STATUS_ERROR} in case of unspecified error
2328      *         - {@link #STATUS_PERMISSION_DENIED} if the caller does not have system permission
2329      *         - {@link #STATUS_NO_INIT} if the native service cannot be reached
2330      *         - {@link #STATUS_BAD_VALUE} if modules is null
2331      *         - {@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails
2332      *
2333      * @removed Please use {@link #listModulesAsOriginator(ArrayList, Identity)} or
2334      * {@link #listModulesAsMiddleman(ArrayList, Identity, Identity)}, based on whether the
2335      * client is acting on behalf of its own identity or a separate identity.
2336      * @hide
2337      */
2338     @UnsupportedAppUsage
listModules(@onNull ArrayList<ModuleProperties> modules)2339     public static int listModules(@NonNull ArrayList<ModuleProperties> modules) {
2340         return STATUS_OK;
2341     }
2342 
2343     /**
2344      * Returns a list of descriptors for all hardware modules loaded.
2345      * This variant is intended for use when the caller itself is the originator of the operation.
2346      * @param modules A ModuleProperties array where the list will be returned.
2347      * @param originatorIdentity The identity of the originator, which will be used for permission
2348      *                           purposes.
2349      * @return - {@link #STATUS_OK} in case of success
2350      *         - {@link #STATUS_ERROR} in case of unspecified error
2351      *         - {@link #STATUS_PERMISSION_DENIED} if the caller does not have system permission
2352      *         - {@link #STATUS_NO_INIT} if the native service cannot be reached
2353      *         - {@link #STATUS_BAD_VALUE} if modules is null
2354      *         - {@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails
2355      * @deprecated Use {@link android.media.soundtrigger.SoundTriggerManager} instead.
2356      * @hide
2357      */
2358     @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
2359     @Deprecated
listModulesAsOriginator(@onNull ArrayList<ModuleProperties> modules, @NonNull Identity originatorIdentity)2360     public static int listModulesAsOriginator(@NonNull ArrayList<ModuleProperties> modules,
2361             @NonNull Identity originatorIdentity) {
2362         try {
2363             SoundTriggerModuleDescriptor[] descs = getService().listModulesAsOriginator(
2364                     originatorIdentity);
2365             convertDescriptorsToModuleProperties(descs, modules);
2366             return STATUS_OK;
2367         } catch (Exception e) {
2368             return handleException(e);
2369         }
2370     }
2371 
2372     /**
2373      * Returns a list of descriptors for all hardware modules loaded.
2374      * This variant is intended for use when the caller is acting on behalf of a different identity
2375      * for permission purposes.
2376      * @param modules A ModuleProperties array where the list will be returned.
2377      * @param middlemanIdentity The identity of the caller, acting as middleman.
2378      * @param originatorIdentity The identity of the originator, which will be used for permission
2379      *                           purposes.
2380      * @return - {@link #STATUS_OK} in case of success
2381      *         - {@link #STATUS_ERROR} in case of unspecified error
2382      *         - {@link #STATUS_PERMISSION_DENIED} if the caller does not have system permission
2383      *         - {@link #STATUS_NO_INIT} if the native service cannot be reached
2384      *         - {@link #STATUS_BAD_VALUE} if modules is null
2385      *         - {@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails
2386      *
2387      * @deprecated Use {@link android.media.soundtrigger.SoundTriggerManager} instead.
2388      * @hide
2389      */
2390     @RequiresPermission(SOUNDTRIGGER_DELEGATE_IDENTITY)
2391     @Deprecated
listModulesAsMiddleman(@onNull ArrayList<ModuleProperties> modules, @NonNull Identity middlemanIdentity, @NonNull Identity originatorIdentity)2392     public static int listModulesAsMiddleman(@NonNull ArrayList<ModuleProperties> modules,
2393             @NonNull Identity middlemanIdentity,
2394             @NonNull Identity originatorIdentity) {
2395         try {
2396             SoundTriggerModuleDescriptor[] descs = getService().listModulesAsMiddleman(
2397                     middlemanIdentity, originatorIdentity);
2398             convertDescriptorsToModuleProperties(descs, modules);
2399             return STATUS_OK;
2400         } catch (Exception e) {
2401             return handleException(e);
2402         }
2403     }
2404 
2405     /**
2406      * Converts an array of SoundTriggerModuleDescriptor into an (existing) ArrayList of
2407      * ModuleProperties.
2408      * @param descsIn The input descriptors.
2409      * @param modulesOut The output list.
2410      */
convertDescriptorsToModuleProperties( @onNull SoundTriggerModuleDescriptor[] descsIn, @NonNull ArrayList<ModuleProperties> modulesOut)2411     private static void convertDescriptorsToModuleProperties(
2412             @NonNull SoundTriggerModuleDescriptor[] descsIn,
2413             @NonNull ArrayList<ModuleProperties> modulesOut) {
2414         modulesOut.clear();
2415         modulesOut.ensureCapacity(descsIn.length);
2416         for (SoundTriggerModuleDescriptor desc : descsIn) {
2417             modulesOut.add(ConversionUtil.aidl2apiModuleDescriptor(desc));
2418         }
2419     }
2420 
2421     /**
2422      * Get an interface on a hardware module to control sound models and recognition on
2423      * this module.
2424      * @param moduleId Sound module system identifier {@link ModuleProperties#mId}. mandatory.
2425      * @param listener {@link StatusListener} interface. Mandatory.
2426      * @param handler the Handler that will receive the callabcks. Can be null if default handler
2427      *                is OK.
2428      * @return a valid sound module in case of success or null in case of error.
2429      *
2430      * @removed Use {@link android.media.soundtrigger.SoundTriggerManager} instead.
2431      * @hide
2432      */
2433     @UnsupportedAppUsage
attachModule(int moduleId, @NonNull StatusListener listener, @Nullable Handler handler)2434     private static SoundTriggerModule attachModule(int moduleId,
2435             @NonNull StatusListener listener,
2436             @Nullable Handler handler) {
2437             return null;
2438     }
2439 
2440     /**
2441      * Get an interface on a hardware module to control sound models and recognition on
2442      * this module.
2443      * This variant is intended for use when the caller is acting on behalf of a different identity
2444      * for permission purposes.
2445      * @param moduleId Sound module system identifier {@link ModuleProperties#mId}. mandatory.
2446      * @param listener {@link StatusListener} interface. Mandatory.
2447      * @param handler the Handler that will receive the callabcks. Can be null if default handler
2448      *                is OK.
2449      * @param middlemanIdentity The identity of the caller, acting as middleman.
2450      * @param originatorIdentity The identity of the originator, which will be used for permission
2451      *                           purposes.
2452      * @return a valid sound module in case of success or null in case of error.
2453      * @deprecated Use {@link android.media.soundtrigger.SoundTriggerManager} instead.
2454      * @hide
2455      */
2456     @RequiresPermission(SOUNDTRIGGER_DELEGATE_IDENTITY)
2457     @Deprecated
attachModuleAsMiddleman(int moduleId, @NonNull SoundTrigger.StatusListener listener, @Nullable Handler handler, Identity middlemanIdentity, Identity originatorIdentity)2458     public static SoundTriggerModule attachModuleAsMiddleman(int moduleId,
2459             @NonNull SoundTrigger.StatusListener listener,
2460             @Nullable Handler handler, Identity middlemanIdentity,
2461             Identity originatorIdentity) {
2462         Looper looper = handler != null ? handler.getLooper() : Looper.getMainLooper();
2463         try {
2464             return new SoundTriggerModule(getService(), moduleId, listener, looper,
2465                     middlemanIdentity, originatorIdentity, false);
2466         } catch (Exception e) {
2467             Log.e(TAG, "", e);
2468             return null;
2469         }
2470     }
2471 
2472     /**
2473      * Get an interface on a hardware module to control sound models and recognition on
2474      * this module.
2475      * This variant is intended for use when the caller itself is the originator of the operation.
2476      * @param moduleId Sound module system identifier {@link ModuleProperties#mId}. mandatory.
2477      * @param listener {@link StatusListener} interface. Mandatory.
2478      * @param handler the Handler that will receive the callabcks. Can be null if default handler
2479      *                is OK.
2480      * @param originatorIdentity The identity of the originator, which will be used for permission
2481      *                           purposes.
2482      * @return a valid sound module in case of success or null in case of error.
2483      *
2484      * @hide
2485      */
2486     @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
attachModuleAsOriginator(int moduleId, @NonNull SoundTrigger.StatusListener listener, @Nullable Handler handler, @NonNull Identity originatorIdentity)2487     public static SoundTriggerModule attachModuleAsOriginator(int moduleId,
2488             @NonNull SoundTrigger.StatusListener listener,
2489             @Nullable Handler handler, @NonNull Identity originatorIdentity) {
2490         Looper looper = handler != null ? handler.getLooper() : Looper.getMainLooper();
2491         try {
2492             return new SoundTriggerModule(getService(), moduleId, listener, looper,
2493                     originatorIdentity);
2494         } catch (Exception e) {
2495             Log.e(TAG, "", e);
2496             return null;
2497         }
2498     }
2499 
getService()2500     private static ISoundTriggerMiddlewareService getService() {
2501         synchronized (mServiceLock) {
2502             while (true) {
2503                 IBinder binder = null;
2504                 try {
2505                     binder =
2506                             ServiceManager.getServiceOrThrow(
2507                                     Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE);
2508                     return ISoundTriggerMiddlewareService.Stub.asInterface(binder);
2509                 } catch (Exception e) {
2510                     Log.e(TAG, "Failed to bind to soundtrigger service", e);
2511                 }
2512             }
2513         }
2514     }
2515 
2516     /**
2517      * Interface provided by the client application when attaching to a {@link SoundTriggerModule}
2518      * to received recognition and error notifications.
2519      *
2520      * @hide
2521      */
2522     public interface StatusListener {
2523         /**
2524          * Called when recognition succeeds of fails
2525          */
onRecognition(RecognitionEvent event)2526         void onRecognition(RecognitionEvent event);
2527 
2528         /**
2529          * Called when a sound model has been preemptively unloaded by the underlying
2530          * implementation.
2531          */
onModelUnloaded(int modelHandle)2532         void onModelUnloaded(int modelHandle);
2533 
2534         /**
2535          * Called whenever underlying conditions change, such that load/start operations that have
2536          * previously failed or got preempted may now succeed. This is not a guarantee, merely a
2537          * hint that the client may want to retry operations.
2538          */
onResourcesAvailable()2539         void onResourcesAvailable();
2540 
2541         /**
2542          * Called when the sound trigger native service dies
2543          */
onServiceDied()2544         void onServiceDied();
2545     }
2546 }
2547