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