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