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