1 /**
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.hardware.soundtrigger;
18 
19 import static android.system.OsConstants.EINVAL;
20 import static android.system.OsConstants.ENODEV;
21 import static android.system.OsConstants.ENOSYS;
22 import static android.system.OsConstants.EPERM;
23 import static android.system.OsConstants.EPIPE;
24 
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.annotation.SystemApi;
28 import android.app.ActivityThread;
29 import android.compat.annotation.UnsupportedAppUsage;
30 import android.media.AudioFormat;
31 import android.os.Handler;
32 import android.os.Parcel;
33 import android.os.Parcelable;
34 
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 import java.util.UUID;
38 
39 /**
40  * The SoundTrigger class provides access via JNI to the native service managing
41  * the sound trigger HAL.
42  *
43  * @hide
44  */
45 @SystemApi
46 public class SoundTrigger {
47 
SoundTrigger()48     private SoundTrigger() {
49     }
50 
51     /**
52      * Status code used when the operation succeeded
53      */
54     public static final int STATUS_OK = 0;
55     /** @hide */
56     public static final int STATUS_ERROR = Integer.MIN_VALUE;
57     /** @hide */
58     public static final int STATUS_PERMISSION_DENIED = -EPERM;
59     /** @hide */
60     public static final int STATUS_NO_INIT = -ENODEV;
61     /** @hide */
62     public static final int STATUS_BAD_VALUE = -EINVAL;
63     /** @hide */
64     public static final int STATUS_DEAD_OBJECT = -EPIPE;
65     /** @hide */
66     public static final int STATUS_INVALID_OPERATION = -ENOSYS;
67 
68     /*****************************************************************************
69      * A ModuleProperties describes a given sound trigger hardware module
70      * managed by the native sound trigger service. Each module has a unique
71      * ID used to target any API call to this paricular module. Module
72      * properties are returned by listModules() method.
73      *
74      * @hide
75      ****************************************************************************/
76     public static class ModuleProperties implements Parcelable {
77         /** Unique module ID provided by the native service */
78         @UnsupportedAppUsage
79         public final int id;
80 
81         /** human readable voice detection engine implementor */
82         public final String implementor;
83 
84         /** human readable voice detection engine description */
85         public final String description;
86 
87         /** Unique voice engine Id (changes with each version) */
88         @UnsupportedAppUsage
89         public final UUID uuid;
90 
91         /** Voice detection engine version */
92         public final int version;
93 
94         /** Maximum number of active sound models */
95         @UnsupportedAppUsage
96         public final int maxSoundModels;
97 
98         /** Maximum number of key phrases */
99         public final int maxKeyphrases;
100 
101         /** Maximum number of users per key phrase */
102         public final int maxUsers;
103 
104         /** Supported recognition modes (bit field, RECOGNITION_MODE_VOICE_TRIGGER ...) */
105         public final int recognitionModes;
106 
107         /** Supports seamless transition to capture mode after recognition */
108         public final boolean supportsCaptureTransition;
109 
110         /** Maximum buffering capacity in ms if supportsCaptureTransition() is true */
111         public final int maxBufferMs;
112 
113         /** Supports capture by other use cases while detection is active */
114         public final boolean supportsConcurrentCapture;
115 
116         /** Rated power consumption when detection is active with TDB silence/sound/speech ratio */
117         public final int powerConsumptionMw;
118 
119         /** Returns the trigger (key phrase) capture in the binary data of the
120          * recognition callback event */
121         public final boolean returnsTriggerInEvent;
122 
123         @UnsupportedAppUsage
ModuleProperties(int id, String implementor, String description, String uuid, int version, int maxSoundModels, int maxKeyphrases, int maxUsers, int recognitionModes, boolean supportsCaptureTransition, int maxBufferMs, boolean supportsConcurrentCapture, int powerConsumptionMw, boolean returnsTriggerInEvent)124         ModuleProperties(int id, String implementor, String description,
125                 String uuid, int version, int maxSoundModels, int maxKeyphrases,
126                 int maxUsers, int recognitionModes, boolean supportsCaptureTransition,
127                 int maxBufferMs, boolean supportsConcurrentCapture,
128                 int powerConsumptionMw, boolean returnsTriggerInEvent) {
129             this.id = id;
130             this.implementor = implementor;
131             this.description = description;
132             this.uuid = UUID.fromString(uuid);
133             this.version = version;
134             this.maxSoundModels = maxSoundModels;
135             this.maxKeyphrases = maxKeyphrases;
136             this.maxUsers = maxUsers;
137             this.recognitionModes = recognitionModes;
138             this.supportsCaptureTransition = supportsCaptureTransition;
139             this.maxBufferMs = maxBufferMs;
140             this.supportsConcurrentCapture = supportsConcurrentCapture;
141             this.powerConsumptionMw = powerConsumptionMw;
142             this.returnsTriggerInEvent = returnsTriggerInEvent;
143         }
144 
145         public static final @android.annotation.NonNull Parcelable.Creator<ModuleProperties> CREATOR
146                 = new Parcelable.Creator<ModuleProperties>() {
147             public ModuleProperties createFromParcel(Parcel in) {
148                 return ModuleProperties.fromParcel(in);
149             }
150 
151             public ModuleProperties[] newArray(int size) {
152                 return new ModuleProperties[size];
153             }
154         };
155 
fromParcel(Parcel in)156         private static ModuleProperties fromParcel(Parcel in) {
157             int id = in.readInt();
158             String implementor = in.readString();
159             String description = in.readString();
160             String uuid = in.readString();
161             int version = in.readInt();
162             int maxSoundModels = in.readInt();
163             int maxKeyphrases = in.readInt();
164             int maxUsers = in.readInt();
165             int recognitionModes = in.readInt();
166             boolean supportsCaptureTransition = in.readByte() == 1;
167             int maxBufferMs = in.readInt();
168             boolean supportsConcurrentCapture = in.readByte() == 1;
169             int powerConsumptionMw = in.readInt();
170             boolean returnsTriggerInEvent = in.readByte() == 1;
171             return new ModuleProperties(id, implementor, description, uuid, version,
172                     maxSoundModels, maxKeyphrases, maxUsers, recognitionModes,
173                     supportsCaptureTransition, maxBufferMs, supportsConcurrentCapture,
174                     powerConsumptionMw, returnsTriggerInEvent);
175         }
176 
177         @Override
writeToParcel(Parcel dest, int flags)178         public void writeToParcel(Parcel dest, int flags) {
179             dest.writeInt(id);
180             dest.writeString(implementor);
181             dest.writeString(description);
182             dest.writeString(uuid.toString());
183             dest.writeInt(version);
184             dest.writeInt(maxSoundModels);
185             dest.writeInt(maxKeyphrases);
186             dest.writeInt(maxUsers);
187             dest.writeInt(recognitionModes);
188             dest.writeByte((byte) (supportsCaptureTransition ? 1 : 0));
189             dest.writeInt(maxBufferMs);
190             dest.writeByte((byte) (supportsConcurrentCapture ? 1 : 0));
191             dest.writeInt(powerConsumptionMw);
192             dest.writeByte((byte) (returnsTriggerInEvent ? 1 : 0));
193         }
194 
195         @Override
describeContents()196         public int describeContents() {
197             return 0;
198         }
199 
200         @Override
toString()201         public String toString() {
202             return "ModuleProperties [id=" + id + ", implementor=" + implementor + ", description="
203                     + description + ", uuid=" + uuid + ", version=" + version + ", maxSoundModels="
204                     + maxSoundModels + ", maxKeyphrases=" + maxKeyphrases + ", maxUsers="
205                     + maxUsers + ", recognitionModes=" + recognitionModes
206                     + ", supportsCaptureTransition=" + supportsCaptureTransition + ", maxBufferMs="
207                     + maxBufferMs + ", supportsConcurrentCapture=" + supportsConcurrentCapture
208                     + ", powerConsumptionMw=" + powerConsumptionMw
209                     + ", returnsTriggerInEvent=" + returnsTriggerInEvent + "]";
210         }
211     }
212 
213     /*****************************************************************************
214      * A SoundModel describes the attributes and contains the binary data used by the hardware
215      * implementation to detect a particular sound pattern.
216      * A specialized version {@link KeyphraseSoundModel} is defined for key phrase
217      * sound models.
218      *
219      * @hide
220      ****************************************************************************/
221     public static class SoundModel {
222         /** Undefined sound model type */
223         public static final int TYPE_UNKNOWN = -1;
224 
225         /** Keyphrase sound model */
226         public static final int TYPE_KEYPHRASE = 0;
227 
228         /**
229          * A generic sound model. Use this type only for non-keyphrase sound models such as
230          * ones that match a particular sound pattern.
231          */
232         public static final int TYPE_GENERIC_SOUND = 1;
233 
234         /** Unique sound model identifier */
235         @UnsupportedAppUsage
236         public final UUID uuid;
237 
238         /** Sound model type (e.g. TYPE_KEYPHRASE); */
239         public final int type;
240 
241         /** Unique sound model vendor identifier */
242         @UnsupportedAppUsage
243         public final UUID vendorUuid;
244 
245         /** Opaque data. For use by vendor implementation and enrollment application */
246         @UnsupportedAppUsage
247         public final byte[] data;
248 
SoundModel(UUID uuid, UUID vendorUuid, int type, byte[] data)249         public SoundModel(UUID uuid, UUID vendorUuid, int type, byte[] data) {
250             this.uuid = uuid;
251             this.vendorUuid = vendorUuid;
252             this.type = type;
253             this.data = data;
254         }
255 
256         @Override
hashCode()257         public int hashCode() {
258             final int prime = 31;
259             int result = 1;
260             result = prime * result + Arrays.hashCode(data);
261             result = prime * result + type;
262             result = prime * result + ((uuid == null) ? 0 : uuid.hashCode());
263             result = prime * result + ((vendorUuid == null) ? 0 : vendorUuid.hashCode());
264             return result;
265         }
266 
267         @Override
equals(Object obj)268         public boolean equals(Object obj) {
269             if (this == obj)
270                 return true;
271             if (obj == null)
272                 return false;
273             if (!(obj instanceof SoundModel))
274                 return false;
275             SoundModel other = (SoundModel) obj;
276             if (!Arrays.equals(data, other.data))
277                 return false;
278             if (type != other.type)
279                 return false;
280             if (uuid == null) {
281                 if (other.uuid != null)
282                     return false;
283             } else if (!uuid.equals(other.uuid))
284                 return false;
285             if (vendorUuid == null) {
286                 if (other.vendorUuid != null)
287                     return false;
288             } else if (!vendorUuid.equals(other.vendorUuid))
289                 return false;
290             return true;
291         }
292     }
293 
294     /*****************************************************************************
295      * A Keyphrase describes a key phrase that can be detected by a
296      * {@link KeyphraseSoundModel}
297      *
298      * @hide
299      ****************************************************************************/
300     public static class Keyphrase implements Parcelable {
301         /** Unique identifier for this keyphrase */
302         @UnsupportedAppUsage
303         public final int id;
304 
305         /** Recognition modes supported for this key phrase in the model */
306         @UnsupportedAppUsage
307         public final int recognitionModes;
308 
309         /** Locale of the keyphrase. JAVA Locale string e.g en_US */
310         @UnsupportedAppUsage
311         public final String locale;
312 
313         /** Key phrase text */
314         @UnsupportedAppUsage
315         public final String text;
316 
317         /** Users this key phrase has been trained for. countains sound trigger specific user IDs
318          * derived from system user IDs {@link android.os.UserHandle#getIdentifier()}. */
319         @UnsupportedAppUsage
320         public final int[] users;
321 
322         @UnsupportedAppUsage
Keyphrase(int id, int recognitionModes, String locale, String text, int[] users)323         public Keyphrase(int id, int recognitionModes, String locale, String text, int[] users) {
324             this.id = id;
325             this.recognitionModes = recognitionModes;
326             this.locale = locale;
327             this.text = text;
328             this.users = users;
329         }
330 
331         public static final @android.annotation.NonNull Parcelable.Creator<Keyphrase> CREATOR
332                 = new Parcelable.Creator<Keyphrase>() {
333             public Keyphrase createFromParcel(Parcel in) {
334                 return Keyphrase.fromParcel(in);
335             }
336 
337             public Keyphrase[] newArray(int size) {
338                 return new Keyphrase[size];
339             }
340         };
341 
fromParcel(Parcel in)342         private static Keyphrase fromParcel(Parcel in) {
343             int id = in.readInt();
344             int recognitionModes = in.readInt();
345             String locale = in.readString();
346             String text = in.readString();
347             int[] users = null;
348             int numUsers = in.readInt();
349             if (numUsers >= 0) {
350                 users = new int[numUsers];
351                 in.readIntArray(users);
352             }
353             return new Keyphrase(id, recognitionModes, locale, text, users);
354         }
355 
356         @Override
writeToParcel(Parcel dest, int flags)357         public void writeToParcel(Parcel dest, int flags) {
358             dest.writeInt(id);
359             dest.writeInt(recognitionModes);
360             dest.writeString(locale);
361             dest.writeString(text);
362             if (users != null) {
363                 dest.writeInt(users.length);
364                 dest.writeIntArray(users);
365             } else {
366                 dest.writeInt(-1);
367             }
368         }
369 
370         @Override
describeContents()371         public int describeContents() {
372             return 0;
373         }
374 
375         @Override
hashCode()376         public int hashCode() {
377             final int prime = 31;
378             int result = 1;
379             result = prime * result + ((text == null) ? 0 : text.hashCode());
380             result = prime * result + id;
381             result = prime * result + ((locale == null) ? 0 : locale.hashCode());
382             result = prime * result + recognitionModes;
383             result = prime * result + Arrays.hashCode(users);
384             return result;
385         }
386 
387         @Override
equals(Object obj)388         public boolean equals(Object obj) {
389             if (this == obj)
390                 return true;
391             if (obj == null)
392                 return false;
393             if (getClass() != obj.getClass())
394                 return false;
395             Keyphrase other = (Keyphrase) obj;
396             if (text == null) {
397                 if (other.text != null)
398                     return false;
399             } else if (!text.equals(other.text))
400                 return false;
401             if (id != other.id)
402                 return false;
403             if (locale == null) {
404                 if (other.locale != null)
405                     return false;
406             } else if (!locale.equals(other.locale))
407                 return false;
408             if (recognitionModes != other.recognitionModes)
409                 return false;
410             if (!Arrays.equals(users, other.users))
411                 return false;
412             return true;
413         }
414 
415         @Override
toString()416         public String toString() {
417             return "Keyphrase [id=" + id + ", recognitionModes=" + recognitionModes + ", locale="
418                     + locale + ", text=" + text + ", users=" + Arrays.toString(users) + "]";
419         }
420     }
421 
422     /*****************************************************************************
423      * A KeyphraseSoundModel is a specialized {@link SoundModel} for key phrases.
424      * It contains data needed by the hardware to detect a certain number of key phrases
425      * and the list of corresponding {@link Keyphrase} descriptors.
426      *
427      * @hide
428      ****************************************************************************/
429     public static class KeyphraseSoundModel extends SoundModel implements Parcelable {
430         /** Key phrases in this sound model */
431         @UnsupportedAppUsage
432         public final Keyphrase[] keyphrases; // keyword phrases in model
433 
434         @UnsupportedAppUsage
KeyphraseSoundModel( UUID uuid, UUID vendorUuid, byte[] data, Keyphrase[] keyphrases)435         public KeyphraseSoundModel(
436                 UUID uuid, UUID vendorUuid, byte[] data, Keyphrase[] keyphrases) {
437             super(uuid, vendorUuid, TYPE_KEYPHRASE, data);
438             this.keyphrases = keyphrases;
439         }
440 
441         public static final @android.annotation.NonNull Parcelable.Creator<KeyphraseSoundModel> CREATOR
442                 = new Parcelable.Creator<KeyphraseSoundModel>() {
443             public KeyphraseSoundModel createFromParcel(Parcel in) {
444                 return KeyphraseSoundModel.fromParcel(in);
445             }
446 
447             public KeyphraseSoundModel[] newArray(int size) {
448                 return new KeyphraseSoundModel[size];
449             }
450         };
451 
fromParcel(Parcel in)452         private static KeyphraseSoundModel fromParcel(Parcel in) {
453             UUID uuid = UUID.fromString(in.readString());
454             UUID vendorUuid = null;
455             int length = in.readInt();
456             if (length >= 0) {
457                 vendorUuid = UUID.fromString(in.readString());
458             }
459             byte[] data = in.readBlob();
460             Keyphrase[] keyphrases = in.createTypedArray(Keyphrase.CREATOR);
461             return new KeyphraseSoundModel(uuid, vendorUuid, data, keyphrases);
462         }
463 
464         @Override
describeContents()465         public int describeContents() {
466             return 0;
467         }
468 
469         @Override
writeToParcel(Parcel dest, int flags)470         public void writeToParcel(Parcel dest, int flags) {
471             dest.writeString(uuid.toString());
472             if (vendorUuid == null) {
473                 dest.writeInt(-1);
474             } else {
475                 dest.writeInt(vendorUuid.toString().length());
476                 dest.writeString(vendorUuid.toString());
477             }
478             dest.writeBlob(data);
479             dest.writeTypedArray(keyphrases, flags);
480         }
481 
482         @Override
toString()483         public String toString() {
484             return "KeyphraseSoundModel [keyphrases=" + Arrays.toString(keyphrases)
485                     + ", uuid=" + uuid + ", vendorUuid=" + vendorUuid
486                     + ", type=" + type + ", data=" + (data == null ? 0 : data.length) + "]";
487         }
488 
489         @Override
hashCode()490         public int hashCode() {
491             final int prime = 31;
492             int result = super.hashCode();
493             result = prime * result + Arrays.hashCode(keyphrases);
494             return result;
495         }
496 
497         @Override
equals(Object obj)498         public boolean equals(Object obj) {
499             if (this == obj)
500                 return true;
501             if (!super.equals(obj))
502                 return false;
503             if (!(obj instanceof KeyphraseSoundModel))
504                 return false;
505             KeyphraseSoundModel other = (KeyphraseSoundModel) obj;
506             if (!Arrays.equals(keyphrases, other.keyphrases))
507                 return false;
508             return true;
509         }
510     }
511 
512 
513     /*****************************************************************************
514      * A GenericSoundModel is a specialized {@link SoundModel} for non-voice sound
515      * patterns.
516      *
517      * @hide
518      ****************************************************************************/
519     public static class GenericSoundModel extends SoundModel implements Parcelable {
520 
521         public static final @android.annotation.NonNull Parcelable.Creator<GenericSoundModel> CREATOR
522                 = new Parcelable.Creator<GenericSoundModel>() {
523             public GenericSoundModel createFromParcel(Parcel in) {
524                 return GenericSoundModel.fromParcel(in);
525             }
526 
527             public GenericSoundModel[] newArray(int size) {
528                 return new GenericSoundModel[size];
529             }
530         };
531 
532         @UnsupportedAppUsage
GenericSoundModel(UUID uuid, UUID vendorUuid, byte[] data)533         public GenericSoundModel(UUID uuid, UUID vendorUuid, byte[] data) {
534             super(uuid, vendorUuid, TYPE_GENERIC_SOUND, data);
535         }
536 
537         @Override
describeContents()538         public int describeContents() {
539             return 0;
540         }
541 
fromParcel(Parcel in)542         private static GenericSoundModel fromParcel(Parcel in) {
543             UUID uuid = UUID.fromString(in.readString());
544             UUID vendorUuid = null;
545             int length = in.readInt();
546             if (length >= 0) {
547                 vendorUuid = UUID.fromString(in.readString());
548             }
549             byte[] data = in.readBlob();
550             return new GenericSoundModel(uuid, vendorUuid, data);
551         }
552 
553         @Override
writeToParcel(Parcel dest, int flags)554         public void writeToParcel(Parcel dest, int flags) {
555             dest.writeString(uuid.toString());
556             if (vendorUuid == null) {
557                 dest.writeInt(-1);
558             } else {
559                 dest.writeInt(vendorUuid.toString().length());
560                 dest.writeString(vendorUuid.toString());
561             }
562             dest.writeBlob(data);
563         }
564 
565         @Override
toString()566         public String toString() {
567             return "GenericSoundModel [uuid=" + uuid + ", vendorUuid=" + vendorUuid
568                     + ", type=" + type + ", data=" + (data == null ? 0 : data.length) + "]";
569         }
570     }
571 
572     /**
573      *  Modes for key phrase recognition
574      */
575 
576     /**
577      * Simple recognition of the key phrase
578      *
579      * @hide
580      */
581     public static final int RECOGNITION_MODE_VOICE_TRIGGER = 0x1;
582     /**
583      * Trigger only if one user is identified
584      *
585      * @hide
586      */
587     public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 0x2;
588     /**
589      * Trigger only if one user is authenticated
590      *
591      * @hide
592      */
593     public static final int RECOGNITION_MODE_USER_AUTHENTICATION = 0x4;
594 
595     /**
596      *  Status codes for {@link RecognitionEvent}
597      */
598     /**
599      * Recognition success
600      *
601      * @hide
602      */
603     public static final int RECOGNITION_STATUS_SUCCESS = 0;
604     /**
605      * Recognition aborted (e.g. capture preempted by anotehr use case
606      *
607      * @hide
608      */
609     public static final int RECOGNITION_STATUS_ABORT = 1;
610     /**
611      * Recognition failure
612      *
613      * @hide
614      */
615     public static final int RECOGNITION_STATUS_FAILURE = 2;
616     /**
617      * Recognition event was triggered by a getModelState request, not by the
618      * DSP.
619      *
620      * @hide
621      */
622     public static final int RECOGNITION_STATUS_GET_STATE_RESPONSE = 3;
623 
624     /**
625      *  A RecognitionEvent is provided by the
626      *  {@code StatusListener#onRecognition(RecognitionEvent)}
627      *  callback upon recognition success or failure.
628      */
629     public static class RecognitionEvent {
630         /**
631          * Recognition status e.g RECOGNITION_STATUS_SUCCESS
632          *
633          * @hide
634          */
635         @UnsupportedAppUsage
636         public final int status;
637         /**
638          *
639          * Sound Model corresponding to this event callback
640          *
641          * @hide
642          */
643         @UnsupportedAppUsage
644         public final int soundModelHandle;
645         /**
646          * True if it is possible to capture audio from this utterance buffered by the hardware
647          *
648          * @hide
649          */
650         @UnsupportedAppUsage
651         public final boolean captureAvailable;
652         /**
653          * Audio session ID to be used when capturing the utterance with an AudioRecord
654          * if captureAvailable() is true.
655          *
656          * @hide
657          */
658         @UnsupportedAppUsage
659         public final int captureSession;
660         /**
661          * Delay in ms between end of model detection and start of audio available for capture.
662          * A negative value is possible (e.g. if keyphrase is also available for capture)
663          *
664          * @hide
665          */
666         public final int captureDelayMs;
667         /**
668          * Duration in ms of audio captured before the start of the trigger. 0 if none.
669          *
670          * @hide
671          */
672         public final int capturePreambleMs;
673         /**
674          * True if  the trigger (key phrase capture is present in binary data
675          *
676          * @hide
677          */
678         public final boolean triggerInData;
679         /**
680          * Audio format of either the trigger in event data or to use for capture of the
681          * rest of the utterance
682          *
683          * @hide
684          */
685         public final AudioFormat captureFormat;
686         /**
687          * Opaque data for use by system applications who know about voice engine internals,
688          * typically during enrollment.
689          *
690          * @hide
691          */
692         @UnsupportedAppUsage
693         public final byte[] data;
694 
695         /** @hide */
696         @UnsupportedAppUsage
RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable, int captureSession, int captureDelayMs, int capturePreambleMs, boolean triggerInData, AudioFormat captureFormat, byte[] data)697         public RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
698                 int captureSession, int captureDelayMs, int capturePreambleMs,
699                 boolean triggerInData, AudioFormat captureFormat, byte[] data) {
700             this.status = status;
701             this.soundModelHandle = soundModelHandle;
702             this.captureAvailable = captureAvailable;
703             this.captureSession = captureSession;
704             this.captureDelayMs = captureDelayMs;
705             this.capturePreambleMs = capturePreambleMs;
706             this.triggerInData = triggerInData;
707             this.captureFormat = captureFormat;
708             this.data = data;
709         }
710 
711         /**
712          * Check if is possible to capture audio from this utterance buffered by the hardware.
713          *
714          * @return {@code true} iff a capturing is possible
715          */
isCaptureAvailable()716         public boolean isCaptureAvailable() {
717             return captureAvailable;
718         }
719 
720         /**
721          * Get the audio format of either the trigger in event data or to use for capture of the
722          * rest of the utterance
723          *
724          * @return the audio format
725          */
getCaptureFormat()726         @Nullable public AudioFormat getCaptureFormat() {
727             return captureFormat;
728         }
729 
730         /**
731          * Get Audio session ID to be used when capturing the utterance with an {@link AudioRecord}
732          * if {@link #isCaptureAvailable()} is true.
733          *
734          * @return The id of the capture session
735          */
getCaptureSession()736         public int getCaptureSession() {
737             return captureSession;
738         }
739 
740         /**
741          * Get the opaque data for use by system applications who know about voice engine
742          * internals, typically during enrollment.
743          *
744          * @return The data of the event
745          */
getData()746         public byte[] getData() {
747             return data;
748         }
749 
750         /** @hide */
751         public static final @android.annotation.NonNull Parcelable.Creator<RecognitionEvent> CREATOR
752                 = new Parcelable.Creator<RecognitionEvent>() {
753             public RecognitionEvent createFromParcel(Parcel in) {
754                 return RecognitionEvent.fromParcel(in);
755             }
756 
757             public RecognitionEvent[] newArray(int size) {
758                 return new RecognitionEvent[size];
759             }
760         };
761 
762         /** @hide */
fromParcel(Parcel in)763         protected static RecognitionEvent fromParcel(Parcel in) {
764             int status = in.readInt();
765             int soundModelHandle = in.readInt();
766             boolean captureAvailable = in.readByte() == 1;
767             int captureSession = in.readInt();
768             int captureDelayMs = in.readInt();
769             int capturePreambleMs = in.readInt();
770             boolean triggerInData = in.readByte() == 1;
771             AudioFormat captureFormat = null;
772             if (in.readByte() == 1) {
773                 int sampleRate = in.readInt();
774                 int encoding = in.readInt();
775                 int channelMask = in.readInt();
776                 captureFormat = (new AudioFormat.Builder())
777                         .setChannelMask(channelMask)
778                         .setEncoding(encoding)
779                         .setSampleRate(sampleRate)
780                         .build();
781             }
782             byte[] data = in.readBlob();
783             return new RecognitionEvent(status, soundModelHandle, captureAvailable, captureSession,
784                     captureDelayMs, capturePreambleMs, triggerInData, captureFormat, data);
785         }
786 
787         /** @hide */
describeContents()788         public int describeContents() {
789             return 0;
790         }
791 
792         /** @hide */
writeToParcel(Parcel dest, int flags)793         public void writeToParcel(Parcel dest, int flags) {
794             dest.writeInt(status);
795             dest.writeInt(soundModelHandle);
796             dest.writeByte((byte) (captureAvailable ? 1 : 0));
797             dest.writeInt(captureSession);
798             dest.writeInt(captureDelayMs);
799             dest.writeInt(capturePreambleMs);
800             dest.writeByte((byte) (triggerInData ? 1 : 0));
801             if (captureFormat != null) {
802                 dest.writeByte((byte)1);
803                 dest.writeInt(captureFormat.getSampleRate());
804                 dest.writeInt(captureFormat.getEncoding());
805                 dest.writeInt(captureFormat.getChannelMask());
806             } else {
807                 dest.writeByte((byte)0);
808             }
809             dest.writeBlob(data);
810         }
811 
812         @Override
hashCode()813         public int hashCode() {
814             final int prime = 31;
815             int result = 1;
816             result = prime * result + (captureAvailable ? 1231 : 1237);
817             result = prime * result + captureDelayMs;
818             result = prime * result + capturePreambleMs;
819             result = prime * result + captureSession;
820             result = prime * result + (triggerInData ? 1231 : 1237);
821             if (captureFormat != null) {
822                 result = prime * result + captureFormat.getSampleRate();
823                 result = prime * result + captureFormat.getEncoding();
824                 result = prime * result + captureFormat.getChannelMask();
825             }
826             result = prime * result + Arrays.hashCode(data);
827             result = prime * result + soundModelHandle;
828             result = prime * result + status;
829             return result;
830         }
831 
832         @Override
equals(@ullable Object obj)833         public boolean equals(@Nullable Object obj) {
834             if (this == obj)
835                 return true;
836             if (obj == null)
837                 return false;
838             if (getClass() != obj.getClass())
839                 return false;
840             RecognitionEvent other = (RecognitionEvent) obj;
841             if (captureAvailable != other.captureAvailable)
842                 return false;
843             if (captureDelayMs != other.captureDelayMs)
844                 return false;
845             if (capturePreambleMs != other.capturePreambleMs)
846                 return false;
847             if (captureSession != other.captureSession)
848                 return false;
849             if (!Arrays.equals(data, other.data))
850                 return false;
851             if (soundModelHandle != other.soundModelHandle)
852                 return false;
853             if (status != other.status)
854                 return false;
855             if (triggerInData != other.triggerInData)
856                 return false;
857             if (captureFormat == null) {
858                 if (other.captureFormat != null)
859                     return false;
860             } else {
861                 if (other.captureFormat == null)
862                     return false;
863                 if (captureFormat.getSampleRate() != other.captureFormat.getSampleRate())
864                     return false;
865                 if (captureFormat.getEncoding() != other.captureFormat.getEncoding())
866                     return false;
867                 if (captureFormat.getChannelMask() != other.captureFormat.getChannelMask())
868                     return false;
869             }
870             return true;
871         }
872 
873         @NonNull
874         @Override
toString()875         public String toString() {
876             return "RecognitionEvent [status=" + status + ", soundModelHandle=" + soundModelHandle
877                     + ", captureAvailable=" + captureAvailable + ", captureSession="
878                     + captureSession + ", captureDelayMs=" + captureDelayMs
879                     + ", capturePreambleMs=" + capturePreambleMs
880                     + ", triggerInData=" + triggerInData
881                     + ((captureFormat == null) ? "" :
882                         (", sampleRate=" + captureFormat.getSampleRate()))
883                     + ((captureFormat == null) ? "" :
884                         (", encoding=" + captureFormat.getEncoding()))
885                     + ((captureFormat == null) ? "" :
886                         (", channelMask=" + captureFormat.getChannelMask()))
887                     + ", data=" + (data == null ? 0 : data.length) + "]";
888         }
889     }
890 
891     /**
892      *  A RecognitionConfig is provided to
893      *  {@link SoundTriggerModule#startRecognition(int, RecognitionConfig)} to configure the
894      *  recognition request.
895      *
896      *  @hide
897      */
898     public static class RecognitionConfig implements Parcelable {
899         /** True if the DSP should capture the trigger sound and make it available for further
900          * capture. */
901         @UnsupportedAppUsage
902         public final boolean captureRequested;
903         /**
904          * True if the service should restart listening after the DSP triggers.
905          * Note: This config flag is currently used at the service layer rather than by the DSP.
906          */
907         public final boolean allowMultipleTriggers;
908         /** List of all keyphrases in the sound model for which recognition should be performed with
909          * options for each keyphrase. */
910         @UnsupportedAppUsage
911         public final KeyphraseRecognitionExtra keyphrases[];
912         /** Opaque data for use by system applications who know about voice engine internals,
913          * typically during enrollment. */
914         @UnsupportedAppUsage
915         public final byte[] data;
916 
917         @UnsupportedAppUsage
RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers, KeyphraseRecognitionExtra[] keyphrases, byte[] data)918         public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers,
919                 KeyphraseRecognitionExtra[] keyphrases, byte[] data) {
920             this.captureRequested = captureRequested;
921             this.allowMultipleTriggers = allowMultipleTriggers;
922             this.keyphrases = keyphrases;
923             this.data = data;
924         }
925 
926         public static final @android.annotation.NonNull Parcelable.Creator<RecognitionConfig> CREATOR
927                 = new Parcelable.Creator<RecognitionConfig>() {
928             public RecognitionConfig createFromParcel(Parcel in) {
929                 return RecognitionConfig.fromParcel(in);
930             }
931 
932             public RecognitionConfig[] newArray(int size) {
933                 return new RecognitionConfig[size];
934             }
935         };
936 
fromParcel(Parcel in)937         private static RecognitionConfig fromParcel(Parcel in) {
938             boolean captureRequested = in.readByte() == 1;
939             boolean allowMultipleTriggers = in.readByte() == 1;
940             KeyphraseRecognitionExtra[] keyphrases =
941                     in.createTypedArray(KeyphraseRecognitionExtra.CREATOR);
942             byte[] data = in.readBlob();
943             return new RecognitionConfig(captureRequested, allowMultipleTriggers, keyphrases, data);
944         }
945 
946         @Override
writeToParcel(Parcel dest, int flags)947         public void writeToParcel(Parcel dest, int flags) {
948             dest.writeByte((byte) (captureRequested ? 1 : 0));
949             dest.writeByte((byte) (allowMultipleTriggers ? 1 : 0));
950             dest.writeTypedArray(keyphrases, flags);
951             dest.writeBlob(data);
952         }
953 
954         @Override
describeContents()955         public int describeContents() {
956             return 0;
957         }
958 
959         @Override
toString()960         public String toString() {
961             return "RecognitionConfig [captureRequested=" + captureRequested
962                     + ", allowMultipleTriggers=" + allowMultipleTriggers + ", keyphrases="
963                     + Arrays.toString(keyphrases) + ", data=" + Arrays.toString(data) + "]";
964         }
965     }
966 
967     /**
968      * Confidence level for users defined in a keyphrase.
969      * - The confidence level is expressed in percent (0% -100%).
970      * When used in a {@link KeyphraseRecognitionEvent} it indicates the detected confidence level
971      * When used in a {@link RecognitionConfig} it indicates the minimum confidence level that
972      * should trigger a recognition.
973      * - The user ID is derived from the system ID {@link android.os.UserHandle#getIdentifier()}.
974      *
975      * @hide
976      */
977     public static class ConfidenceLevel implements Parcelable {
978         @UnsupportedAppUsage
979         public final int userId;
980         @UnsupportedAppUsage
981         public final int confidenceLevel;
982 
983         @UnsupportedAppUsage
ConfidenceLevel(int userId, int confidenceLevel)984         public ConfidenceLevel(int userId, int confidenceLevel) {
985             this.userId = userId;
986             this.confidenceLevel = confidenceLevel;
987         }
988 
989         public static final @android.annotation.NonNull Parcelable.Creator<ConfidenceLevel> CREATOR
990                 = new Parcelable.Creator<ConfidenceLevel>() {
991             public ConfidenceLevel createFromParcel(Parcel in) {
992                 return ConfidenceLevel.fromParcel(in);
993             }
994 
995             public ConfidenceLevel[] newArray(int size) {
996                 return new ConfidenceLevel[size];
997             }
998         };
999 
fromParcel(Parcel in)1000         private static ConfidenceLevel fromParcel(Parcel in) {
1001             int userId = in.readInt();
1002             int confidenceLevel = in.readInt();
1003             return new ConfidenceLevel(userId, confidenceLevel);
1004         }
1005 
1006         @Override
writeToParcel(Parcel dest, int flags)1007         public void writeToParcel(Parcel dest, int flags) {
1008             dest.writeInt(userId);
1009             dest.writeInt(confidenceLevel);
1010         }
1011 
1012         @Override
describeContents()1013         public int describeContents() {
1014             return 0;
1015         }
1016 
1017         @Override
hashCode()1018         public int hashCode() {
1019             final int prime = 31;
1020             int result = 1;
1021             result = prime * result + confidenceLevel;
1022             result = prime * result + userId;
1023             return result;
1024         }
1025 
1026         @Override
equals(Object obj)1027         public boolean equals(Object obj) {
1028             if (this == obj)
1029                 return true;
1030             if (obj == null)
1031                 return false;
1032             if (getClass() != obj.getClass())
1033                 return false;
1034             ConfidenceLevel other = (ConfidenceLevel) obj;
1035             if (confidenceLevel != other.confidenceLevel)
1036                 return false;
1037             if (userId != other.userId)
1038                 return false;
1039             return true;
1040         }
1041 
1042         @Override
toString()1043         public String toString() {
1044             return "ConfidenceLevel [userId=" + userId
1045                     + ", confidenceLevel=" + confidenceLevel + "]";
1046         }
1047     }
1048 
1049     /**
1050      *  Additional data conveyed by a {@link KeyphraseRecognitionEvent}
1051      *  for a key phrase detection.
1052      *
1053      * @hide
1054      */
1055     public static class KeyphraseRecognitionExtra implements Parcelable {
1056         /** The keyphrase ID */
1057         @UnsupportedAppUsage
1058         public final int id;
1059 
1060         /** Recognition modes matched for this event */
1061         @UnsupportedAppUsage
1062         public final int recognitionModes;
1063 
1064         /** Confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER when user identification
1065          * is not performed */
1066         @UnsupportedAppUsage
1067         public final int coarseConfidenceLevel;
1068 
1069         /** Confidence levels for all users recognized (KeyphraseRecognitionEvent) or to
1070          * be recognized (RecognitionConfig) */
1071         @UnsupportedAppUsage
1072         public final ConfidenceLevel[] confidenceLevels;
1073 
1074         @UnsupportedAppUsage
KeyphraseRecognitionExtra(int id, int recognitionModes, int coarseConfidenceLevel, ConfidenceLevel[] confidenceLevels)1075         public KeyphraseRecognitionExtra(int id, int recognitionModes, int coarseConfidenceLevel,
1076                 ConfidenceLevel[] confidenceLevels) {
1077             this.id = id;
1078             this.recognitionModes = recognitionModes;
1079             this.coarseConfidenceLevel = coarseConfidenceLevel;
1080             this.confidenceLevels = confidenceLevels;
1081         }
1082 
1083         public static final @android.annotation.NonNull Parcelable.Creator<KeyphraseRecognitionExtra> CREATOR
1084                 = new Parcelable.Creator<KeyphraseRecognitionExtra>() {
1085             public KeyphraseRecognitionExtra createFromParcel(Parcel in) {
1086                 return KeyphraseRecognitionExtra.fromParcel(in);
1087             }
1088 
1089             public KeyphraseRecognitionExtra[] newArray(int size) {
1090                 return new KeyphraseRecognitionExtra[size];
1091             }
1092         };
1093 
fromParcel(Parcel in)1094         private static KeyphraseRecognitionExtra fromParcel(Parcel in) {
1095             int id = in.readInt();
1096             int recognitionModes = in.readInt();
1097             int coarseConfidenceLevel = in.readInt();
1098             ConfidenceLevel[] confidenceLevels = in.createTypedArray(ConfidenceLevel.CREATOR);
1099             return new KeyphraseRecognitionExtra(id, recognitionModes, coarseConfidenceLevel,
1100                     confidenceLevels);
1101         }
1102 
1103         @Override
writeToParcel(Parcel dest, int flags)1104         public void writeToParcel(Parcel dest, int flags) {
1105             dest.writeInt(id);
1106             dest.writeInt(recognitionModes);
1107             dest.writeInt(coarseConfidenceLevel);
1108             dest.writeTypedArray(confidenceLevels, flags);
1109         }
1110 
1111         @Override
describeContents()1112         public int describeContents() {
1113             return 0;
1114         }
1115 
1116         @Override
hashCode()1117         public int hashCode() {
1118             final int prime = 31;
1119             int result = 1;
1120             result = prime * result + Arrays.hashCode(confidenceLevels);
1121             result = prime * result + id;
1122             result = prime * result + recognitionModes;
1123             result = prime * result + coarseConfidenceLevel;
1124             return result;
1125         }
1126 
1127         @Override
equals(Object obj)1128         public boolean equals(Object obj) {
1129             if (this == obj)
1130                 return true;
1131             if (obj == null)
1132                 return false;
1133             if (getClass() != obj.getClass())
1134                 return false;
1135             KeyphraseRecognitionExtra other = (KeyphraseRecognitionExtra) obj;
1136             if (!Arrays.equals(confidenceLevels, other.confidenceLevels))
1137                 return false;
1138             if (id != other.id)
1139                 return false;
1140             if (recognitionModes != other.recognitionModes)
1141                 return false;
1142             if (coarseConfidenceLevel != other.coarseConfidenceLevel)
1143                 return false;
1144             return true;
1145         }
1146 
1147         @Override
toString()1148         public String toString() {
1149             return "KeyphraseRecognitionExtra [id=" + id + ", recognitionModes=" + recognitionModes
1150                     + ", coarseConfidenceLevel=" + coarseConfidenceLevel
1151                     + ", confidenceLevels=" + Arrays.toString(confidenceLevels) + "]";
1152         }
1153     }
1154 
1155     /**
1156      *  Specialized {@link RecognitionEvent} for a key phrase detection.
1157      *
1158      *  @hide
1159      */
1160     public static class KeyphraseRecognitionEvent extends RecognitionEvent implements Parcelable {
1161         /** Indicates if the key phrase is present in the buffered audio available for capture */
1162         @UnsupportedAppUsage
1163         public final KeyphraseRecognitionExtra[] keyphraseExtras;
1164 
1165         @UnsupportedAppUsage
KeyphraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable, int captureSession, int captureDelayMs, int capturePreambleMs, boolean triggerInData, AudioFormat captureFormat, byte[] data, KeyphraseRecognitionExtra[] keyphraseExtras)1166         public KeyphraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
1167                int captureSession, int captureDelayMs, int capturePreambleMs,
1168                boolean triggerInData, AudioFormat captureFormat, byte[] data,
1169                KeyphraseRecognitionExtra[] keyphraseExtras) {
1170             super(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs,
1171                   capturePreambleMs, triggerInData, captureFormat, data);
1172             this.keyphraseExtras = keyphraseExtras;
1173         }
1174 
1175         public static final @android.annotation.NonNull Parcelable.Creator<KeyphraseRecognitionEvent> CREATOR
1176                 = new Parcelable.Creator<KeyphraseRecognitionEvent>() {
1177             public KeyphraseRecognitionEvent createFromParcel(Parcel in) {
1178                 return KeyphraseRecognitionEvent.fromParcelForKeyphrase(in);
1179             }
1180 
1181             public KeyphraseRecognitionEvent[] newArray(int size) {
1182                 return new KeyphraseRecognitionEvent[size];
1183             }
1184         };
1185 
fromParcelForKeyphrase(Parcel in)1186         private static KeyphraseRecognitionEvent fromParcelForKeyphrase(Parcel in) {
1187             int status = in.readInt();
1188             int soundModelHandle = in.readInt();
1189             boolean captureAvailable = in.readByte() == 1;
1190             int captureSession = in.readInt();
1191             int captureDelayMs = in.readInt();
1192             int capturePreambleMs = in.readInt();
1193             boolean triggerInData = in.readByte() == 1;
1194             AudioFormat captureFormat = null;
1195             if (in.readByte() == 1) {
1196                 int sampleRate = in.readInt();
1197                 int encoding = in.readInt();
1198                 int channelMask = in.readInt();
1199                 captureFormat = (new AudioFormat.Builder())
1200                     .setChannelMask(channelMask)
1201                     .setEncoding(encoding)
1202                     .setSampleRate(sampleRate)
1203                     .build();
1204             }
1205             byte[] data = in.readBlob();
1206             KeyphraseRecognitionExtra[] keyphraseExtras =
1207                     in.createTypedArray(KeyphraseRecognitionExtra.CREATOR);
1208             return new KeyphraseRecognitionEvent(status, soundModelHandle, captureAvailable,
1209                     captureSession, captureDelayMs, capturePreambleMs, triggerInData,
1210                     captureFormat, data, keyphraseExtras);
1211         }
1212 
1213         @Override
writeToParcel(Parcel dest, int flags)1214         public void writeToParcel(Parcel dest, int flags) {
1215             dest.writeInt(status);
1216             dest.writeInt(soundModelHandle);
1217             dest.writeByte((byte) (captureAvailable ? 1 : 0));
1218             dest.writeInt(captureSession);
1219             dest.writeInt(captureDelayMs);
1220             dest.writeInt(capturePreambleMs);
1221             dest.writeByte((byte) (triggerInData ? 1 : 0));
1222             if (captureFormat != null) {
1223                 dest.writeByte((byte)1);
1224                 dest.writeInt(captureFormat.getSampleRate());
1225                 dest.writeInt(captureFormat.getEncoding());
1226                 dest.writeInt(captureFormat.getChannelMask());
1227             } else {
1228                 dest.writeByte((byte)0);
1229             }
1230             dest.writeBlob(data);
1231             dest.writeTypedArray(keyphraseExtras, flags);
1232         }
1233 
1234         @Override
describeContents()1235         public int describeContents() {
1236             return 0;
1237         }
1238 
1239         @Override
hashCode()1240         public int hashCode() {
1241             final int prime = 31;
1242             int result = super.hashCode();
1243             result = prime * result + Arrays.hashCode(keyphraseExtras);
1244             return result;
1245         }
1246 
1247         @Override
equals(Object obj)1248         public boolean equals(Object obj) {
1249             if (this == obj)
1250                 return true;
1251             if (!super.equals(obj))
1252                 return false;
1253             if (getClass() != obj.getClass())
1254                 return false;
1255             KeyphraseRecognitionEvent other = (KeyphraseRecognitionEvent) obj;
1256             if (!Arrays.equals(keyphraseExtras, other.keyphraseExtras))
1257                 return false;
1258             return true;
1259         }
1260 
1261         @Override
toString()1262         public String toString() {
1263             return "KeyphraseRecognitionEvent [keyphraseExtras=" + Arrays.toString(keyphraseExtras)
1264                     + ", status=" + status
1265                     + ", soundModelHandle=" + soundModelHandle + ", captureAvailable="
1266                     + captureAvailable + ", captureSession=" + captureSession + ", captureDelayMs="
1267                     + captureDelayMs + ", capturePreambleMs=" + capturePreambleMs
1268                     + ", triggerInData=" + triggerInData
1269                     + ((captureFormat == null) ? "" :
1270                         (", sampleRate=" + captureFormat.getSampleRate()))
1271                     + ((captureFormat == null) ? "" :
1272                         (", encoding=" + captureFormat.getEncoding()))
1273                     + ((captureFormat == null) ? "" :
1274                         (", channelMask=" + captureFormat.getChannelMask()))
1275                     + ", data=" + (data == null ? 0 : data.length) + "]";
1276         }
1277     }
1278 
1279     /**
1280      * Sub-class of RecognitionEvent specifically for sound-trigger based sound
1281      * models(non-keyphrase). Currently does not contain any additional fields.
1282      *
1283      * @hide
1284      */
1285     public static class GenericRecognitionEvent extends RecognitionEvent implements Parcelable {
1286         @UnsupportedAppUsage
GenericRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable, int captureSession, int captureDelayMs, int capturePreambleMs, boolean triggerInData, AudioFormat captureFormat, byte[] data)1287         public GenericRecognitionEvent(int status, int soundModelHandle,
1288                 boolean captureAvailable, int captureSession, int captureDelayMs,
1289                 int capturePreambleMs, boolean triggerInData, AudioFormat captureFormat,
1290                 byte[] data) {
1291             super(status, soundModelHandle, captureAvailable, captureSession,
1292                     captureDelayMs, capturePreambleMs, triggerInData, captureFormat,
1293                     data);
1294         }
1295 
1296         public static final @android.annotation.NonNull Parcelable.Creator<GenericRecognitionEvent> CREATOR
1297                 = new Parcelable.Creator<GenericRecognitionEvent>() {
1298             public GenericRecognitionEvent createFromParcel(Parcel in) {
1299                 return GenericRecognitionEvent.fromParcelForGeneric(in);
1300             }
1301 
1302             public GenericRecognitionEvent[] newArray(int size) {
1303                 return new GenericRecognitionEvent[size];
1304             }
1305         };
1306 
fromParcelForGeneric(Parcel in)1307         private static GenericRecognitionEvent fromParcelForGeneric(Parcel in) {
1308             RecognitionEvent event = RecognitionEvent.fromParcel(in);
1309             return new GenericRecognitionEvent(event.status, event.soundModelHandle,
1310                     event.captureAvailable, event.captureSession, event.captureDelayMs,
1311                     event.capturePreambleMs, event.triggerInData, event.captureFormat, event.data);
1312         }
1313 
1314         @Override
equals(Object obj)1315         public boolean equals(Object obj) {
1316             if (this == obj)
1317                 return true;
1318             if (obj == null)
1319                 return false;
1320             if (getClass() != obj.getClass()) return false;
1321             RecognitionEvent other = (RecognitionEvent) obj;
1322             return super.equals(obj);
1323         }
1324 
1325         @Override
toString()1326         public String toString() {
1327             return "GenericRecognitionEvent ::" + super.toString();
1328         }
1329     }
1330 
1331     /**
1332      *  Status codes for {@link SoundModelEvent}
1333      */
1334     /**
1335      * Sound Model was updated
1336      *
1337      * @hide
1338      */
1339     public static final int SOUNDMODEL_STATUS_UPDATED = 0;
1340 
1341     /**
1342      *  A SoundModelEvent is provided by the
1343      *  {@link StatusListener#onSoundModelUpdate(SoundModelEvent)}
1344      *  callback when a sound model has been updated by the implementation
1345      *
1346      *  @hide
1347      */
1348     public static class SoundModelEvent implements Parcelable {
1349         /** Status e.g {@link #SOUNDMODEL_STATUS_UPDATED} */
1350         public final int status;
1351         /** The updated sound model handle */
1352         public final int soundModelHandle;
1353         /** New sound model data */
1354         public final byte[] data;
1355 
1356         @UnsupportedAppUsage
SoundModelEvent(int status, int soundModelHandle, byte[] data)1357         SoundModelEvent(int status, int soundModelHandle, byte[] data) {
1358             this.status = status;
1359             this.soundModelHandle = soundModelHandle;
1360             this.data = data;
1361         }
1362 
1363         public static final @android.annotation.NonNull Parcelable.Creator<SoundModelEvent> CREATOR
1364                 = new Parcelable.Creator<SoundModelEvent>() {
1365             public SoundModelEvent createFromParcel(Parcel in) {
1366                 return SoundModelEvent.fromParcel(in);
1367             }
1368 
1369             public SoundModelEvent[] newArray(int size) {
1370                 return new SoundModelEvent[size];
1371             }
1372         };
1373 
fromParcel(Parcel in)1374         private static SoundModelEvent fromParcel(Parcel in) {
1375             int status = in.readInt();
1376             int soundModelHandle = in.readInt();
1377             byte[] data = in.readBlob();
1378             return new SoundModelEvent(status, soundModelHandle, data);
1379         }
1380 
1381         @Override
describeContents()1382         public int describeContents() {
1383             return 0;
1384         }
1385 
1386         @Override
writeToParcel(Parcel dest, int flags)1387         public void writeToParcel(Parcel dest, int flags) {
1388             dest.writeInt(status);
1389             dest.writeInt(soundModelHandle);
1390             dest.writeBlob(data);
1391         }
1392 
1393         @Override
hashCode()1394         public int hashCode() {
1395             final int prime = 31;
1396             int result = 1;
1397             result = prime * result + Arrays.hashCode(data);
1398             result = prime * result + soundModelHandle;
1399             result = prime * result + status;
1400             return result;
1401         }
1402 
1403         @Override
equals(Object obj)1404         public boolean equals(Object obj) {
1405             if (this == obj)
1406                 return true;
1407             if (obj == null)
1408                 return false;
1409             if (getClass() != obj.getClass())
1410                 return false;
1411             SoundModelEvent other = (SoundModelEvent) obj;
1412             if (!Arrays.equals(data, other.data))
1413                 return false;
1414             if (soundModelHandle != other.soundModelHandle)
1415                 return false;
1416             if (status != other.status)
1417                 return false;
1418             return true;
1419         }
1420 
1421         @Override
toString()1422         public String toString() {
1423             return "SoundModelEvent [status=" + status + ", soundModelHandle=" + soundModelHandle
1424                     + ", data=" + (data == null ? 0 : data.length) + "]";
1425         }
1426     }
1427 
1428     /**
1429      *  Native service state. {@link StatusListener#onServiceStateChange(int)}
1430      */
1431     // Keep in sync with system/core/include/system/sound_trigger.h
1432     /**
1433      * Sound trigger service is enabled
1434      *
1435      * @hide
1436      */
1437     public static final int SERVICE_STATE_ENABLED = 0;
1438     /**
1439      * Sound trigger service is disabled
1440      *
1441      * @hide
1442      */
1443     public static final int SERVICE_STATE_DISABLED = 1;
1444 
1445     /**
1446      * @return returns current package name.
1447      */
getCurrentOpPackageName()1448     static String getCurrentOpPackageName() {
1449         String packageName = ActivityThread.currentOpPackageName();
1450         if (packageName == null) {
1451             return "";
1452         }
1453         return packageName;
1454     }
1455 
1456     /**
1457      * Returns a list of descriptors for all hardware modules loaded.
1458      * @param modules A ModuleProperties array where the list will be returned.
1459      * @return - {@link #STATUS_OK} in case of success
1460      *         - {@link #STATUS_ERROR} in case of unspecified error
1461      *         - {@link #STATUS_PERMISSION_DENIED} if the caller does not have system permission
1462      *         - {@link #STATUS_NO_INIT} if the native service cannot be reached
1463      *         - {@link #STATUS_BAD_VALUE} if modules is null
1464      *         - {@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails
1465      *
1466      * @hide
1467      */
1468     @UnsupportedAppUsage
listModules(ArrayList<ModuleProperties> modules)1469     public static int listModules(ArrayList<ModuleProperties> modules) {
1470         return listModules(getCurrentOpPackageName(), modules);
1471     }
1472 
1473     /**
1474      * Returns a list of descriptors for all hardware modules loaded.
1475      * @param opPackageName
1476      * @param modules A ModuleProperties array where the list will be returned.
1477      * @return - {@link #STATUS_OK} in case of success
1478      *         - {@link #STATUS_ERROR} in case of unspecified error
1479      *         - {@link #STATUS_PERMISSION_DENIED} if the caller does not have system permission
1480      *         - {@link #STATUS_NO_INIT} if the native service cannot be reached
1481      *         - {@link #STATUS_BAD_VALUE} if modules is null
1482      *         - {@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails
1483      */
listModules(String opPackageName, ArrayList<ModuleProperties> modules)1484     private static native int listModules(String opPackageName,
1485                                           ArrayList<ModuleProperties> modules);
1486 
1487     /**
1488      * Get an interface on a hardware module to control sound models and recognition on
1489      * this module.
1490      * @param moduleId Sound module system identifier {@link ModuleProperties#id}. mandatory.
1491      * @param listener {@link StatusListener} interface. Mandatory.
1492      * @param handler the Handler that will receive the callabcks. Can be null if default handler
1493      *                is OK.
1494      * @return a valid sound module in case of success or null in case of error.
1495      *
1496      * @hide
1497      */
1498     @UnsupportedAppUsage
attachModule(int moduleId, StatusListener listener, Handler handler)1499     public static SoundTriggerModule attachModule(int moduleId,
1500                                                   StatusListener listener,
1501                                                   Handler handler) {
1502         if (listener == null) {
1503             return null;
1504         }
1505         SoundTriggerModule module = new SoundTriggerModule(moduleId, listener, handler);
1506         return module;
1507     }
1508 
1509     /**
1510      * Interface provided by the client application when attaching to a {@link SoundTriggerModule}
1511      * to received recognition and error notifications.
1512      *
1513      * @hide
1514      */
1515     public static interface StatusListener {
1516         /**
1517          * Called when recognition succeeds of fails
1518          */
onRecognition(RecognitionEvent event)1519         public abstract void onRecognition(RecognitionEvent event);
1520 
1521         /**
1522          * Called when a sound model has been updated
1523          */
onSoundModelUpdate(SoundModelEvent event)1524         public abstract void onSoundModelUpdate(SoundModelEvent event);
1525 
1526         /**
1527          * Called when the sound trigger native service state changes.
1528          * @param state Native service state. One of {@link SoundTrigger#SERVICE_STATE_ENABLED},
1529          * {@link SoundTrigger#SERVICE_STATE_DISABLED}
1530          */
onServiceStateChange(int state)1531         public abstract void onServiceStateChange(int state);
1532 
1533         /**
1534          * Called when the sound trigger native service dies
1535          */
onServiceDied()1536         public abstract void onServiceDied();
1537     }
1538 }
1539