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