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