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