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