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.system.OsConstants.EINVAL;
20 import static android.system.OsConstants.ENODEV;
21 import static android.system.OsConstants.ENOSYS;
22 import static android.system.OsConstants.EPERM;
23 import static android.system.OsConstants.EPIPE;
24 
25 import static java.util.Objects.requireNonNull;
26 
27 import android.annotation.IntDef;
28 import android.annotation.NonNull;
29 import android.annotation.Nullable;
30 import android.annotation.SuppressLint;
31 import android.annotation.SystemApi;
32 import android.annotation.TestApi;
33 import android.app.ActivityThread;
34 import android.compat.annotation.UnsupportedAppUsage;
35 import android.content.Context;
36 import android.media.AudioFormat;
37 import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
38 import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
39 import android.media.soundtrigger_middleware.Status;
40 import android.os.Handler;
41 import android.os.IBinder;
42 import android.os.Looper;
43 import android.os.Parcel;
44 import android.os.Parcelable;
45 import android.os.RemoteException;
46 import android.os.ServiceManager;
47 import android.os.ServiceSpecificException;
48 import android.util.Log;
49 
50 import java.lang.annotation.Retention;
51 import java.lang.annotation.RetentionPolicy;
52 import java.util.ArrayList;
53 import java.util.Arrays;
54 import java.util.Locale;
55 import java.util.UUID;
56 
57 /**
58  * The SoundTrigger class provides access to the service managing the sound trigger HAL.
59  *
60  * @hide
61  */
62 @TestApi
63 @SystemApi
64 public class SoundTrigger {
65     private static final String TAG = "SoundTrigger";
66 
SoundTrigger()67     private SoundTrigger() {
68     }
69 
70     /**
71      * Status code used when the operation succeeded
72      */
73     public static final int STATUS_OK = 0;
74     /** @hide */
75     public static final int STATUS_ERROR = Integer.MIN_VALUE;
76     /** @hide */
77     public static final int STATUS_PERMISSION_DENIED = -EPERM;
78     /** @hide */
79     public static final int STATUS_NO_INIT = -ENODEV;
80     /** @hide */
81     public static final int STATUS_BAD_VALUE = -EINVAL;
82     /** @hide */
83     public static final int STATUS_DEAD_OBJECT = -EPIPE;
84     /** @hide */
85     public static final int STATUS_INVALID_OPERATION = -ENOSYS;
86 
87     /*****************************************************************************
88      * A ModuleProperties describes a given sound trigger hardware module
89      * managed by the native sound trigger service. Each module has a unique
90      * ID used to target any API call to this paricular module. Module
91      * properties are returned by listModules() method.
92      *
93      ****************************************************************************/
94     public static final class ModuleProperties implements Parcelable {
95 
96         /**
97          * Bit field values of AudioCapabilities supported by the implemented HAL
98          * driver.
99          * @hide
100          */
101         @Retention(RetentionPolicy.SOURCE)
102         @IntDef(flag = true, prefix = { "AUDIO_CAPABILITY_" }, value = {
103                 AUDIO_CAPABILITY_ECHO_CANCELLATION,
104                 AUDIO_CAPABILITY_NOISE_SUPPRESSION
105         })
106         public @interface AudioCapabilities {}
107 
108         /**
109          * If set the underlying module supports AEC.
110          * Describes bit field {@link ModuleProperties#mAudioCapabilities}
111          */
112         public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 0x1;
113         /**
114          * If set, the underlying module supports noise suppression.
115          * Describes bit field {@link ModuleProperties#mAudioCapabilities}
116          */
117         public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = 0x2;
118 
119         private final int mId;
120         @NonNull
121         private final String mImplementor;
122         @NonNull
123         private final String mDescription;
124         @NonNull
125         private final UUID mUuid;
126         private final int mVersion;
127         @NonNull
128         private final String mSupportedModelArch;
129         private final int mMaxSoundModels;
130         private final int mMaxKeyphrases;
131         private final int mMaxUsers;
132         @RecognitionModes
133         private final int mRecognitionModes;
134         private final boolean mSupportsCaptureTransition;
135         private final int mMaxBufferMillis;
136         private final boolean mSupportsConcurrentCapture;
137         private final int mPowerConsumptionMw;
138         private final boolean mReturnsTriggerInEvent;
139         @AudioCapabilities
140         private final int mAudioCapabilities;
141 
142         /** @hide */
143         @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)144         public ModuleProperties(int id, @NonNull String implementor, @NonNull String description,
145                 @NonNull String uuid, int version, @NonNull String supportedModelArch,
146                 int maxSoundModels, int maxKeyphrases, int maxUsers,
147                 @RecognitionModes int recognitionModes, boolean supportsCaptureTransition,
148                 int maxBufferMs, boolean supportsConcurrentCapture, int powerConsumptionMw,
149                 boolean returnsTriggerInEvent, int audioCapabilities) {
150             this.mId = id;
151             this.mImplementor = requireNonNull(implementor);
152             this.mDescription = requireNonNull(description);
153             this.mUuid = UUID.fromString(requireNonNull(uuid));
154             this.mVersion = version;
155             this.mSupportedModelArch = requireNonNull(supportedModelArch);
156             this.mMaxSoundModels = maxSoundModels;
157             this.mMaxKeyphrases = maxKeyphrases;
158             this.mMaxUsers = maxUsers;
159             this.mRecognitionModes = recognitionModes;
160             this.mSupportsCaptureTransition = supportsCaptureTransition;
161             this.mMaxBufferMillis = maxBufferMs;
162             this.mSupportsConcurrentCapture = supportsConcurrentCapture;
163             this.mPowerConsumptionMw = powerConsumptionMw;
164             this.mReturnsTriggerInEvent = returnsTriggerInEvent;
165             this.mAudioCapabilities = audioCapabilities;
166         }
167 
168         /** Unique module ID provided by the native service */
getId()169         public int getId() {
170             return mId;
171         }
172 
173         /** human readable voice detection engine implementor */
174         @NonNull
getImplementor()175         public String getImplementor() {
176             return mImplementor;
177         }
178 
179         /** human readable voice detection engine description */
180         @NonNull
getDescription()181         public String getDescription() {
182             return mDescription;
183         }
184 
185         /** Unique voice engine Id (changes with each version) */
186         @NonNull
getUuid()187         public UUID getUuid() {
188             return mUuid;
189         }
190 
191         /** Voice detection engine version */
getVersion()192         public int getVersion() {
193             return mVersion;
194         }
195 
196         /**
197          * String naming the architecture used for running the supported models.
198          * (eg. a platform running models on a DSP could implement this string to convey the DSP
199          * architecture used)
200          */
201         @NonNull
getSupportedModelArch()202         public String getSupportedModelArch() {
203             return mSupportedModelArch;
204         }
205 
206         /** Maximum number of active sound models */
getMaxSoundModels()207         public int getMaxSoundModels() {
208             return mMaxSoundModels;
209         }
210 
211         /** Maximum number of key phrases */
getMaxKeyphrases()212         public int getMaxKeyphrases() {
213             return mMaxKeyphrases;
214         }
215 
216         /** Maximum number of users per key phrase */
getMaxUsers()217         public int getMaxUsers() {
218             return mMaxUsers;
219         }
220 
221         /** Supported recognition modes (bit field, RECOGNITION_MODE_VOICE_TRIGGER ...) */
222         @RecognitionModes
getRecognitionModes()223         public int getRecognitionModes() {
224             return mRecognitionModes;
225         }
226 
227         /** Supports seamless transition to capture mode after recognition */
isCaptureTransitionSupported()228         public boolean isCaptureTransitionSupported() {
229             return mSupportsCaptureTransition;
230         }
231 
232         /** Maximum buffering capacity in ms if supportsCaptureTransition() is true */
getMaxBufferMillis()233         public int getMaxBufferMillis() {
234             return mMaxBufferMillis;
235         }
236 
237         /** Supports capture by other use cases while detection is active */
isConcurrentCaptureSupported()238         public boolean isConcurrentCaptureSupported() {
239             return mSupportsConcurrentCapture;
240         }
241 
242         /** Rated power consumption when detection is active with TDB silence/sound/speech ratio */
getPowerConsumptionMw()243         public int getPowerConsumptionMw() {
244             return mPowerConsumptionMw;
245         }
246 
247         /** Returns the trigger (key phrase) capture in the binary data of the
248          * recognition callback event */
isTriggerReturnedInEvent()249         public boolean isTriggerReturnedInEvent() {
250             return mReturnsTriggerInEvent;
251         }
252 
253         /**
254          * Bit field encoding of the AudioCapabilities
255          * supported by the firmware.
256          */
257         @AudioCapabilities
getAudioCapabilities()258         public int getAudioCapabilities() {
259             return mAudioCapabilities;
260         }
261 
262         public static final @android.annotation.NonNull Parcelable.Creator<ModuleProperties> CREATOR
263                 = new Parcelable.Creator<ModuleProperties>() {
264             public ModuleProperties createFromParcel(Parcel in) {
265                 return ModuleProperties.fromParcel(in);
266             }
267 
268             public ModuleProperties[] newArray(int size) {
269                 return new ModuleProperties[size];
270             }
271         };
272 
fromParcel(Parcel in)273         private static ModuleProperties fromParcel(Parcel in) {
274             int id = in.readInt();
275             String implementor = in.readString();
276             String description = in.readString();
277             String uuid = in.readString();
278             int version = in.readInt();
279             String supportedModelArch = in.readString();
280             int maxSoundModels = in.readInt();
281             int maxKeyphrases = in.readInt();
282             int maxUsers = in.readInt();
283             int recognitionModes = in.readInt();
284             boolean supportsCaptureTransition = in.readByte() == 1;
285             int maxBufferMs = in.readInt();
286             boolean supportsConcurrentCapture = in.readByte() == 1;
287             int powerConsumptionMw = in.readInt();
288             boolean returnsTriggerInEvent = in.readByte() == 1;
289             int audioCapabilities = in.readInt();
290             return new ModuleProperties(id, implementor, description, uuid, version,
291                     supportedModelArch, maxSoundModels, maxKeyphrases, maxUsers, recognitionModes,
292                     supportsCaptureTransition, maxBufferMs, supportsConcurrentCapture,
293                     powerConsumptionMw, returnsTriggerInEvent, audioCapabilities);
294         }
295 
296         @Override
writeToParcel(@uppressLint"MissingNullability") Parcel dest, int flags)297         public void writeToParcel(@SuppressLint("MissingNullability") Parcel dest, int flags) {
298             dest.writeInt(getId());
299             dest.writeString(getImplementor());
300             dest.writeString(getDescription());
301             dest.writeString(getUuid().toString());
302             dest.writeInt(getVersion());
303             dest.writeString(getSupportedModelArch());
304             dest.writeInt(getMaxSoundModels());
305             dest.writeInt(getMaxKeyphrases());
306             dest.writeInt(getMaxUsers());
307             dest.writeInt(getRecognitionModes());
308             dest.writeByte((byte) (isCaptureTransitionSupported() ? 1 : 0));
309             dest.writeInt(getMaxBufferMillis());
310             dest.writeByte((byte) (isConcurrentCaptureSupported() ? 1 : 0));
311             dest.writeInt(getPowerConsumptionMw());
312             dest.writeByte((byte) (isTriggerReturnedInEvent() ? 1 : 0));
313             dest.writeInt(getAudioCapabilities());
314         }
315 
316         @Override
describeContents()317         public int describeContents() {
318             return 0;
319         }
320 
321         @Override
equals(@ullable Object obj)322         public boolean equals(@Nullable Object obj) {
323             if (this == obj) {
324                 return true;
325             }
326             if (obj == null) {
327                 return false;
328             }
329             if (!(obj instanceof ModuleProperties)) {
330                 return false;
331             }
332             ModuleProperties other = (ModuleProperties) obj;
333             if (mId != other.mId) {
334                 return false;
335             }
336             if (!mImplementor.equals(other.mImplementor)) {
337                 return false;
338             }
339             if (!mDescription.equals(other.mDescription)) {
340                 return false;
341             }
342             if (!mUuid.equals(other.mUuid)) {
343                 return false;
344             }
345             if (mVersion != other.mVersion) {
346                 return false;
347             }
348             if (!mSupportedModelArch.equals(other.mSupportedModelArch)) {
349                 return false;
350             }
351             if (mMaxSoundModels != other.mMaxSoundModels) {
352                 return false;
353             }
354             if (mMaxKeyphrases != other.mMaxKeyphrases) {
355                 return false;
356             }
357             if (mMaxUsers != other.mMaxUsers) {
358                 return false;
359             }
360             if (mRecognitionModes != other.mRecognitionModes) {
361                 return false;
362             }
363             if (mSupportsCaptureTransition != other.mSupportsCaptureTransition) {
364                 return false;
365             }
366             if (mMaxBufferMillis != other.mMaxBufferMillis) {
367                 return false;
368             }
369             if (mSupportsConcurrentCapture != other.mSupportsConcurrentCapture) {
370                 return false;
371             }
372             if (mPowerConsumptionMw != other.mPowerConsumptionMw) {
373                 return false;
374             }
375             if (mReturnsTriggerInEvent != other.mReturnsTriggerInEvent) {
376                 return false;
377             }
378             if (mAudioCapabilities != other.mAudioCapabilities) {
379                 return false;
380             }
381             return true;
382         }
383 
384         @Override
hashCode()385         public int hashCode() {
386             final int prime = 31;
387             int result = 1;
388             result = prime * result + mId;
389             result = prime * result + mImplementor.hashCode();
390             result = prime * result + mDescription.hashCode();
391             result = prime * result + mUuid.hashCode();
392             result = prime * result + mVersion;
393             result = prime * result + mSupportedModelArch.hashCode();
394             result = prime * result + mMaxSoundModels;
395             result = prime * result + mMaxKeyphrases;
396             result = prime * result + mMaxUsers;
397             result = prime * result + mRecognitionModes;
398             result = prime * result + (mSupportsCaptureTransition ? 1 : 0);
399             result = prime * result + mMaxBufferMillis;
400             result = prime * result + (mSupportsConcurrentCapture ? 1 : 0);
401             result = prime * result + mPowerConsumptionMw;
402             result = prime * result + (mReturnsTriggerInEvent ? 1 : 0);
403             result = prime * result + mAudioCapabilities;
404             return result;
405         }
406 
407         @Override
toString()408         public String toString() {
409             return "ModuleProperties [id=" + getId() + ", implementor=" + getImplementor()
410                     + ", description=" + getDescription() + ", uuid=" + getUuid()
411                     + ", version=" + getVersion() + " , supportedModelArch="
412                     + getSupportedModelArch() + ", maxSoundModels=" + getMaxSoundModels()
413                     + ", maxKeyphrases=" + getMaxKeyphrases() + ", maxUsers=" + getMaxUsers()
414                     + ", recognitionModes=" + getRecognitionModes() + ", supportsCaptureTransition="
415                     + isCaptureTransitionSupported() + ", maxBufferMs=" + getMaxBufferMillis()
416                     + ", supportsConcurrentCapture=" + isConcurrentCaptureSupported()
417                     + ", powerConsumptionMw=" + getPowerConsumptionMw()
418                     + ", returnsTriggerInEvent=" + isTriggerReturnedInEvent()
419                     + ", audioCapabilities=" + getAudioCapabilities() + "]";
420         }
421     }
422 
423     /**
424      * A SoundModel describes the attributes and contains the binary data used by the hardware
425      * implementation to detect a particular sound pattern.
426      * A specialized version {@link KeyphraseSoundModel} is defined for key phrase
427      * sound models.
428      */
429     public static class SoundModel {
430 
431         /** @hide */
432         @Retention(RetentionPolicy.SOURCE)
433         @IntDef({
434                 TYPE_GENERIC_SOUND,
435                 TYPE_KEYPHRASE,
436                 TYPE_UNKNOWN,
437         })
438         public @interface SoundModelType {}
439 
440         /**
441          * Undefined sound model type
442          * @hide
443          */
444         public static final int TYPE_UNKNOWN = -1;
445 
446         /** Keyphrase sound model */
447         public static final int TYPE_KEYPHRASE = 0;
448 
449         /**
450          * A generic sound model. Use this type only for non-keyphrase sound models such as
451          * ones that match a particular sound pattern.
452          */
453         public static final int TYPE_GENERIC_SOUND = 1;
454 
455         @NonNull
456         private final UUID mUuid;
457         @SoundModelType
458         private final int mType;
459         @NonNull
460         private final UUID mVendorUuid;
461         private final int mVersion;
462         @NonNull
463         private final byte[] mData;
464 
465         /** @hide */
SoundModel(@onNull UUID uuid, @Nullable UUID vendorUuid, @SoundModelType int type, @Nullable byte[] data, int version)466         public SoundModel(@NonNull UUID uuid, @Nullable UUID vendorUuid, @SoundModelType int type,
467                 @Nullable byte[] data, int version) {
468             this.mUuid = requireNonNull(uuid);
469             this.mVendorUuid = vendorUuid != null ? vendorUuid : new UUID(0, 0);
470             this.mType = type;
471             this.mVersion = version;
472             this.mData = data != null ? data : new byte[0];
473         }
474 
475         /** Unique sound model identifier */
476         @NonNull
getUuid()477         public UUID getUuid() {
478             return mUuid;
479         }
480 
481         /** Sound model type (e.g. TYPE_KEYPHRASE); */
482         @SoundModelType
getType()483         public int getType() {
484             return mType;
485         }
486 
487         /** Unique sound model vendor identifier */
488         @NonNull
getVendorUuid()489         public UUID getVendorUuid() {
490             return mVendorUuid;
491         }
492 
493         /** vendor specific version number of the model */
getVersion()494         public int getVersion() {
495             return mVersion;
496         }
497 
498         /** Opaque data. For use by vendor implementation and enrollment application */
499         @NonNull
getData()500         public byte[] getData() {
501             return mData;
502         }
503 
504         @Override
hashCode()505         public int hashCode() {
506             final int prime = 31;
507             int result = 1;
508             result = prime * result + getVersion();
509             result = prime * result + Arrays.hashCode(getData());
510             result = prime * result + getType();
511             result = prime * result + ((getUuid() == null) ? 0 : getUuid().hashCode());
512             result = prime * result + ((getVendorUuid() == null) ? 0 : getVendorUuid().hashCode());
513             return result;
514         }
515 
516         @Override
equals(Object obj)517         public boolean equals(Object obj) {
518             if (this == obj) {
519                 return true;
520             }
521             if (obj == null) {
522                 return false;
523             }
524             if (!(obj instanceof SoundModel)) {
525                 return false;
526             }
527             SoundModel other = (SoundModel) obj;
528             if (getType() != other.getType()) {
529                 return false;
530             }
531             if (getUuid() == null) {
532                 if (other.getUuid() != null) {
533                     return false;
534                 }
535             } else if (!getUuid().equals(other.getUuid())) {
536                 return false;
537             }
538             if (getVendorUuid() == null) {
539                 if (other.getVendorUuid() != null) {
540                     return false;
541                 }
542             } else if (!getVendorUuid().equals(other.getVendorUuid())) {
543                 return false;
544             }
545             if (!Arrays.equals(getData(), other.getData())) {
546                 return false;
547             }
548             if (getVersion() != other.getVersion()) {
549                 return false;
550             }
551             return true;
552         }
553     }
554 
555     /**
556      * A Keyphrase describes a key phrase that can be detected by a
557      * {@link KeyphraseSoundModel}
558      */
559     public static final class Keyphrase implements Parcelable {
560 
561         private final int mId;
562         @RecognitionModes
563         private final int mRecognitionModes;
564         @NonNull
565         private final Locale mLocale;
566         @NonNull
567         private final String mText;
568         @NonNull
569         private final int[] mUsers;
570 
571         /**
572          * Constructor for Keyphrase describes a key phrase that can be detected by a
573          * {@link KeyphraseSoundModel}
574          *
575          * @param id Unique keyphrase identifier for this keyphrase
576          * @param recognitionModes Bit field representation of recognition modes this keyphrase
577          *                         supports
578          * @param locale Locale of the keyphrase
579          * @param text Key phrase text
580          * @param users Users this key phrase has been trained for.
581          */
Keyphrase(int id, @RecognitionModes int recognitionModes, @NonNull Locale locale, @NonNull String text, @Nullable int[] users)582         public Keyphrase(int id, @RecognitionModes int recognitionModes, @NonNull Locale locale,
583                 @NonNull String text, @Nullable int[] users) {
584             this.mId = id;
585             this.mRecognitionModes = recognitionModes;
586             this.mLocale = requireNonNull(locale);
587             this.mText = requireNonNull(text);
588             this.mUsers = users != null ? users : new int[0];
589         }
590 
591         /** Unique identifier for this keyphrase */
getId()592         public int getId() {
593             return mId;
594         }
595 
596         /**
597          * Recognition modes supported for this key phrase in the model
598          *
599          * @see #RECOGNITION_MODE_VOICE_TRIGGER
600          * @see #RECOGNITION_MODE_USER_IDENTIFICATION
601          * @see #RECOGNITION_MODE_USER_AUTHENTICATION
602          * @see #RECOGNITION_MODE_GENERIC
603          */
604         @RecognitionModes
getRecognitionModes()605         public int getRecognitionModes() {
606             return mRecognitionModes;
607         }
608 
609         /** Locale of the keyphrase. */
610         @NonNull
getLocale()611         public Locale getLocale() {
612             return mLocale;
613         }
614 
615         /** Key phrase text */
616         @NonNull
getText()617         public String getText() {
618             return mText;
619         }
620 
621         /**
622          * Users this key phrase has been trained for. countains sound trigger specific user IDs
623          * derived from system user IDs {@link android.os.UserHandle#getIdentifier()}.
624          */
625         @NonNull
getUsers()626         public int[] getUsers() {
627             return mUsers;
628         }
629 
630         public static final @NonNull Parcelable.Creator<Keyphrase> CREATOR =
631                 new Parcelable.Creator<Keyphrase>() {
632             @NonNull
633             public Keyphrase createFromParcel(@NonNull Parcel in) {
634                 return Keyphrase.readFromParcel(in);
635             }
636 
637             @NonNull
638             public Keyphrase[] newArray(int size) {
639                 return new Keyphrase[size];
640             }
641         };
642 
643         /**
644          * Read from Parcel to generate keyphrase
645          */
646         @NonNull
readFromParcel(@onNull Parcel in)647         public static Keyphrase readFromParcel(@NonNull Parcel in) {
648             int id = in.readInt();
649             int recognitionModes = in.readInt();
650             Locale locale = Locale.forLanguageTag(in.readString());
651             String text = in.readString();
652             int[] users = null;
653             int numUsers = in.readInt();
654             if (numUsers >= 0) {
655                 users = new int[numUsers];
656                 in.readIntArray(users);
657             }
658             return new Keyphrase(id, recognitionModes, locale, text, users);
659         }
660 
661         @Override
writeToParcel(@onNull Parcel dest, int flags)662         public void writeToParcel(@NonNull Parcel dest, int flags) {
663             dest.writeInt(getId());
664             dest.writeInt(getRecognitionModes());
665             dest.writeString(getLocale().toLanguageTag());
666             dest.writeString(getText());
667             if (getUsers() != null) {
668                 dest.writeInt(getUsers().length);
669                 dest.writeIntArray(getUsers());
670             } else {
671                 dest.writeInt(-1);
672             }
673         }
674 
675         /** @hide */
676         @Override
describeContents()677         public int describeContents() {
678             return 0;
679         }
680 
681         @Override
hashCode()682         public int hashCode() {
683             final int prime = 31;
684             int result = 1;
685             result = prime * result + ((getText() == null) ? 0 : getText().hashCode());
686             result = prime * result + getId();
687             result = prime * result + ((getLocale() == null) ? 0 : getLocale().hashCode());
688             result = prime * result + getRecognitionModes();
689             result = prime * result + Arrays.hashCode(getUsers());
690             return result;
691         }
692 
693         @Override
equals(Object obj)694         public boolean equals(Object obj) {
695             if (this == obj) {
696                 return true;
697             }
698             if (obj == null) {
699                 return false;
700             }
701             if (getClass() != obj.getClass()) {
702                 return false;
703             }
704             Keyphrase other = (Keyphrase) obj;
705             if (getText() == null) {
706                 if (other.getText() != null) {
707                     return false;
708                 }
709             } else if (!getText().equals(other.getText())) {
710                 return false;
711             }
712             if (getId() != other.getId()) {
713                 return false;
714             }
715             if (getLocale() == null) {
716                 if (other.getLocale() != null) {
717                     return false;
718                 }
719             } else if (!getLocale().equals(other.getLocale())) {
720                 return false;
721             }
722             if (getRecognitionModes() != other.getRecognitionModes()) {
723                 return false;
724             }
725             if (!Arrays.equals(getUsers(), other.getUsers())) {
726                 return false;
727             }
728             return true;
729         }
730 
731         @Override
toString()732         public String toString() {
733             return "Keyphrase [id=" + getId() + ", recognitionModes=" + getRecognitionModes()
734                     + ", locale=" + getLocale().toLanguageTag() + ", text=" + getText()
735                     + ", users=" + Arrays.toString(getUsers()) + "]";
736         }
737     }
738 
739     /**
740      * A KeyphraseSoundModel is a specialized {@link SoundModel} for key phrases.
741      * It contains data needed by the hardware to detect a certain number of key phrases
742      * and the list of corresponding {@link Keyphrase} descriptors.
743      */
744     public static final class KeyphraseSoundModel extends SoundModel implements Parcelable {
745 
746         @NonNull
747         private final Keyphrase[] mKeyphrases;
748 
KeyphraseSoundModel( @onNull UUID uuid, @NonNull UUID vendorUuid, @Nullable byte[] data, @Nullable Keyphrase[] keyphrases, int version)749         public KeyphraseSoundModel(
750                 @NonNull UUID uuid, @NonNull UUID vendorUuid, @Nullable byte[] data,
751                 @Nullable Keyphrase[] keyphrases, int version) {
752             super(uuid, vendorUuid, TYPE_KEYPHRASE, data, version);
753             this.mKeyphrases = keyphrases != null ? keyphrases : new Keyphrase[0];
754         }
755 
KeyphraseSoundModel(@onNull UUID uuid, @NonNull UUID vendorUuid, @Nullable byte[] data, @Nullable Keyphrase[] keyphrases)756         public KeyphraseSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid,
757                 @Nullable byte[] data, @Nullable Keyphrase[] keyphrases) {
758             this(uuid, vendorUuid, data, keyphrases, -1);
759         }
760 
761         /** Key phrases in this sound model */
762         @NonNull
getKeyphrases()763         public Keyphrase[] getKeyphrases() {
764             return mKeyphrases;
765         }
766 
767         public static final @NonNull Parcelable.Creator<KeyphraseSoundModel> CREATOR =
768                 new Parcelable.Creator<KeyphraseSoundModel>() {
769             @NonNull
770             public KeyphraseSoundModel createFromParcel(@NonNull Parcel in) {
771                 return KeyphraseSoundModel.readFromParcel(in);
772             }
773 
774             @NonNull
775             public KeyphraseSoundModel[] newArray(int size) {
776                 return new KeyphraseSoundModel[size];
777             }
778         };
779 
780         /**
781          * Read from Parcel to generate KeyphraseSoundModel
782          */
783         @NonNull
readFromParcel(@onNull Parcel in)784         public static KeyphraseSoundModel readFromParcel(@NonNull Parcel in) {
785             UUID uuid = UUID.fromString(in.readString());
786             UUID vendorUuid = null;
787             int length = in.readInt();
788             if (length >= 0) {
789                 vendorUuid = UUID.fromString(in.readString());
790             }
791             int version = in.readInt();
792             byte[] data = in.readBlob();
793             Keyphrase[] keyphrases = in.createTypedArray(Keyphrase.CREATOR);
794             return new KeyphraseSoundModel(uuid, vendorUuid, data, keyphrases, version);
795         }
796 
797         /** @hide */
798         @Override
describeContents()799         public int describeContents() {
800             return 0;
801         }
802 
803         @Override
writeToParcel(@onNull Parcel dest, int flags)804         public void writeToParcel(@NonNull Parcel dest, int flags) {
805             dest.writeString(getUuid().toString());
806             if (getVendorUuid() == null) {
807                 dest.writeInt(-1);
808             } else {
809                 dest.writeInt(getVendorUuid().toString().length());
810                 dest.writeString(getVendorUuid().toString());
811             }
812             dest.writeInt(getVersion());
813             dest.writeBlob(getData());
814             dest.writeTypedArray(getKeyphrases(), flags);
815         }
816 
817         @Override
toString()818         public String toString() {
819             return "KeyphraseSoundModel [keyphrases=" + Arrays.toString(getKeyphrases())
820                     + ", uuid=" + getUuid() + ", vendorUuid=" + getVendorUuid()
821                     + ", type=" + getType()
822                     + ", data=" + (getData() == null ? 0 : getData().length)
823                     + ", version=" + getVersion() + "]";
824         }
825 
826         @Override
hashCode()827         public int hashCode() {
828             final int prime = 31;
829             int result = super.hashCode();
830             result = prime * result + Arrays.hashCode(getKeyphrases());
831             return result;
832         }
833 
834         @Override
equals(Object obj)835         public boolean equals(Object obj) {
836             if (this == obj) {
837                 return true;
838             }
839             if (!super.equals(obj)) {
840                 return false;
841             }
842             if (!(obj instanceof KeyphraseSoundModel)) {
843                 return false;
844             }
845             KeyphraseSoundModel other = (KeyphraseSoundModel) obj;
846             if (!Arrays.equals(getKeyphrases(), other.getKeyphrases())) {
847                 return false;
848             }
849             return true;
850         }
851     }
852 
853 
854     /*****************************************************************************
855      * A GenericSoundModel is a specialized {@link SoundModel} for non-voice sound
856      * patterns.
857      *
858      * @hide
859      ****************************************************************************/
860     public static class GenericSoundModel extends SoundModel implements Parcelable {
861 
862         public static final @android.annotation.NonNull Parcelable.Creator<GenericSoundModel> CREATOR
863                 = new Parcelable.Creator<GenericSoundModel>() {
864             public GenericSoundModel createFromParcel(Parcel in) {
865                 return GenericSoundModel.fromParcel(in);
866             }
867 
868             public GenericSoundModel[] newArray(int size) {
869                 return new GenericSoundModel[size];
870             }
871         };
872 
GenericSoundModel(@onNull UUID uuid, @NonNull UUID vendorUuid, @Nullable byte[] data, int version)873         public GenericSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid,
874                 @Nullable byte[] data, int version) {
875             super(uuid, vendorUuid, TYPE_GENERIC_SOUND, data, version);
876         }
877 
878         @UnsupportedAppUsage
GenericSoundModel(@onNull UUID uuid, @NonNull UUID vendorUuid, @Nullable byte[] data)879         public GenericSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid,
880                 @Nullable byte[] data) {
881             this(uuid, vendorUuid, data, -1);
882         }
883 
884         @Override
describeContents()885         public int describeContents() {
886             return 0;
887         }
888 
fromParcel(Parcel in)889         private static GenericSoundModel fromParcel(Parcel in) {
890             UUID uuid = UUID.fromString(in.readString());
891             UUID vendorUuid = null;
892             int length = in.readInt();
893             if (length >= 0) {
894                 vendorUuid = UUID.fromString(in.readString());
895             }
896             byte[] data = in.readBlob();
897             int version = in.readInt();
898             return new GenericSoundModel(uuid, vendorUuid, data, version);
899         }
900 
901         @Override
writeToParcel(Parcel dest, int flags)902         public void writeToParcel(Parcel dest, int flags) {
903             dest.writeString(getUuid().toString());
904             if (getVendorUuid() == null) {
905                 dest.writeInt(-1);
906             } else {
907                 dest.writeInt(getVendorUuid().toString().length());
908                 dest.writeString(getVendorUuid().toString());
909             }
910             dest.writeBlob(getData());
911             dest.writeInt(getVersion());
912         }
913 
914         @Override
toString()915         public String toString() {
916             return "GenericSoundModel [uuid=" + getUuid() + ", vendorUuid=" + getVendorUuid()
917                     + ", type=" + getType()
918                     + ", data=" + (getData() == null ? 0 : getData().length)
919                     + ", version=" + getVersion() + "]";
920         }
921     }
922 
923     /**
924      * A ModelParamRange is a representation of supported parameter range for a
925      * given loaded model.
926      */
927     public static final class ModelParamRange implements Parcelable {
928 
929         /**
930          * The inclusive start of supported range.
931          */
932         private final int mStart;
933 
934         /**
935          * The inclusive end of supported range.
936          */
937         private final int mEnd;
938 
939         /** @hide */
940         @TestApi
ModelParamRange(int start, int end)941         public ModelParamRange(int start, int end) {
942             this.mStart = start;
943             this.mEnd = end;
944         }
945 
946         /** @hide */
ModelParamRange(@onNull Parcel in)947         private ModelParamRange(@NonNull Parcel in) {
948             this.mStart = in.readInt();
949             this.mEnd = in.readInt();
950         }
951 
952         /**
953          * Get the beginning of the param range
954          *
955          * @return The inclusive start of the supported range.
956          */
getStart()957         public int getStart() {
958             return mStart;
959         }
960 
961         /**
962          * Get the end of the param range
963          *
964          * @return The inclusive end of the supported range.
965          */
getEnd()966         public int getEnd() {
967             return mEnd;
968         }
969 
970         @NonNull
971         public static final Creator<ModelParamRange> CREATOR =
972                 new Creator<ModelParamRange>() {
973                     @Override
974                     @NonNull
975                     public ModelParamRange createFromParcel(@NonNull Parcel in) {
976                         return new ModelParamRange(in);
977                     }
978 
979                     @Override
980                     @NonNull
981                     public ModelParamRange[] newArray(int size) {
982                         return new ModelParamRange[size];
983                     }
984                 };
985 
986         /** @hide */
987         @Override
describeContents()988         public int describeContents() {
989             return 0;
990         }
991 
992         /** @hide */
993         @Override
hashCode()994         public int hashCode() {
995             final int prime = 31;
996             int result = 1;
997             result = prime * result + (mStart);
998             result = prime * result + (mEnd);
999             return result;
1000         }
1001 
1002         @Override
equals(@ullable Object obj)1003         public boolean equals(@Nullable Object obj) {
1004             if (this == obj) {
1005                 return true;
1006             }
1007             if (obj == null) {
1008                 return false;
1009             }
1010             if (getClass() != obj.getClass()) {
1011                 return false;
1012             }
1013             ModelParamRange other = (ModelParamRange) obj;
1014             if (mStart != other.mStart) {
1015                 return false;
1016             }
1017             if (mEnd != other.mEnd) {
1018                 return false;
1019             }
1020             return true;
1021         }
1022 
1023         @Override
writeToParcel(@onNull Parcel dest, int flags)1024         public void writeToParcel(@NonNull Parcel dest, int flags) {
1025             dest.writeInt(mStart);
1026             dest.writeInt(mEnd);
1027         }
1028 
1029         @Override
1030         @NonNull
toString()1031         public String toString() {
1032             return "ModelParamRange [start=" + mStart + ", end=" + mEnd + "]";
1033         }
1034     }
1035 
1036     /**
1037      * Modes for key phrase recognition
1038      * @hide
1039      */
1040     @Retention(RetentionPolicy.SOURCE)
1041     @IntDef(flag = true, prefix = { "RECOGNITION_MODE_" }, value = {
1042             RECOGNITION_MODE_VOICE_TRIGGER,
1043             RECOGNITION_MODE_USER_IDENTIFICATION,
1044             RECOGNITION_MODE_USER_AUTHENTICATION,
1045             RECOGNITION_MODE_GENERIC
1046     })
1047     public @interface RecognitionModes {}
1048 
1049     /**
1050      * Trigger on recognition of a key phrase
1051      */
1052     public static final int RECOGNITION_MODE_VOICE_TRIGGER = 0x1;
1053     /**
1054      * Trigger only if one user is identified
1055      */
1056     public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 0x2;
1057     /**
1058      * Trigger only if one user is authenticated
1059      */
1060     public static final int RECOGNITION_MODE_USER_AUTHENTICATION = 0x4;
1061     /**
1062      * Generic (non-speech) recognition.
1063      */
1064     public static final int RECOGNITION_MODE_GENERIC = 0x8;
1065 
1066     /**
1067      *  Status codes for {@link RecognitionEvent}
1068      */
1069     /**
1070      * Recognition success
1071      *
1072      * @hide
1073      */
1074     public static final int RECOGNITION_STATUS_SUCCESS = 0;
1075     /**
1076      * Recognition aborted (e.g. capture preempted by anotehr use case
1077      *
1078      * @hide
1079      */
1080     public static final int RECOGNITION_STATUS_ABORT = 1;
1081     /**
1082      * Recognition failure
1083      *
1084      * @hide
1085      */
1086     public static final int RECOGNITION_STATUS_FAILURE = 2;
1087     /**
1088      * Recognition event was triggered by a getModelState request, not by the
1089      * DSP.
1090      *
1091      * @hide
1092      */
1093     public static final int RECOGNITION_STATUS_GET_STATE_RESPONSE = 3;
1094 
1095     /**
1096      *  A RecognitionEvent is provided by the
1097      *  {@code StatusListener#onRecognition(RecognitionEvent)}
1098      *  callback upon recognition success or failure.
1099      */
1100     public static class RecognitionEvent {
1101         /**
1102          * Recognition status e.g RECOGNITION_STATUS_SUCCESS
1103          *
1104          * @hide
1105          */
1106         @UnsupportedAppUsage
1107         public final int status;
1108         /**
1109          *
1110          * Sound Model corresponding to this event callback
1111          *
1112          * @hide
1113          */
1114         @UnsupportedAppUsage
1115         public final int soundModelHandle;
1116         /**
1117          * True if it is possible to capture audio from this utterance buffered by the hardware
1118          *
1119          * @hide
1120          */
1121         @UnsupportedAppUsage
1122         public final boolean captureAvailable;
1123         /**
1124          * Audio session ID to be used when capturing the utterance with an AudioRecord
1125          * if captureAvailable() is true.
1126          *
1127          * @hide
1128          */
1129         @UnsupportedAppUsage
1130         public final int captureSession;
1131         /**
1132          * Delay in ms between end of model detection and start of audio available for capture.
1133          * A negative value is possible (e.g. if keyphrase is also available for capture)
1134          *
1135          * @hide
1136          */
1137         public final int captureDelayMs;
1138         /**
1139          * Duration in ms of audio captured before the start of the trigger. 0 if none.
1140          *
1141          * @hide
1142          */
1143         public final int capturePreambleMs;
1144         /**
1145          * True if  the trigger (key phrase capture is present in binary data
1146          *
1147          * @hide
1148          */
1149         public final boolean triggerInData;
1150         /**
1151          * Audio format of either the trigger in event data or to use for capture of the
1152          * rest of the utterance
1153          *
1154          * @hide
1155          */
1156         @NonNull
1157         public final AudioFormat captureFormat;
1158         /**
1159          * Opaque data for use by system applications who know about voice engine internals,
1160          * typically during enrollment.
1161          *
1162          * @hide
1163          */
1164         @UnsupportedAppUsage
1165         @NonNull
1166         public final byte[] data;
1167 
1168         /** @hide */
1169         @TestApi
1170         @UnsupportedAppUsage
RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable, int captureSession, int captureDelayMs, int capturePreambleMs, boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data)1171         public RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
1172                 int captureSession, int captureDelayMs, int capturePreambleMs,
1173                 boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data) {
1174             this.status = status;
1175             this.soundModelHandle = soundModelHandle;
1176             this.captureAvailable = captureAvailable;
1177             this.captureSession = captureSession;
1178             this.captureDelayMs = captureDelayMs;
1179             this.capturePreambleMs = capturePreambleMs;
1180             this.triggerInData = triggerInData;
1181             this.captureFormat = requireNonNull(captureFormat);
1182             this.data = data != null ? data : new byte[0];
1183         }
1184 
1185         /**
1186          * Check if is possible to capture audio from this utterance buffered by the hardware.
1187          *
1188          * @return {@code true} iff a capturing is possible
1189          */
isCaptureAvailable()1190         public boolean isCaptureAvailable() {
1191             return captureAvailable;
1192         }
1193 
1194         /**
1195          * Get the audio format of either the trigger in event data or to use for capture of the
1196          * rest of the utterance
1197          *
1198          * @return the audio format
1199          */
getCaptureFormat()1200         @Nullable public AudioFormat getCaptureFormat() {
1201             return captureFormat;
1202         }
1203 
1204         /**
1205          * Get Audio session ID to be used when capturing the utterance with an {@link AudioRecord}
1206          * if {@link #isCaptureAvailable()} is true.
1207          *
1208          * @return The id of the capture session
1209          */
getCaptureSession()1210         public int getCaptureSession() {
1211             return captureSession;
1212         }
1213 
1214         /**
1215          * Get the opaque data for use by system applications who know about voice engine
1216          * internals, typically during enrollment.
1217          *
1218          * @return The data of the event
1219          */
1220         @SuppressLint("MissingNullability")
getData()1221         public byte[] getData() {
1222             return data;
1223         }
1224 
1225         /** @hide */
1226         public static final @android.annotation.NonNull Parcelable.Creator<RecognitionEvent> CREATOR
1227                 = new Parcelable.Creator<RecognitionEvent>() {
1228             public RecognitionEvent createFromParcel(Parcel in) {
1229                 return RecognitionEvent.fromParcel(in);
1230             }
1231 
1232             public RecognitionEvent[] newArray(int size) {
1233                 return new RecognitionEvent[size];
1234             }
1235         };
1236 
1237         /** @hide */
fromParcel(Parcel in)1238         protected static RecognitionEvent fromParcel(Parcel in) {
1239             int status = in.readInt();
1240             int soundModelHandle = in.readInt();
1241             boolean captureAvailable = in.readByte() == 1;
1242             int captureSession = in.readInt();
1243             int captureDelayMs = in.readInt();
1244             int capturePreambleMs = in.readInt();
1245             boolean triggerInData = in.readByte() == 1;
1246             AudioFormat captureFormat = null;
1247             if (in.readByte() == 1) {
1248                 int sampleRate = in.readInt();
1249                 int encoding = in.readInt();
1250                 int channelMask = in.readInt();
1251                 captureFormat = (new AudioFormat.Builder())
1252                         .setChannelMask(channelMask)
1253                         .setEncoding(encoding)
1254                         .setSampleRate(sampleRate)
1255                         .build();
1256             }
1257             byte[] data = in.readBlob();
1258             return new RecognitionEvent(status, soundModelHandle, captureAvailable, captureSession,
1259                     captureDelayMs, capturePreambleMs, triggerInData, captureFormat, data);
1260         }
1261 
1262         /** @hide */
describeContents()1263         public int describeContents() {
1264             return 0;
1265         }
1266 
1267         /** @hide */
writeToParcel(Parcel dest, int flags)1268         public void writeToParcel(Parcel dest, int flags) {
1269             dest.writeInt(status);
1270             dest.writeInt(soundModelHandle);
1271             dest.writeByte((byte) (captureAvailable ? 1 : 0));
1272             dest.writeInt(captureSession);
1273             dest.writeInt(captureDelayMs);
1274             dest.writeInt(capturePreambleMs);
1275             dest.writeByte((byte) (triggerInData ? 1 : 0));
1276             if (captureFormat != null) {
1277                 dest.writeByte((byte)1);
1278                 dest.writeInt(captureFormat.getSampleRate());
1279                 dest.writeInt(captureFormat.getEncoding());
1280                 dest.writeInt(captureFormat.getChannelMask());
1281             } else {
1282                 dest.writeByte((byte)0);
1283             }
1284             dest.writeBlob(data);
1285         }
1286 
1287         @Override
hashCode()1288         public int hashCode() {
1289             final int prime = 31;
1290             int result = 1;
1291             result = prime * result + (captureAvailable ? 1231 : 1237);
1292             result = prime * result + captureDelayMs;
1293             result = prime * result + capturePreambleMs;
1294             result = prime * result + captureSession;
1295             result = prime * result + (triggerInData ? 1231 : 1237);
1296             if (captureFormat != null) {
1297                 result = prime * result + captureFormat.getSampleRate();
1298                 result = prime * result + captureFormat.getEncoding();
1299                 result = prime * result + captureFormat.getChannelMask();
1300             }
1301             result = prime * result + Arrays.hashCode(data);
1302             result = prime * result + soundModelHandle;
1303             result = prime * result + status;
1304             return result;
1305         }
1306 
1307         @Override
equals(@ullable Object obj)1308         public boolean equals(@Nullable Object obj) {
1309             if (this == obj)
1310                 return true;
1311             if (obj == null)
1312                 return false;
1313             if (getClass() != obj.getClass())
1314                 return false;
1315             RecognitionEvent other = (RecognitionEvent) obj;
1316             if (captureAvailable != other.captureAvailable)
1317                 return false;
1318             if (captureDelayMs != other.captureDelayMs)
1319                 return false;
1320             if (capturePreambleMs != other.capturePreambleMs)
1321                 return false;
1322             if (captureSession != other.captureSession)
1323                 return false;
1324             if (!Arrays.equals(data, other.data))
1325                 return false;
1326             if (soundModelHandle != other.soundModelHandle)
1327                 return false;
1328             if (status != other.status)
1329                 return false;
1330             if (triggerInData != other.triggerInData)
1331                 return false;
1332             if (captureFormat == null) {
1333                 if (other.captureFormat != null)
1334                     return false;
1335             } else {
1336                 if (other.captureFormat == null)
1337                     return false;
1338                 if (captureFormat.getSampleRate() != other.captureFormat.getSampleRate())
1339                     return false;
1340                 if (captureFormat.getEncoding() != other.captureFormat.getEncoding())
1341                     return false;
1342                 if (captureFormat.getChannelMask() != other.captureFormat.getChannelMask())
1343                     return false;
1344             }
1345             return true;
1346         }
1347 
1348         @NonNull
1349         @Override
toString()1350         public String toString() {
1351             return "RecognitionEvent [status=" + status + ", soundModelHandle=" + soundModelHandle
1352                     + ", captureAvailable=" + captureAvailable + ", captureSession="
1353                     + captureSession + ", captureDelayMs=" + captureDelayMs
1354                     + ", capturePreambleMs=" + capturePreambleMs
1355                     + ", triggerInData=" + triggerInData
1356                     + ((captureFormat == null) ? "" :
1357                         (", sampleRate=" + captureFormat.getSampleRate()))
1358                     + ((captureFormat == null) ? "" :
1359                         (", encoding=" + captureFormat.getEncoding()))
1360                     + ((captureFormat == null) ? "" :
1361                         (", channelMask=" + captureFormat.getChannelMask()))
1362                     + ", data=" + (data == null ? 0 : data.length) + "]";
1363         }
1364     }
1365 
1366     /**
1367      *  A RecognitionConfig is provided to
1368      *  {@link SoundTriggerModule#startRecognition(int, RecognitionConfig)} to configure the
1369      *  recognition request.
1370      *
1371      *  @hide
1372      */
1373     public static class RecognitionConfig implements Parcelable {
1374         /** True if the DSP should capture the trigger sound and make it available for further
1375          * capture. */
1376         @UnsupportedAppUsage
1377         public final boolean captureRequested;
1378         /**
1379          * True if the service should restart listening after the DSP triggers.
1380          * Note: This config flag is currently used at the service layer rather than by the DSP.
1381          */
1382         public final boolean allowMultipleTriggers;
1383         /** List of all keyphrases in the sound model for which recognition should be performed with
1384          * options for each keyphrase. */
1385         @UnsupportedAppUsage
1386         @NonNull
1387         public final KeyphraseRecognitionExtra keyphrases[];
1388         /** Opaque data for use by system applications who know about voice engine internals,
1389          * typically during enrollment. */
1390         @UnsupportedAppUsage
1391         @NonNull
1392         public final byte[] data;
1393 
1394         /**
1395          * Bit field encoding of the AudioCapabilities
1396          * supported by the firmware.
1397          */
1398         @ModuleProperties.AudioCapabilities
1399         public final int audioCapabilities;
1400 
RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers, @Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data, int audioCapabilities)1401         public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers,
1402                 @Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data,
1403                 int audioCapabilities) {
1404             this.captureRequested = captureRequested;
1405             this.allowMultipleTriggers = allowMultipleTriggers;
1406             this.keyphrases = keyphrases != null ? keyphrases : new KeyphraseRecognitionExtra[0];
1407             this.data = data != null ? data : new byte[0];
1408             this.audioCapabilities = audioCapabilities;
1409         }
1410 
1411         @UnsupportedAppUsage
RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers, @Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data)1412         public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers,
1413                 @Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data) {
1414             this(captureRequested, allowMultipleTriggers, keyphrases, data, 0);
1415         }
1416 
1417         public static final @android.annotation.NonNull Parcelable.Creator<RecognitionConfig> CREATOR
1418                 = new Parcelable.Creator<RecognitionConfig>() {
1419             public RecognitionConfig createFromParcel(Parcel in) {
1420                 return RecognitionConfig.fromParcel(in);
1421             }
1422 
1423             public RecognitionConfig[] newArray(int size) {
1424                 return new RecognitionConfig[size];
1425             }
1426         };
1427 
fromParcel(Parcel in)1428         private static RecognitionConfig fromParcel(Parcel in) {
1429             boolean captureRequested = in.readByte() == 1;
1430             boolean allowMultipleTriggers = in.readByte() == 1;
1431             KeyphraseRecognitionExtra[] keyphrases =
1432                     in.createTypedArray(KeyphraseRecognitionExtra.CREATOR);
1433             byte[] data = in.readBlob();
1434             int audioCapabilities = in.readInt();
1435             return new RecognitionConfig(captureRequested, allowMultipleTriggers, keyphrases, data,
1436                     audioCapabilities);
1437         }
1438 
1439         @Override
writeToParcel(Parcel dest, int flags)1440         public void writeToParcel(Parcel dest, int flags) {
1441             dest.writeByte((byte) (captureRequested ? 1 : 0));
1442             dest.writeByte((byte) (allowMultipleTriggers ? 1 : 0));
1443             dest.writeTypedArray(keyphrases, flags);
1444             dest.writeBlob(data);
1445             dest.writeInt(audioCapabilities);
1446         }
1447 
1448         @Override
describeContents()1449         public int describeContents() {
1450             return 0;
1451         }
1452 
1453         @Override
toString()1454         public String toString() {
1455             return "RecognitionConfig [captureRequested=" + captureRequested
1456                     + ", allowMultipleTriggers=" + allowMultipleTriggers + ", keyphrases="
1457                     + Arrays.toString(keyphrases) + ", data=" + Arrays.toString(data)
1458                     + ", audioCapabilities=" + Integer.toHexString(audioCapabilities) + "]";
1459         }
1460     }
1461 
1462     /**
1463      * Confidence level for users defined in a keyphrase.
1464      * - The confidence level is expressed in percent (0% -100%).
1465      * When used in a {@link KeyphraseRecognitionEvent} it indicates the detected confidence level
1466      * When used in a {@link RecognitionConfig} it indicates the minimum confidence level that
1467      * should trigger a recognition.
1468      * - The user ID is derived from the system ID {@link android.os.UserHandle#getIdentifier()}.
1469      *
1470      * @hide
1471      */
1472     public static class ConfidenceLevel implements Parcelable {
1473         @UnsupportedAppUsage
1474         public final int userId;
1475         @UnsupportedAppUsage
1476         public final int confidenceLevel;
1477 
1478         @UnsupportedAppUsage
ConfidenceLevel(int userId, int confidenceLevel)1479         public ConfidenceLevel(int userId, int confidenceLevel) {
1480             this.userId = userId;
1481             this.confidenceLevel = confidenceLevel;
1482         }
1483 
1484         public static final @android.annotation.NonNull Parcelable.Creator<ConfidenceLevel> CREATOR
1485                 = new Parcelable.Creator<ConfidenceLevel>() {
1486             public ConfidenceLevel createFromParcel(Parcel in) {
1487                 return ConfidenceLevel.fromParcel(in);
1488             }
1489 
1490             public ConfidenceLevel[] newArray(int size) {
1491                 return new ConfidenceLevel[size];
1492             }
1493         };
1494 
fromParcel(Parcel in)1495         private static ConfidenceLevel fromParcel(Parcel in) {
1496             int userId = in.readInt();
1497             int confidenceLevel = in.readInt();
1498             return new ConfidenceLevel(userId, confidenceLevel);
1499         }
1500 
1501         @Override
writeToParcel(Parcel dest, int flags)1502         public void writeToParcel(Parcel dest, int flags) {
1503             dest.writeInt(userId);
1504             dest.writeInt(confidenceLevel);
1505         }
1506 
1507         @Override
describeContents()1508         public int describeContents() {
1509             return 0;
1510         }
1511 
1512         @Override
hashCode()1513         public int hashCode() {
1514             final int prime = 31;
1515             int result = 1;
1516             result = prime * result + confidenceLevel;
1517             result = prime * result + userId;
1518             return result;
1519         }
1520 
1521         @Override
equals(Object obj)1522         public boolean equals(Object obj) {
1523             if (this == obj)
1524                 return true;
1525             if (obj == null)
1526                 return false;
1527             if (getClass() != obj.getClass())
1528                 return false;
1529             ConfidenceLevel other = (ConfidenceLevel) obj;
1530             if (confidenceLevel != other.confidenceLevel)
1531                 return false;
1532             if (userId != other.userId)
1533                 return false;
1534             return true;
1535         }
1536 
1537         @Override
toString()1538         public String toString() {
1539             return "ConfidenceLevel [userId=" + userId
1540                     + ", confidenceLevel=" + confidenceLevel + "]";
1541         }
1542     }
1543 
1544     /**
1545      *  Additional data conveyed by a {@link KeyphraseRecognitionEvent}
1546      *  for a key phrase detection.
1547      *
1548      * @hide
1549      */
1550     public static class KeyphraseRecognitionExtra implements Parcelable {
1551         /** The keyphrase ID */
1552         @UnsupportedAppUsage
1553         public final int id;
1554 
1555         /** Recognition modes matched for this event */
1556         @UnsupportedAppUsage
1557         public final int recognitionModes;
1558 
1559         /** Confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER when user identification
1560          * is not performed */
1561         @UnsupportedAppUsage
1562         public final int coarseConfidenceLevel;
1563 
1564         /** Confidence levels for all users recognized (KeyphraseRecognitionEvent) or to
1565          * be recognized (RecognitionConfig) */
1566         @UnsupportedAppUsage
1567         @NonNull
1568         public final ConfidenceLevel[] confidenceLevels;
1569 
1570         @UnsupportedAppUsage
KeyphraseRecognitionExtra(int id, int recognitionModes, int coarseConfidenceLevel, @Nullable ConfidenceLevel[] confidenceLevels)1571         public KeyphraseRecognitionExtra(int id, int recognitionModes, int coarseConfidenceLevel,
1572                 @Nullable ConfidenceLevel[] confidenceLevels) {
1573             this.id = id;
1574             this.recognitionModes = recognitionModes;
1575             this.coarseConfidenceLevel = coarseConfidenceLevel;
1576             this.confidenceLevels =
1577                     confidenceLevels != null ? confidenceLevels : new ConfidenceLevel[0];
1578         }
1579 
1580         public static final @android.annotation.NonNull Parcelable.Creator<KeyphraseRecognitionExtra> CREATOR
1581                 = new Parcelable.Creator<KeyphraseRecognitionExtra>() {
1582             public KeyphraseRecognitionExtra createFromParcel(Parcel in) {
1583                 return KeyphraseRecognitionExtra.fromParcel(in);
1584             }
1585 
1586             public KeyphraseRecognitionExtra[] newArray(int size) {
1587                 return new KeyphraseRecognitionExtra[size];
1588             }
1589         };
1590 
fromParcel(Parcel in)1591         private static KeyphraseRecognitionExtra fromParcel(Parcel in) {
1592             int id = in.readInt();
1593             int recognitionModes = in.readInt();
1594             int coarseConfidenceLevel = in.readInt();
1595             ConfidenceLevel[] confidenceLevels = in.createTypedArray(ConfidenceLevel.CREATOR);
1596             return new KeyphraseRecognitionExtra(id, recognitionModes, coarseConfidenceLevel,
1597                     confidenceLevels);
1598         }
1599 
1600         @Override
writeToParcel(Parcel dest, int flags)1601         public void writeToParcel(Parcel dest, int flags) {
1602             dest.writeInt(id);
1603             dest.writeInt(recognitionModes);
1604             dest.writeInt(coarseConfidenceLevel);
1605             dest.writeTypedArray(confidenceLevels, flags);
1606         }
1607 
1608         @Override
describeContents()1609         public int describeContents() {
1610             return 0;
1611         }
1612 
1613         @Override
hashCode()1614         public int hashCode() {
1615             final int prime = 31;
1616             int result = 1;
1617             result = prime * result + Arrays.hashCode(confidenceLevels);
1618             result = prime * result + id;
1619             result = prime * result + recognitionModes;
1620             result = prime * result + coarseConfidenceLevel;
1621             return result;
1622         }
1623 
1624         @Override
equals(Object obj)1625         public boolean equals(Object obj) {
1626             if (this == obj)
1627                 return true;
1628             if (obj == null)
1629                 return false;
1630             if (getClass() != obj.getClass())
1631                 return false;
1632             KeyphraseRecognitionExtra other = (KeyphraseRecognitionExtra) obj;
1633             if (!Arrays.equals(confidenceLevels, other.confidenceLevels))
1634                 return false;
1635             if (id != other.id)
1636                 return false;
1637             if (recognitionModes != other.recognitionModes)
1638                 return false;
1639             if (coarseConfidenceLevel != other.coarseConfidenceLevel)
1640                 return false;
1641             return true;
1642         }
1643 
1644         @Override
toString()1645         public String toString() {
1646             return "KeyphraseRecognitionExtra [id=" + id + ", recognitionModes=" + recognitionModes
1647                     + ", coarseConfidenceLevel=" + coarseConfidenceLevel
1648                     + ", confidenceLevels=" + Arrays.toString(confidenceLevels) + "]";
1649         }
1650     }
1651 
1652     /**
1653      *  Specialized {@link RecognitionEvent} for a key phrase detection.
1654      *
1655      *  @hide
1656      */
1657     public static class KeyphraseRecognitionEvent extends RecognitionEvent implements Parcelable {
1658         /** Indicates if the key phrase is present in the buffered audio available for capture */
1659         @UnsupportedAppUsage
1660         @NonNull
1661         public final KeyphraseRecognitionExtra[] keyphraseExtras;
1662 
1663         @UnsupportedAppUsage
KeyphraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable, int captureSession, int captureDelayMs, int capturePreambleMs, boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data, @Nullable KeyphraseRecognitionExtra[] keyphraseExtras)1664         public KeyphraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
1665                int captureSession, int captureDelayMs, int capturePreambleMs,
1666                boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data,
1667                @Nullable KeyphraseRecognitionExtra[] keyphraseExtras) {
1668             super(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs,
1669                   capturePreambleMs, triggerInData, captureFormat, data);
1670             this.keyphraseExtras =
1671                     keyphraseExtras != null ? keyphraseExtras : new KeyphraseRecognitionExtra[0];
1672         }
1673 
1674         public static final @android.annotation.NonNull Parcelable.Creator<KeyphraseRecognitionEvent> CREATOR
1675                 = new Parcelable.Creator<KeyphraseRecognitionEvent>() {
1676             public KeyphraseRecognitionEvent createFromParcel(Parcel in) {
1677                 return KeyphraseRecognitionEvent.fromParcelForKeyphrase(in);
1678             }
1679 
1680             public KeyphraseRecognitionEvent[] newArray(int size) {
1681                 return new KeyphraseRecognitionEvent[size];
1682             }
1683         };
1684 
fromParcelForKeyphrase(Parcel in)1685         private static KeyphraseRecognitionEvent fromParcelForKeyphrase(Parcel in) {
1686             int status = in.readInt();
1687             int soundModelHandle = in.readInt();
1688             boolean captureAvailable = in.readByte() == 1;
1689             int captureSession = in.readInt();
1690             int captureDelayMs = in.readInt();
1691             int capturePreambleMs = in.readInt();
1692             boolean triggerInData = in.readByte() == 1;
1693             AudioFormat captureFormat = null;
1694             if (in.readByte() == 1) {
1695                 int sampleRate = in.readInt();
1696                 int encoding = in.readInt();
1697                 int channelMask = in.readInt();
1698                 captureFormat = (new AudioFormat.Builder())
1699                     .setChannelMask(channelMask)
1700                     .setEncoding(encoding)
1701                     .setSampleRate(sampleRate)
1702                     .build();
1703             }
1704             byte[] data = in.readBlob();
1705             KeyphraseRecognitionExtra[] keyphraseExtras =
1706                     in.createTypedArray(KeyphraseRecognitionExtra.CREATOR);
1707             return new KeyphraseRecognitionEvent(status, soundModelHandle, captureAvailable,
1708                     captureSession, captureDelayMs, capturePreambleMs, triggerInData,
1709                     captureFormat, data, keyphraseExtras);
1710         }
1711 
1712         @Override
writeToParcel(Parcel dest, int flags)1713         public void writeToParcel(Parcel dest, int flags) {
1714             dest.writeInt(status);
1715             dest.writeInt(soundModelHandle);
1716             dest.writeByte((byte) (captureAvailable ? 1 : 0));
1717             dest.writeInt(captureSession);
1718             dest.writeInt(captureDelayMs);
1719             dest.writeInt(capturePreambleMs);
1720             dest.writeByte((byte) (triggerInData ? 1 : 0));
1721             if (captureFormat != null) {
1722                 dest.writeByte((byte)1);
1723                 dest.writeInt(captureFormat.getSampleRate());
1724                 dest.writeInt(captureFormat.getEncoding());
1725                 dest.writeInt(captureFormat.getChannelMask());
1726             } else {
1727                 dest.writeByte((byte)0);
1728             }
1729             dest.writeBlob(data);
1730             dest.writeTypedArray(keyphraseExtras, flags);
1731         }
1732 
1733         @Override
describeContents()1734         public int describeContents() {
1735             return 0;
1736         }
1737 
1738         @Override
hashCode()1739         public int hashCode() {
1740             final int prime = 31;
1741             int result = super.hashCode();
1742             result = prime * result + Arrays.hashCode(keyphraseExtras);
1743             return result;
1744         }
1745 
1746         @Override
equals(Object obj)1747         public boolean equals(Object obj) {
1748             if (this == obj)
1749                 return true;
1750             if (!super.equals(obj))
1751                 return false;
1752             if (getClass() != obj.getClass())
1753                 return false;
1754             KeyphraseRecognitionEvent other = (KeyphraseRecognitionEvent) obj;
1755             if (!Arrays.equals(keyphraseExtras, other.keyphraseExtras))
1756                 return false;
1757             return true;
1758         }
1759 
1760         @Override
toString()1761         public String toString() {
1762             return "KeyphraseRecognitionEvent [keyphraseExtras=" + Arrays.toString(keyphraseExtras)
1763                     + ", status=" + status
1764                     + ", soundModelHandle=" + soundModelHandle + ", captureAvailable="
1765                     + captureAvailable + ", captureSession=" + captureSession + ", captureDelayMs="
1766                     + captureDelayMs + ", capturePreambleMs=" + capturePreambleMs
1767                     + ", triggerInData=" + triggerInData
1768                     + ((captureFormat == null) ? "" :
1769                         (", sampleRate=" + captureFormat.getSampleRate()))
1770                     + ((captureFormat == null) ? "" :
1771                         (", encoding=" + captureFormat.getEncoding()))
1772                     + ((captureFormat == null) ? "" :
1773                         (", channelMask=" + captureFormat.getChannelMask()))
1774                     + ", data=" + (data == null ? 0 : data.length) + "]";
1775         }
1776     }
1777 
1778     /**
1779      * Sub-class of RecognitionEvent specifically for sound-trigger based sound
1780      * models(non-keyphrase). Currently does not contain any additional fields.
1781      *
1782      * @hide
1783      */
1784     public static class GenericRecognitionEvent extends RecognitionEvent implements Parcelable {
1785         @UnsupportedAppUsage
GenericRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable, int captureSession, int captureDelayMs, int capturePreambleMs, boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data)1786         public GenericRecognitionEvent(int status, int soundModelHandle,
1787                 boolean captureAvailable, int captureSession, int captureDelayMs,
1788                 int capturePreambleMs, boolean triggerInData, @NonNull AudioFormat captureFormat,
1789                 @Nullable byte[] data) {
1790             super(status, soundModelHandle, captureAvailable, captureSession,
1791                     captureDelayMs, capturePreambleMs, triggerInData, captureFormat,
1792                     data);
1793         }
1794 
1795         public static final @android.annotation.NonNull Parcelable.Creator<GenericRecognitionEvent> CREATOR
1796                 = new Parcelable.Creator<GenericRecognitionEvent>() {
1797             public GenericRecognitionEvent createFromParcel(Parcel in) {
1798                 return GenericRecognitionEvent.fromParcelForGeneric(in);
1799             }
1800 
1801             public GenericRecognitionEvent[] newArray(int size) {
1802                 return new GenericRecognitionEvent[size];
1803             }
1804         };
1805 
fromParcelForGeneric(Parcel in)1806         private static GenericRecognitionEvent fromParcelForGeneric(Parcel in) {
1807             RecognitionEvent event = RecognitionEvent.fromParcel(in);
1808             return new GenericRecognitionEvent(event.status, event.soundModelHandle,
1809                     event.captureAvailable, event.captureSession, event.captureDelayMs,
1810                     event.capturePreambleMs, event.triggerInData, event.captureFormat, event.data);
1811         }
1812 
1813         @Override
equals(Object obj)1814         public boolean equals(Object obj) {
1815             if (this == obj)
1816                 return true;
1817             if (obj == null)
1818                 return false;
1819             if (getClass() != obj.getClass()) return false;
1820             RecognitionEvent other = (RecognitionEvent) obj;
1821             return super.equals(obj);
1822         }
1823 
1824         @Override
toString()1825         public String toString() {
1826             return "GenericRecognitionEvent ::" + super.toString();
1827         }
1828     }
1829 
1830     /**
1831      *  Status codes for {@link SoundModelEvent}
1832      */
1833     /**
1834      * Sound Model was updated
1835      *
1836      * @hide
1837      */
1838     public static final int SOUNDMODEL_STATUS_UPDATED = 0;
1839 
1840     /**
1841      *  A SoundModelEvent is provided by the
1842      *  {@link StatusListener#onSoundModelUpdate(SoundModelEvent)}
1843      *  callback when a sound model has been updated by the implementation
1844      *
1845      *  @hide
1846      */
1847     public static class SoundModelEvent implements Parcelable {
1848         /** Status e.g {@link #SOUNDMODEL_STATUS_UPDATED} */
1849         public final int status;
1850         /** The updated sound model handle */
1851         public final int soundModelHandle;
1852         /** New sound model data */
1853         @NonNull
1854         public final byte[] data;
1855 
1856         @UnsupportedAppUsage
SoundModelEvent(int status, int soundModelHandle, @Nullable byte[] data)1857         SoundModelEvent(int status, int soundModelHandle, @Nullable byte[] data) {
1858             this.status = status;
1859             this.soundModelHandle = soundModelHandle;
1860             this.data = data != null ? data : new byte[0];
1861         }
1862 
1863         public static final @android.annotation.NonNull Parcelable.Creator<SoundModelEvent> CREATOR
1864                 = new Parcelable.Creator<SoundModelEvent>() {
1865             public SoundModelEvent createFromParcel(Parcel in) {
1866                 return SoundModelEvent.fromParcel(in);
1867             }
1868 
1869             public SoundModelEvent[] newArray(int size) {
1870                 return new SoundModelEvent[size];
1871             }
1872         };
1873 
fromParcel(Parcel in)1874         private static SoundModelEvent fromParcel(Parcel in) {
1875             int status = in.readInt();
1876             int soundModelHandle = in.readInt();
1877             byte[] data = in.readBlob();
1878             return new SoundModelEvent(status, soundModelHandle, data);
1879         }
1880 
1881         @Override
describeContents()1882         public int describeContents() {
1883             return 0;
1884         }
1885 
1886         @Override
writeToParcel(Parcel dest, int flags)1887         public void writeToParcel(Parcel dest, int flags) {
1888             dest.writeInt(status);
1889             dest.writeInt(soundModelHandle);
1890             dest.writeBlob(data);
1891         }
1892 
1893         @Override
hashCode()1894         public int hashCode() {
1895             final int prime = 31;
1896             int result = 1;
1897             result = prime * result + Arrays.hashCode(data);
1898             result = prime * result + soundModelHandle;
1899             result = prime * result + status;
1900             return result;
1901         }
1902 
1903         @Override
equals(Object obj)1904         public boolean equals(Object obj) {
1905             if (this == obj)
1906                 return true;
1907             if (obj == null)
1908                 return false;
1909             if (getClass() != obj.getClass())
1910                 return false;
1911             SoundModelEvent other = (SoundModelEvent) obj;
1912             if (!Arrays.equals(data, other.data))
1913                 return false;
1914             if (soundModelHandle != other.soundModelHandle)
1915                 return false;
1916             if (status != other.status)
1917                 return false;
1918             return true;
1919         }
1920 
1921         @Override
toString()1922         public String toString() {
1923             return "SoundModelEvent [status=" + status + ", soundModelHandle=" + soundModelHandle
1924                     + ", data=" + (data == null ? 0 : data.length) + "]";
1925         }
1926     }
1927 
1928     /**
1929      *  Native service state. {@link StatusListener#onServiceStateChange(int)}
1930      */
1931     // Keep in sync with system/core/include/system/sound_trigger.h
1932     /**
1933      * Sound trigger service is enabled
1934      *
1935      * @hide
1936      */
1937     public static final int SERVICE_STATE_ENABLED = 0;
1938     /**
1939      * Sound trigger service is disabled
1940      *
1941      * @hide
1942      */
1943     public static final int SERVICE_STATE_DISABLED = 1;
1944     private static Object mServiceLock = new Object();
1945     private static ISoundTriggerMiddlewareService mService;
1946    /**
1947      * @return returns current package name.
1948      */
getCurrentOpPackageName()1949     static String getCurrentOpPackageName() {
1950         String packageName = ActivityThread.currentOpPackageName();
1951         if (packageName == null) {
1952             return "";
1953         }
1954         return packageName;
1955     }
1956 
1957     /**
1958      * Translate an exception thrown from interaction with the underlying service to an error code.
1959      * Throws a runtime exception for unexpected conditions.
1960      * @param e The caught exception.
1961      * @return The error code.
1962      *
1963      * @hide
1964      */
handleException(Exception e)1965     static int handleException(Exception e) {
1966         Log.w(TAG, "Exception caught", e);
1967         if (e instanceof RemoteException) {
1968             return STATUS_DEAD_OBJECT;
1969         }
1970         if (e instanceof ServiceSpecificException) {
1971             switch (((ServiceSpecificException) e).errorCode) {
1972                 case Status.OPERATION_NOT_SUPPORTED:
1973                     return STATUS_INVALID_OPERATION;
1974                 case Status.TEMPORARY_PERMISSION_DENIED:
1975                     return STATUS_PERMISSION_DENIED;
1976                 case Status.DEAD_OBJECT:
1977                     return STATUS_DEAD_OBJECT;
1978                 case Status.INTERNAL_ERROR:
1979                     return STATUS_ERROR;
1980             }
1981             return STATUS_ERROR;
1982         }
1983         if (e instanceof SecurityException) {
1984             return STATUS_PERMISSION_DENIED;
1985         }
1986         if (e instanceof IllegalStateException) {
1987             return STATUS_INVALID_OPERATION;
1988         }
1989         if (e instanceof IllegalArgumentException || e instanceof NullPointerException) {
1990             return STATUS_BAD_VALUE;
1991         }
1992         // This is not one of the conditions represented by our error code, escalate to a
1993         // RuntimeException.
1994         Log.e(TAG, "Escalating unexpected exception: ", e);
1995         throw new RuntimeException(e);
1996     }
1997 
1998     /**
1999      * Returns a list of descriptors for all hardware modules loaded.
2000      * @param modules A ModuleProperties array where the list will be returned.
2001      * @return - {@link #STATUS_OK} in case of success
2002      *         - {@link #STATUS_ERROR} in case of unspecified error
2003      *         - {@link #STATUS_PERMISSION_DENIED} if the caller does not have system permission
2004      *         - {@link #STATUS_NO_INIT} if the native service cannot be reached
2005      *         - {@link #STATUS_BAD_VALUE} if modules is null
2006      *         - {@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails
2007      *
2008      * @hide
2009      */
2010     @UnsupportedAppUsage
listModules(@onNull ArrayList<ModuleProperties> modules)2011     public static int listModules(@NonNull ArrayList<ModuleProperties> modules) {
2012         try {
2013             SoundTriggerModuleDescriptor[] descs = getService().listModules();
2014             modules.clear();
2015             modules.ensureCapacity(descs.length);
2016             for (SoundTriggerModuleDescriptor desc : descs) {
2017                 modules.add(ConversionUtil.aidl2apiModuleDescriptor(desc));
2018             }
2019             return STATUS_OK;
2020         } catch (Exception e) {
2021             return handleException(e);
2022         }
2023     }
2024 
2025     /**
2026      * Get an interface on a hardware module to control sound models and recognition on
2027      * this module.
2028      * @param moduleId Sound module system identifier {@link ModuleProperties#mId}. mandatory.
2029      * @param listener {@link StatusListener} interface. Mandatory.
2030      * @param handler the Handler that will receive the callabcks. Can be null if default handler
2031      *                is OK.
2032      * @return a valid sound module in case of success or null in case of error.
2033      *
2034      * @hide
2035      */
2036     @UnsupportedAppUsage
attachModule(int moduleId, @NonNull StatusListener listener, @Nullable Handler handler)2037     public static @NonNull SoundTriggerModule attachModule(int moduleId,
2038             @NonNull StatusListener listener,
2039             @Nullable Handler handler) {
2040         Looper looper = handler != null ? handler.getLooper() : Looper.getMainLooper();
2041         try {
2042             return new SoundTriggerModule(getService(), moduleId, listener, looper);
2043         } catch (Exception e) {
2044             Log.e(TAG, "", e);
2045             return null;
2046         }
2047     }
2048 
getService()2049     private static ISoundTriggerMiddlewareService getService() {
2050         synchronized (mServiceLock) {
2051             while (true) {
2052                 IBinder binder = null;
2053                 try {
2054                     binder =
2055                             ServiceManager.getServiceOrThrow(
2056                                     Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE);
2057                     binder.linkToDeath(() -> {
2058                         synchronized (mServiceLock) {
2059                             mService = null;
2060                         }
2061                     }, 0);
2062                     mService = ISoundTriggerMiddlewareService.Stub.asInterface(binder);
2063                     break;
2064                 } catch (Exception e) {
2065                     Log.e(TAG, "Failed to bind to soundtrigger service", e);
2066                 }
2067             }
2068             return  mService;
2069         }
2070 
2071     }
2072 
2073     /**
2074      * Interface provided by the client application when attaching to a {@link SoundTriggerModule}
2075      * to received recognition and error notifications.
2076      *
2077      * @hide
2078      */
2079     public static interface StatusListener {
2080         /**
2081          * Called when recognition succeeds of fails
2082          */
onRecognition(RecognitionEvent event)2083         public abstract void onRecognition(RecognitionEvent event);
2084 
2085         /**
2086          * Called when a sound model has been updated
2087          */
onSoundModelUpdate(SoundModelEvent event)2088         public abstract void onSoundModelUpdate(SoundModelEvent event);
2089 
2090         /**
2091          * Called when the sound trigger native service state changes.
2092          * @param state Native service state. One of {@link SoundTrigger#SERVICE_STATE_ENABLED},
2093          * {@link SoundTrigger#SERVICE_STATE_DISABLED}
2094          */
onServiceStateChange(int state)2095         public abstract void onServiceStateChange(int state);
2096 
2097         /**
2098          * Called when the sound trigger native service dies
2099          */
onServiceDied()2100         public abstract void onServiceDied();
2101     }
2102 }
2103