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 /**
     29  * The SoundTrigger class provides access via JNI to the native service managing
     30  * the sound trigger HAL.
     31  *
     32  * @hide
     33  */
     34 public 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