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