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