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