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