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