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 static java.util.Objects.requireNonNull; 26 27 import android.annotation.IntDef; 28 import android.annotation.NonNull; 29 import android.annotation.Nullable; 30 import android.annotation.SuppressLint; 31 import android.annotation.SystemApi; 32 import android.annotation.TestApi; 33 import android.app.ActivityThread; 34 import android.compat.annotation.UnsupportedAppUsage; 35 import android.content.Context; 36 import android.media.AudioFormat; 37 import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService; 38 import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor; 39 import android.media.soundtrigger_middleware.Status; 40 import android.os.Handler; 41 import android.os.IBinder; 42 import android.os.Looper; 43 import android.os.Parcel; 44 import android.os.Parcelable; 45 import android.os.RemoteException; 46 import android.os.ServiceManager; 47 import android.os.ServiceSpecificException; 48 import android.util.Log; 49 50 import java.lang.annotation.Retention; 51 import java.lang.annotation.RetentionPolicy; 52 import java.util.ArrayList; 53 import java.util.Arrays; 54 import java.util.Locale; 55 import java.util.UUID; 56 57 /** 58 * The SoundTrigger class provides access to the service managing the sound trigger HAL. 59 * 60 * @hide 61 */ 62 @TestApi 63 @SystemApi 64 public class SoundTrigger { 65 private static final String TAG = "SoundTrigger"; 66 SoundTrigger()67 private SoundTrigger() { 68 } 69 70 /** 71 * Status code used when the operation succeeded 72 */ 73 public static final int STATUS_OK = 0; 74 /** @hide */ 75 public static final int STATUS_ERROR = Integer.MIN_VALUE; 76 /** @hide */ 77 public static final int STATUS_PERMISSION_DENIED = -EPERM; 78 /** @hide */ 79 public static final int STATUS_NO_INIT = -ENODEV; 80 /** @hide */ 81 public static final int STATUS_BAD_VALUE = -EINVAL; 82 /** @hide */ 83 public static final int STATUS_DEAD_OBJECT = -EPIPE; 84 /** @hide */ 85 public static final int STATUS_INVALID_OPERATION = -ENOSYS; 86 87 /***************************************************************************** 88 * A ModuleProperties describes a given sound trigger hardware module 89 * managed by the native sound trigger service. Each module has a unique 90 * ID used to target any API call to this paricular module. Module 91 * properties are returned by listModules() method. 92 * 93 ****************************************************************************/ 94 public static final class ModuleProperties implements Parcelable { 95 96 /** 97 * Bit field values of AudioCapabilities supported by the implemented HAL 98 * driver. 99 * @hide 100 */ 101 @Retention(RetentionPolicy.SOURCE) 102 @IntDef(flag = true, prefix = { "AUDIO_CAPABILITY_" }, value = { 103 AUDIO_CAPABILITY_ECHO_CANCELLATION, 104 AUDIO_CAPABILITY_NOISE_SUPPRESSION 105 }) 106 public @interface AudioCapabilities {} 107 108 /** 109 * If set the underlying module supports AEC. 110 * Describes bit field {@link ModuleProperties#mAudioCapabilities} 111 */ 112 public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 0x1; 113 /** 114 * If set, the underlying module supports noise suppression. 115 * Describes bit field {@link ModuleProperties#mAudioCapabilities} 116 */ 117 public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = 0x2; 118 119 private final int mId; 120 @NonNull 121 private final String mImplementor; 122 @NonNull 123 private final String mDescription; 124 @NonNull 125 private final UUID mUuid; 126 private final int mVersion; 127 @NonNull 128 private final String mSupportedModelArch; 129 private final int mMaxSoundModels; 130 private final int mMaxKeyphrases; 131 private final int mMaxUsers; 132 @RecognitionModes 133 private final int mRecognitionModes; 134 private final boolean mSupportsCaptureTransition; 135 private final int mMaxBufferMillis; 136 private final boolean mSupportsConcurrentCapture; 137 private final int mPowerConsumptionMw; 138 private final boolean mReturnsTriggerInEvent; 139 @AudioCapabilities 140 private final int mAudioCapabilities; 141 142 /** @hide */ 143 @TestApi ModuleProperties(int id, @NonNull String implementor, @NonNull String description, @NonNull String uuid, int version, @NonNull String supportedModelArch, int maxSoundModels, int maxKeyphrases, int maxUsers, @RecognitionModes int recognitionModes, boolean supportsCaptureTransition, int maxBufferMs, boolean supportsConcurrentCapture, int powerConsumptionMw, boolean returnsTriggerInEvent, int audioCapabilities)144 public ModuleProperties(int id, @NonNull String implementor, @NonNull String description, 145 @NonNull String uuid, int version, @NonNull String supportedModelArch, 146 int maxSoundModels, int maxKeyphrases, int maxUsers, 147 @RecognitionModes int recognitionModes, boolean supportsCaptureTransition, 148 int maxBufferMs, boolean supportsConcurrentCapture, int powerConsumptionMw, 149 boolean returnsTriggerInEvent, int audioCapabilities) { 150 this.mId = id; 151 this.mImplementor = requireNonNull(implementor); 152 this.mDescription = requireNonNull(description); 153 this.mUuid = UUID.fromString(requireNonNull(uuid)); 154 this.mVersion = version; 155 this.mSupportedModelArch = requireNonNull(supportedModelArch); 156 this.mMaxSoundModels = maxSoundModels; 157 this.mMaxKeyphrases = maxKeyphrases; 158 this.mMaxUsers = maxUsers; 159 this.mRecognitionModes = recognitionModes; 160 this.mSupportsCaptureTransition = supportsCaptureTransition; 161 this.mMaxBufferMillis = maxBufferMs; 162 this.mSupportsConcurrentCapture = supportsConcurrentCapture; 163 this.mPowerConsumptionMw = powerConsumptionMw; 164 this.mReturnsTriggerInEvent = returnsTriggerInEvent; 165 this.mAudioCapabilities = audioCapabilities; 166 } 167 168 /** Unique module ID provided by the native service */ getId()169 public int getId() { 170 return mId; 171 } 172 173 /** human readable voice detection engine implementor */ 174 @NonNull getImplementor()175 public String getImplementor() { 176 return mImplementor; 177 } 178 179 /** human readable voice detection engine description */ 180 @NonNull getDescription()181 public String getDescription() { 182 return mDescription; 183 } 184 185 /** Unique voice engine Id (changes with each version) */ 186 @NonNull getUuid()187 public UUID getUuid() { 188 return mUuid; 189 } 190 191 /** Voice detection engine version */ getVersion()192 public int getVersion() { 193 return mVersion; 194 } 195 196 /** 197 * String naming the architecture used for running the supported models. 198 * (eg. a platform running models on a DSP could implement this string to convey the DSP 199 * architecture used) 200 */ 201 @NonNull getSupportedModelArch()202 public String getSupportedModelArch() { 203 return mSupportedModelArch; 204 } 205 206 /** Maximum number of active sound models */ getMaxSoundModels()207 public int getMaxSoundModels() { 208 return mMaxSoundModels; 209 } 210 211 /** Maximum number of key phrases */ getMaxKeyphrases()212 public int getMaxKeyphrases() { 213 return mMaxKeyphrases; 214 } 215 216 /** Maximum number of users per key phrase */ getMaxUsers()217 public int getMaxUsers() { 218 return mMaxUsers; 219 } 220 221 /** Supported recognition modes (bit field, RECOGNITION_MODE_VOICE_TRIGGER ...) */ 222 @RecognitionModes getRecognitionModes()223 public int getRecognitionModes() { 224 return mRecognitionModes; 225 } 226 227 /** Supports seamless transition to capture mode after recognition */ isCaptureTransitionSupported()228 public boolean isCaptureTransitionSupported() { 229 return mSupportsCaptureTransition; 230 } 231 232 /** Maximum buffering capacity in ms if supportsCaptureTransition() is true */ getMaxBufferMillis()233 public int getMaxBufferMillis() { 234 return mMaxBufferMillis; 235 } 236 237 /** Supports capture by other use cases while detection is active */ isConcurrentCaptureSupported()238 public boolean isConcurrentCaptureSupported() { 239 return mSupportsConcurrentCapture; 240 } 241 242 /** Rated power consumption when detection is active with TDB silence/sound/speech ratio */ getPowerConsumptionMw()243 public int getPowerConsumptionMw() { 244 return mPowerConsumptionMw; 245 } 246 247 /** Returns the trigger (key phrase) capture in the binary data of the 248 * recognition callback event */ isTriggerReturnedInEvent()249 public boolean isTriggerReturnedInEvent() { 250 return mReturnsTriggerInEvent; 251 } 252 253 /** 254 * Bit field encoding of the AudioCapabilities 255 * supported by the firmware. 256 */ 257 @AudioCapabilities getAudioCapabilities()258 public int getAudioCapabilities() { 259 return mAudioCapabilities; 260 } 261 262 public static final @android.annotation.NonNull Parcelable.Creator<ModuleProperties> CREATOR 263 = new Parcelable.Creator<ModuleProperties>() { 264 public ModuleProperties createFromParcel(Parcel in) { 265 return ModuleProperties.fromParcel(in); 266 } 267 268 public ModuleProperties[] newArray(int size) { 269 return new ModuleProperties[size]; 270 } 271 }; 272 fromParcel(Parcel in)273 private static ModuleProperties fromParcel(Parcel in) { 274 int id = in.readInt(); 275 String implementor = in.readString(); 276 String description = in.readString(); 277 String uuid = in.readString(); 278 int version = in.readInt(); 279 String supportedModelArch = in.readString(); 280 int maxSoundModels = in.readInt(); 281 int maxKeyphrases = in.readInt(); 282 int maxUsers = in.readInt(); 283 int recognitionModes = in.readInt(); 284 boolean supportsCaptureTransition = in.readByte() == 1; 285 int maxBufferMs = in.readInt(); 286 boolean supportsConcurrentCapture = in.readByte() == 1; 287 int powerConsumptionMw = in.readInt(); 288 boolean returnsTriggerInEvent = in.readByte() == 1; 289 int audioCapabilities = in.readInt(); 290 return new ModuleProperties(id, implementor, description, uuid, version, 291 supportedModelArch, maxSoundModels, maxKeyphrases, maxUsers, recognitionModes, 292 supportsCaptureTransition, maxBufferMs, supportsConcurrentCapture, 293 powerConsumptionMw, returnsTriggerInEvent, audioCapabilities); 294 } 295 296 @Override writeToParcel(@uppressLint"MissingNullability") Parcel dest, int flags)297 public void writeToParcel(@SuppressLint("MissingNullability") Parcel dest, int flags) { 298 dest.writeInt(getId()); 299 dest.writeString(getImplementor()); 300 dest.writeString(getDescription()); 301 dest.writeString(getUuid().toString()); 302 dest.writeInt(getVersion()); 303 dest.writeString(getSupportedModelArch()); 304 dest.writeInt(getMaxSoundModels()); 305 dest.writeInt(getMaxKeyphrases()); 306 dest.writeInt(getMaxUsers()); 307 dest.writeInt(getRecognitionModes()); 308 dest.writeByte((byte) (isCaptureTransitionSupported() ? 1 : 0)); 309 dest.writeInt(getMaxBufferMillis()); 310 dest.writeByte((byte) (isConcurrentCaptureSupported() ? 1 : 0)); 311 dest.writeInt(getPowerConsumptionMw()); 312 dest.writeByte((byte) (isTriggerReturnedInEvent() ? 1 : 0)); 313 dest.writeInt(getAudioCapabilities()); 314 } 315 316 @Override describeContents()317 public int describeContents() { 318 return 0; 319 } 320 321 @Override equals(@ullable Object obj)322 public boolean equals(@Nullable Object obj) { 323 if (this == obj) { 324 return true; 325 } 326 if (obj == null) { 327 return false; 328 } 329 if (!(obj instanceof ModuleProperties)) { 330 return false; 331 } 332 ModuleProperties other = (ModuleProperties) obj; 333 if (mId != other.mId) { 334 return false; 335 } 336 if (!mImplementor.equals(other.mImplementor)) { 337 return false; 338 } 339 if (!mDescription.equals(other.mDescription)) { 340 return false; 341 } 342 if (!mUuid.equals(other.mUuid)) { 343 return false; 344 } 345 if (mVersion != other.mVersion) { 346 return false; 347 } 348 if (!mSupportedModelArch.equals(other.mSupportedModelArch)) { 349 return false; 350 } 351 if (mMaxSoundModels != other.mMaxSoundModels) { 352 return false; 353 } 354 if (mMaxKeyphrases != other.mMaxKeyphrases) { 355 return false; 356 } 357 if (mMaxUsers != other.mMaxUsers) { 358 return false; 359 } 360 if (mRecognitionModes != other.mRecognitionModes) { 361 return false; 362 } 363 if (mSupportsCaptureTransition != other.mSupportsCaptureTransition) { 364 return false; 365 } 366 if (mMaxBufferMillis != other.mMaxBufferMillis) { 367 return false; 368 } 369 if (mSupportsConcurrentCapture != other.mSupportsConcurrentCapture) { 370 return false; 371 } 372 if (mPowerConsumptionMw != other.mPowerConsumptionMw) { 373 return false; 374 } 375 if (mReturnsTriggerInEvent != other.mReturnsTriggerInEvent) { 376 return false; 377 } 378 if (mAudioCapabilities != other.mAudioCapabilities) { 379 return false; 380 } 381 return true; 382 } 383 384 @Override hashCode()385 public int hashCode() { 386 final int prime = 31; 387 int result = 1; 388 result = prime * result + mId; 389 result = prime * result + mImplementor.hashCode(); 390 result = prime * result + mDescription.hashCode(); 391 result = prime * result + mUuid.hashCode(); 392 result = prime * result + mVersion; 393 result = prime * result + mSupportedModelArch.hashCode(); 394 result = prime * result + mMaxSoundModels; 395 result = prime * result + mMaxKeyphrases; 396 result = prime * result + mMaxUsers; 397 result = prime * result + mRecognitionModes; 398 result = prime * result + (mSupportsCaptureTransition ? 1 : 0); 399 result = prime * result + mMaxBufferMillis; 400 result = prime * result + (mSupportsConcurrentCapture ? 1 : 0); 401 result = prime * result + mPowerConsumptionMw; 402 result = prime * result + (mReturnsTriggerInEvent ? 1 : 0); 403 result = prime * result + mAudioCapabilities; 404 return result; 405 } 406 407 @Override toString()408 public String toString() { 409 return "ModuleProperties [id=" + getId() + ", implementor=" + getImplementor() 410 + ", description=" + getDescription() + ", uuid=" + getUuid() 411 + ", version=" + getVersion() + " , supportedModelArch=" 412 + getSupportedModelArch() + ", maxSoundModels=" + getMaxSoundModels() 413 + ", maxKeyphrases=" + getMaxKeyphrases() + ", maxUsers=" + getMaxUsers() 414 + ", recognitionModes=" + getRecognitionModes() + ", supportsCaptureTransition=" 415 + isCaptureTransitionSupported() + ", maxBufferMs=" + getMaxBufferMillis() 416 + ", supportsConcurrentCapture=" + isConcurrentCaptureSupported() 417 + ", powerConsumptionMw=" + getPowerConsumptionMw() 418 + ", returnsTriggerInEvent=" + isTriggerReturnedInEvent() 419 + ", audioCapabilities=" + getAudioCapabilities() + "]"; 420 } 421 } 422 423 /** 424 * A SoundModel describes the attributes and contains the binary data used by the hardware 425 * implementation to detect a particular sound pattern. 426 * A specialized version {@link KeyphraseSoundModel} is defined for key phrase 427 * sound models. 428 */ 429 public static class SoundModel { 430 431 /** @hide */ 432 @Retention(RetentionPolicy.SOURCE) 433 @IntDef({ 434 TYPE_GENERIC_SOUND, 435 TYPE_KEYPHRASE, 436 TYPE_UNKNOWN, 437 }) 438 public @interface SoundModelType {} 439 440 /** 441 * Undefined sound model type 442 * @hide 443 */ 444 public static final int TYPE_UNKNOWN = -1; 445 446 /** Keyphrase sound model */ 447 public static final int TYPE_KEYPHRASE = 0; 448 449 /** 450 * A generic sound model. Use this type only for non-keyphrase sound models such as 451 * ones that match a particular sound pattern. 452 */ 453 public static final int TYPE_GENERIC_SOUND = 1; 454 455 @NonNull 456 private final UUID mUuid; 457 @SoundModelType 458 private final int mType; 459 @NonNull 460 private final UUID mVendorUuid; 461 private final int mVersion; 462 @NonNull 463 private final byte[] mData; 464 465 /** @hide */ SoundModel(@onNull UUID uuid, @Nullable UUID vendorUuid, @SoundModelType int type, @Nullable byte[] data, int version)466 public SoundModel(@NonNull UUID uuid, @Nullable UUID vendorUuid, @SoundModelType int type, 467 @Nullable byte[] data, int version) { 468 this.mUuid = requireNonNull(uuid); 469 this.mVendorUuid = vendorUuid != null ? vendorUuid : new UUID(0, 0); 470 this.mType = type; 471 this.mVersion = version; 472 this.mData = data != null ? data : new byte[0]; 473 } 474 475 /** Unique sound model identifier */ 476 @NonNull getUuid()477 public UUID getUuid() { 478 return mUuid; 479 } 480 481 /** Sound model type (e.g. TYPE_KEYPHRASE); */ 482 @SoundModelType getType()483 public int getType() { 484 return mType; 485 } 486 487 /** Unique sound model vendor identifier */ 488 @NonNull getVendorUuid()489 public UUID getVendorUuid() { 490 return mVendorUuid; 491 } 492 493 /** vendor specific version number of the model */ getVersion()494 public int getVersion() { 495 return mVersion; 496 } 497 498 /** Opaque data. For use by vendor implementation and enrollment application */ 499 @NonNull getData()500 public byte[] getData() { 501 return mData; 502 } 503 504 @Override hashCode()505 public int hashCode() { 506 final int prime = 31; 507 int result = 1; 508 result = prime * result + getVersion(); 509 result = prime * result + Arrays.hashCode(getData()); 510 result = prime * result + getType(); 511 result = prime * result + ((getUuid() == null) ? 0 : getUuid().hashCode()); 512 result = prime * result + ((getVendorUuid() == null) ? 0 : getVendorUuid().hashCode()); 513 return result; 514 } 515 516 @Override equals(Object obj)517 public boolean equals(Object obj) { 518 if (this == obj) { 519 return true; 520 } 521 if (obj == null) { 522 return false; 523 } 524 if (!(obj instanceof SoundModel)) { 525 return false; 526 } 527 SoundModel other = (SoundModel) obj; 528 if (getType() != other.getType()) { 529 return false; 530 } 531 if (getUuid() == null) { 532 if (other.getUuid() != null) { 533 return false; 534 } 535 } else if (!getUuid().equals(other.getUuid())) { 536 return false; 537 } 538 if (getVendorUuid() == null) { 539 if (other.getVendorUuid() != null) { 540 return false; 541 } 542 } else if (!getVendorUuid().equals(other.getVendorUuid())) { 543 return false; 544 } 545 if (!Arrays.equals(getData(), other.getData())) { 546 return false; 547 } 548 if (getVersion() != other.getVersion()) { 549 return false; 550 } 551 return true; 552 } 553 } 554 555 /** 556 * A Keyphrase describes a key phrase that can be detected by a 557 * {@link KeyphraseSoundModel} 558 */ 559 public static final class Keyphrase implements Parcelable { 560 561 private final int mId; 562 @RecognitionModes 563 private final int mRecognitionModes; 564 @NonNull 565 private final Locale mLocale; 566 @NonNull 567 private final String mText; 568 @NonNull 569 private final int[] mUsers; 570 571 /** 572 * Constructor for Keyphrase describes a key phrase that can be detected by a 573 * {@link KeyphraseSoundModel} 574 * 575 * @param id Unique keyphrase identifier for this keyphrase 576 * @param recognitionModes Bit field representation of recognition modes this keyphrase 577 * supports 578 * @param locale Locale of the keyphrase 579 * @param text Key phrase text 580 * @param users Users this key phrase has been trained for. 581 */ Keyphrase(int id, @RecognitionModes int recognitionModes, @NonNull Locale locale, @NonNull String text, @Nullable int[] users)582 public Keyphrase(int id, @RecognitionModes int recognitionModes, @NonNull Locale locale, 583 @NonNull String text, @Nullable int[] users) { 584 this.mId = id; 585 this.mRecognitionModes = recognitionModes; 586 this.mLocale = requireNonNull(locale); 587 this.mText = requireNonNull(text); 588 this.mUsers = users != null ? users : new int[0]; 589 } 590 591 /** Unique identifier for this keyphrase */ getId()592 public int getId() { 593 return mId; 594 } 595 596 /** 597 * Recognition modes supported for this key phrase in the model 598 * 599 * @see #RECOGNITION_MODE_VOICE_TRIGGER 600 * @see #RECOGNITION_MODE_USER_IDENTIFICATION 601 * @see #RECOGNITION_MODE_USER_AUTHENTICATION 602 * @see #RECOGNITION_MODE_GENERIC 603 */ 604 @RecognitionModes getRecognitionModes()605 public int getRecognitionModes() { 606 return mRecognitionModes; 607 } 608 609 /** Locale of the keyphrase. */ 610 @NonNull getLocale()611 public Locale getLocale() { 612 return mLocale; 613 } 614 615 /** Key phrase text */ 616 @NonNull getText()617 public String getText() { 618 return mText; 619 } 620 621 /** 622 * Users this key phrase has been trained for. countains sound trigger specific user IDs 623 * derived from system user IDs {@link android.os.UserHandle#getIdentifier()}. 624 */ 625 @NonNull getUsers()626 public int[] getUsers() { 627 return mUsers; 628 } 629 630 public static final @NonNull Parcelable.Creator<Keyphrase> CREATOR = 631 new Parcelable.Creator<Keyphrase>() { 632 @NonNull 633 public Keyphrase createFromParcel(@NonNull Parcel in) { 634 return Keyphrase.readFromParcel(in); 635 } 636 637 @NonNull 638 public Keyphrase[] newArray(int size) { 639 return new Keyphrase[size]; 640 } 641 }; 642 643 /** 644 * Read from Parcel to generate keyphrase 645 */ 646 @NonNull readFromParcel(@onNull Parcel in)647 public static Keyphrase readFromParcel(@NonNull Parcel in) { 648 int id = in.readInt(); 649 int recognitionModes = in.readInt(); 650 Locale locale = Locale.forLanguageTag(in.readString()); 651 String text = in.readString(); 652 int[] users = null; 653 int numUsers = in.readInt(); 654 if (numUsers >= 0) { 655 users = new int[numUsers]; 656 in.readIntArray(users); 657 } 658 return new Keyphrase(id, recognitionModes, locale, text, users); 659 } 660 661 @Override writeToParcel(@onNull Parcel dest, int flags)662 public void writeToParcel(@NonNull Parcel dest, int flags) { 663 dest.writeInt(getId()); 664 dest.writeInt(getRecognitionModes()); 665 dest.writeString(getLocale().toLanguageTag()); 666 dest.writeString(getText()); 667 if (getUsers() != null) { 668 dest.writeInt(getUsers().length); 669 dest.writeIntArray(getUsers()); 670 } else { 671 dest.writeInt(-1); 672 } 673 } 674 675 /** @hide */ 676 @Override describeContents()677 public int describeContents() { 678 return 0; 679 } 680 681 @Override hashCode()682 public int hashCode() { 683 final int prime = 31; 684 int result = 1; 685 result = prime * result + ((getText() == null) ? 0 : getText().hashCode()); 686 result = prime * result + getId(); 687 result = prime * result + ((getLocale() == null) ? 0 : getLocale().hashCode()); 688 result = prime * result + getRecognitionModes(); 689 result = prime * result + Arrays.hashCode(getUsers()); 690 return result; 691 } 692 693 @Override equals(Object obj)694 public boolean equals(Object obj) { 695 if (this == obj) { 696 return true; 697 } 698 if (obj == null) { 699 return false; 700 } 701 if (getClass() != obj.getClass()) { 702 return false; 703 } 704 Keyphrase other = (Keyphrase) obj; 705 if (getText() == null) { 706 if (other.getText() != null) { 707 return false; 708 } 709 } else if (!getText().equals(other.getText())) { 710 return false; 711 } 712 if (getId() != other.getId()) { 713 return false; 714 } 715 if (getLocale() == null) { 716 if (other.getLocale() != null) { 717 return false; 718 } 719 } else if (!getLocale().equals(other.getLocale())) { 720 return false; 721 } 722 if (getRecognitionModes() != other.getRecognitionModes()) { 723 return false; 724 } 725 if (!Arrays.equals(getUsers(), other.getUsers())) { 726 return false; 727 } 728 return true; 729 } 730 731 @Override toString()732 public String toString() { 733 return "Keyphrase [id=" + getId() + ", recognitionModes=" + getRecognitionModes() 734 + ", locale=" + getLocale().toLanguageTag() + ", text=" + getText() 735 + ", users=" + Arrays.toString(getUsers()) + "]"; 736 } 737 } 738 739 /** 740 * A KeyphraseSoundModel is a specialized {@link SoundModel} for key phrases. 741 * It contains data needed by the hardware to detect a certain number of key phrases 742 * and the list of corresponding {@link Keyphrase} descriptors. 743 */ 744 public static final class KeyphraseSoundModel extends SoundModel implements Parcelable { 745 746 @NonNull 747 private final Keyphrase[] mKeyphrases; 748 KeyphraseSoundModel( @onNull UUID uuid, @NonNull UUID vendorUuid, @Nullable byte[] data, @Nullable Keyphrase[] keyphrases, int version)749 public KeyphraseSoundModel( 750 @NonNull UUID uuid, @NonNull UUID vendorUuid, @Nullable byte[] data, 751 @Nullable Keyphrase[] keyphrases, int version) { 752 super(uuid, vendorUuid, TYPE_KEYPHRASE, data, version); 753 this.mKeyphrases = keyphrases != null ? keyphrases : new Keyphrase[0]; 754 } 755 KeyphraseSoundModel(@onNull UUID uuid, @NonNull UUID vendorUuid, @Nullable byte[] data, @Nullable Keyphrase[] keyphrases)756 public KeyphraseSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid, 757 @Nullable byte[] data, @Nullable Keyphrase[] keyphrases) { 758 this(uuid, vendorUuid, data, keyphrases, -1); 759 } 760 761 /** Key phrases in this sound model */ 762 @NonNull getKeyphrases()763 public Keyphrase[] getKeyphrases() { 764 return mKeyphrases; 765 } 766 767 public static final @NonNull Parcelable.Creator<KeyphraseSoundModel> CREATOR = 768 new Parcelable.Creator<KeyphraseSoundModel>() { 769 @NonNull 770 public KeyphraseSoundModel createFromParcel(@NonNull Parcel in) { 771 return KeyphraseSoundModel.readFromParcel(in); 772 } 773 774 @NonNull 775 public KeyphraseSoundModel[] newArray(int size) { 776 return new KeyphraseSoundModel[size]; 777 } 778 }; 779 780 /** 781 * Read from Parcel to generate KeyphraseSoundModel 782 */ 783 @NonNull readFromParcel(@onNull Parcel in)784 public static KeyphraseSoundModel readFromParcel(@NonNull Parcel in) { 785 UUID uuid = UUID.fromString(in.readString()); 786 UUID vendorUuid = null; 787 int length = in.readInt(); 788 if (length >= 0) { 789 vendorUuid = UUID.fromString(in.readString()); 790 } 791 int version = in.readInt(); 792 byte[] data = in.readBlob(); 793 Keyphrase[] keyphrases = in.createTypedArray(Keyphrase.CREATOR); 794 return new KeyphraseSoundModel(uuid, vendorUuid, data, keyphrases, version); 795 } 796 797 /** @hide */ 798 @Override describeContents()799 public int describeContents() { 800 return 0; 801 } 802 803 @Override writeToParcel(@onNull Parcel dest, int flags)804 public void writeToParcel(@NonNull Parcel dest, int flags) { 805 dest.writeString(getUuid().toString()); 806 if (getVendorUuid() == null) { 807 dest.writeInt(-1); 808 } else { 809 dest.writeInt(getVendorUuid().toString().length()); 810 dest.writeString(getVendorUuid().toString()); 811 } 812 dest.writeInt(getVersion()); 813 dest.writeBlob(getData()); 814 dest.writeTypedArray(getKeyphrases(), flags); 815 } 816 817 @Override toString()818 public String toString() { 819 return "KeyphraseSoundModel [keyphrases=" + Arrays.toString(getKeyphrases()) 820 + ", uuid=" + getUuid() + ", vendorUuid=" + getVendorUuid() 821 + ", type=" + getType() 822 + ", data=" + (getData() == null ? 0 : getData().length) 823 + ", version=" + getVersion() + "]"; 824 } 825 826 @Override hashCode()827 public int hashCode() { 828 final int prime = 31; 829 int result = super.hashCode(); 830 result = prime * result + Arrays.hashCode(getKeyphrases()); 831 return result; 832 } 833 834 @Override equals(Object obj)835 public boolean equals(Object obj) { 836 if (this == obj) { 837 return true; 838 } 839 if (!super.equals(obj)) { 840 return false; 841 } 842 if (!(obj instanceof KeyphraseSoundModel)) { 843 return false; 844 } 845 KeyphraseSoundModel other = (KeyphraseSoundModel) obj; 846 if (!Arrays.equals(getKeyphrases(), other.getKeyphrases())) { 847 return false; 848 } 849 return true; 850 } 851 } 852 853 854 /***************************************************************************** 855 * A GenericSoundModel is a specialized {@link SoundModel} for non-voice sound 856 * patterns. 857 * 858 * @hide 859 ****************************************************************************/ 860 public static class GenericSoundModel extends SoundModel implements Parcelable { 861 862 public static final @android.annotation.NonNull Parcelable.Creator<GenericSoundModel> CREATOR 863 = new Parcelable.Creator<GenericSoundModel>() { 864 public GenericSoundModel createFromParcel(Parcel in) { 865 return GenericSoundModel.fromParcel(in); 866 } 867 868 public GenericSoundModel[] newArray(int size) { 869 return new GenericSoundModel[size]; 870 } 871 }; 872 GenericSoundModel(@onNull UUID uuid, @NonNull UUID vendorUuid, @Nullable byte[] data, int version)873 public GenericSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid, 874 @Nullable byte[] data, int version) { 875 super(uuid, vendorUuid, TYPE_GENERIC_SOUND, data, version); 876 } 877 878 @UnsupportedAppUsage GenericSoundModel(@onNull UUID uuid, @NonNull UUID vendorUuid, @Nullable byte[] data)879 public GenericSoundModel(@NonNull UUID uuid, @NonNull UUID vendorUuid, 880 @Nullable byte[] data) { 881 this(uuid, vendorUuid, data, -1); 882 } 883 884 @Override describeContents()885 public int describeContents() { 886 return 0; 887 } 888 fromParcel(Parcel in)889 private static GenericSoundModel fromParcel(Parcel in) { 890 UUID uuid = UUID.fromString(in.readString()); 891 UUID vendorUuid = null; 892 int length = in.readInt(); 893 if (length >= 0) { 894 vendorUuid = UUID.fromString(in.readString()); 895 } 896 byte[] data = in.readBlob(); 897 int version = in.readInt(); 898 return new GenericSoundModel(uuid, vendorUuid, data, version); 899 } 900 901 @Override writeToParcel(Parcel dest, int flags)902 public void writeToParcel(Parcel dest, int flags) { 903 dest.writeString(getUuid().toString()); 904 if (getVendorUuid() == null) { 905 dest.writeInt(-1); 906 } else { 907 dest.writeInt(getVendorUuid().toString().length()); 908 dest.writeString(getVendorUuid().toString()); 909 } 910 dest.writeBlob(getData()); 911 dest.writeInt(getVersion()); 912 } 913 914 @Override toString()915 public String toString() { 916 return "GenericSoundModel [uuid=" + getUuid() + ", vendorUuid=" + getVendorUuid() 917 + ", type=" + getType() 918 + ", data=" + (getData() == null ? 0 : getData().length) 919 + ", version=" + getVersion() + "]"; 920 } 921 } 922 923 /** 924 * A ModelParamRange is a representation of supported parameter range for a 925 * given loaded model. 926 */ 927 public static final class ModelParamRange implements Parcelable { 928 929 /** 930 * The inclusive start of supported range. 931 */ 932 private final int mStart; 933 934 /** 935 * The inclusive end of supported range. 936 */ 937 private final int mEnd; 938 939 /** @hide */ 940 @TestApi ModelParamRange(int start, int end)941 public ModelParamRange(int start, int end) { 942 this.mStart = start; 943 this.mEnd = end; 944 } 945 946 /** @hide */ ModelParamRange(@onNull Parcel in)947 private ModelParamRange(@NonNull Parcel in) { 948 this.mStart = in.readInt(); 949 this.mEnd = in.readInt(); 950 } 951 952 /** 953 * Get the beginning of the param range 954 * 955 * @return The inclusive start of the supported range. 956 */ getStart()957 public int getStart() { 958 return mStart; 959 } 960 961 /** 962 * Get the end of the param range 963 * 964 * @return The inclusive end of the supported range. 965 */ getEnd()966 public int getEnd() { 967 return mEnd; 968 } 969 970 @NonNull 971 public static final Creator<ModelParamRange> CREATOR = 972 new Creator<ModelParamRange>() { 973 @Override 974 @NonNull 975 public ModelParamRange createFromParcel(@NonNull Parcel in) { 976 return new ModelParamRange(in); 977 } 978 979 @Override 980 @NonNull 981 public ModelParamRange[] newArray(int size) { 982 return new ModelParamRange[size]; 983 } 984 }; 985 986 /** @hide */ 987 @Override describeContents()988 public int describeContents() { 989 return 0; 990 } 991 992 /** @hide */ 993 @Override hashCode()994 public int hashCode() { 995 final int prime = 31; 996 int result = 1; 997 result = prime * result + (mStart); 998 result = prime * result + (mEnd); 999 return result; 1000 } 1001 1002 @Override equals(@ullable Object obj)1003 public boolean equals(@Nullable Object obj) { 1004 if (this == obj) { 1005 return true; 1006 } 1007 if (obj == null) { 1008 return false; 1009 } 1010 if (getClass() != obj.getClass()) { 1011 return false; 1012 } 1013 ModelParamRange other = (ModelParamRange) obj; 1014 if (mStart != other.mStart) { 1015 return false; 1016 } 1017 if (mEnd != other.mEnd) { 1018 return false; 1019 } 1020 return true; 1021 } 1022 1023 @Override writeToParcel(@onNull Parcel dest, int flags)1024 public void writeToParcel(@NonNull Parcel dest, int flags) { 1025 dest.writeInt(mStart); 1026 dest.writeInt(mEnd); 1027 } 1028 1029 @Override 1030 @NonNull toString()1031 public String toString() { 1032 return "ModelParamRange [start=" + mStart + ", end=" + mEnd + "]"; 1033 } 1034 } 1035 1036 /** 1037 * Modes for key phrase recognition 1038 * @hide 1039 */ 1040 @Retention(RetentionPolicy.SOURCE) 1041 @IntDef(flag = true, prefix = { "RECOGNITION_MODE_" }, value = { 1042 RECOGNITION_MODE_VOICE_TRIGGER, 1043 RECOGNITION_MODE_USER_IDENTIFICATION, 1044 RECOGNITION_MODE_USER_AUTHENTICATION, 1045 RECOGNITION_MODE_GENERIC 1046 }) 1047 public @interface RecognitionModes {} 1048 1049 /** 1050 * Trigger on recognition of a key phrase 1051 */ 1052 public static final int RECOGNITION_MODE_VOICE_TRIGGER = 0x1; 1053 /** 1054 * Trigger only if one user is identified 1055 */ 1056 public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 0x2; 1057 /** 1058 * Trigger only if one user is authenticated 1059 */ 1060 public static final int RECOGNITION_MODE_USER_AUTHENTICATION = 0x4; 1061 /** 1062 * Generic (non-speech) recognition. 1063 */ 1064 public static final int RECOGNITION_MODE_GENERIC = 0x8; 1065 1066 /** 1067 * Status codes for {@link RecognitionEvent} 1068 */ 1069 /** 1070 * Recognition success 1071 * 1072 * @hide 1073 */ 1074 public static final int RECOGNITION_STATUS_SUCCESS = 0; 1075 /** 1076 * Recognition aborted (e.g. capture preempted by anotehr use case 1077 * 1078 * @hide 1079 */ 1080 public static final int RECOGNITION_STATUS_ABORT = 1; 1081 /** 1082 * Recognition failure 1083 * 1084 * @hide 1085 */ 1086 public static final int RECOGNITION_STATUS_FAILURE = 2; 1087 /** 1088 * Recognition event was triggered by a getModelState request, not by the 1089 * DSP. 1090 * 1091 * @hide 1092 */ 1093 public static final int RECOGNITION_STATUS_GET_STATE_RESPONSE = 3; 1094 1095 /** 1096 * A RecognitionEvent is provided by the 1097 * {@code StatusListener#onRecognition(RecognitionEvent)} 1098 * callback upon recognition success or failure. 1099 */ 1100 public static class RecognitionEvent { 1101 /** 1102 * Recognition status e.g RECOGNITION_STATUS_SUCCESS 1103 * 1104 * @hide 1105 */ 1106 @UnsupportedAppUsage 1107 public final int status; 1108 /** 1109 * 1110 * Sound Model corresponding to this event callback 1111 * 1112 * @hide 1113 */ 1114 @UnsupportedAppUsage 1115 public final int soundModelHandle; 1116 /** 1117 * True if it is possible to capture audio from this utterance buffered by the hardware 1118 * 1119 * @hide 1120 */ 1121 @UnsupportedAppUsage 1122 public final boolean captureAvailable; 1123 /** 1124 * Audio session ID to be used when capturing the utterance with an AudioRecord 1125 * if captureAvailable() is true. 1126 * 1127 * @hide 1128 */ 1129 @UnsupportedAppUsage 1130 public final int captureSession; 1131 /** 1132 * Delay in ms between end of model detection and start of audio available for capture. 1133 * A negative value is possible (e.g. if keyphrase is also available for capture) 1134 * 1135 * @hide 1136 */ 1137 public final int captureDelayMs; 1138 /** 1139 * Duration in ms of audio captured before the start of the trigger. 0 if none. 1140 * 1141 * @hide 1142 */ 1143 public final int capturePreambleMs; 1144 /** 1145 * True if the trigger (key phrase capture is present in binary data 1146 * 1147 * @hide 1148 */ 1149 public final boolean triggerInData; 1150 /** 1151 * Audio format of either the trigger in event data or to use for capture of the 1152 * rest of the utterance 1153 * 1154 * @hide 1155 */ 1156 @NonNull 1157 public final AudioFormat captureFormat; 1158 /** 1159 * Opaque data for use by system applications who know about voice engine internals, 1160 * typically during enrollment. 1161 * 1162 * @hide 1163 */ 1164 @UnsupportedAppUsage 1165 @NonNull 1166 public final byte[] data; 1167 1168 /** @hide */ 1169 @TestApi 1170 @UnsupportedAppUsage RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable, int captureSession, int captureDelayMs, int capturePreambleMs, boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data)1171 public RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable, 1172 int captureSession, int captureDelayMs, int capturePreambleMs, 1173 boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data) { 1174 this.status = status; 1175 this.soundModelHandle = soundModelHandle; 1176 this.captureAvailable = captureAvailable; 1177 this.captureSession = captureSession; 1178 this.captureDelayMs = captureDelayMs; 1179 this.capturePreambleMs = capturePreambleMs; 1180 this.triggerInData = triggerInData; 1181 this.captureFormat = requireNonNull(captureFormat); 1182 this.data = data != null ? data : new byte[0]; 1183 } 1184 1185 /** 1186 * Check if is possible to capture audio from this utterance buffered by the hardware. 1187 * 1188 * @return {@code true} iff a capturing is possible 1189 */ isCaptureAvailable()1190 public boolean isCaptureAvailable() { 1191 return captureAvailable; 1192 } 1193 1194 /** 1195 * Get the audio format of either the trigger in event data or to use for capture of the 1196 * rest of the utterance 1197 * 1198 * @return the audio format 1199 */ getCaptureFormat()1200 @Nullable public AudioFormat getCaptureFormat() { 1201 return captureFormat; 1202 } 1203 1204 /** 1205 * Get Audio session ID to be used when capturing the utterance with an {@link AudioRecord} 1206 * if {@link #isCaptureAvailable()} is true. 1207 * 1208 * @return The id of the capture session 1209 */ getCaptureSession()1210 public int getCaptureSession() { 1211 return captureSession; 1212 } 1213 1214 /** 1215 * Get the opaque data for use by system applications who know about voice engine 1216 * internals, typically during enrollment. 1217 * 1218 * @return The data of the event 1219 */ 1220 @SuppressLint("MissingNullability") getData()1221 public byte[] getData() { 1222 return data; 1223 } 1224 1225 /** @hide */ 1226 public static final @android.annotation.NonNull Parcelable.Creator<RecognitionEvent> CREATOR 1227 = new Parcelable.Creator<RecognitionEvent>() { 1228 public RecognitionEvent createFromParcel(Parcel in) { 1229 return RecognitionEvent.fromParcel(in); 1230 } 1231 1232 public RecognitionEvent[] newArray(int size) { 1233 return new RecognitionEvent[size]; 1234 } 1235 }; 1236 1237 /** @hide */ fromParcel(Parcel in)1238 protected static RecognitionEvent fromParcel(Parcel in) { 1239 int status = in.readInt(); 1240 int soundModelHandle = in.readInt(); 1241 boolean captureAvailable = in.readByte() == 1; 1242 int captureSession = in.readInt(); 1243 int captureDelayMs = in.readInt(); 1244 int capturePreambleMs = in.readInt(); 1245 boolean triggerInData = in.readByte() == 1; 1246 AudioFormat captureFormat = null; 1247 if (in.readByte() == 1) { 1248 int sampleRate = in.readInt(); 1249 int encoding = in.readInt(); 1250 int channelMask = in.readInt(); 1251 captureFormat = (new AudioFormat.Builder()) 1252 .setChannelMask(channelMask) 1253 .setEncoding(encoding) 1254 .setSampleRate(sampleRate) 1255 .build(); 1256 } 1257 byte[] data = in.readBlob(); 1258 return new RecognitionEvent(status, soundModelHandle, captureAvailable, captureSession, 1259 captureDelayMs, capturePreambleMs, triggerInData, captureFormat, data); 1260 } 1261 1262 /** @hide */ describeContents()1263 public int describeContents() { 1264 return 0; 1265 } 1266 1267 /** @hide */ writeToParcel(Parcel dest, int flags)1268 public void writeToParcel(Parcel dest, int flags) { 1269 dest.writeInt(status); 1270 dest.writeInt(soundModelHandle); 1271 dest.writeByte((byte) (captureAvailable ? 1 : 0)); 1272 dest.writeInt(captureSession); 1273 dest.writeInt(captureDelayMs); 1274 dest.writeInt(capturePreambleMs); 1275 dest.writeByte((byte) (triggerInData ? 1 : 0)); 1276 if (captureFormat != null) { 1277 dest.writeByte((byte)1); 1278 dest.writeInt(captureFormat.getSampleRate()); 1279 dest.writeInt(captureFormat.getEncoding()); 1280 dest.writeInt(captureFormat.getChannelMask()); 1281 } else { 1282 dest.writeByte((byte)0); 1283 } 1284 dest.writeBlob(data); 1285 } 1286 1287 @Override hashCode()1288 public int hashCode() { 1289 final int prime = 31; 1290 int result = 1; 1291 result = prime * result + (captureAvailable ? 1231 : 1237); 1292 result = prime * result + captureDelayMs; 1293 result = prime * result + capturePreambleMs; 1294 result = prime * result + captureSession; 1295 result = prime * result + (triggerInData ? 1231 : 1237); 1296 if (captureFormat != null) { 1297 result = prime * result + captureFormat.getSampleRate(); 1298 result = prime * result + captureFormat.getEncoding(); 1299 result = prime * result + captureFormat.getChannelMask(); 1300 } 1301 result = prime * result + Arrays.hashCode(data); 1302 result = prime * result + soundModelHandle; 1303 result = prime * result + status; 1304 return result; 1305 } 1306 1307 @Override equals(@ullable Object obj)1308 public boolean equals(@Nullable Object obj) { 1309 if (this == obj) 1310 return true; 1311 if (obj == null) 1312 return false; 1313 if (getClass() != obj.getClass()) 1314 return false; 1315 RecognitionEvent other = (RecognitionEvent) obj; 1316 if (captureAvailable != other.captureAvailable) 1317 return false; 1318 if (captureDelayMs != other.captureDelayMs) 1319 return false; 1320 if (capturePreambleMs != other.capturePreambleMs) 1321 return false; 1322 if (captureSession != other.captureSession) 1323 return false; 1324 if (!Arrays.equals(data, other.data)) 1325 return false; 1326 if (soundModelHandle != other.soundModelHandle) 1327 return false; 1328 if (status != other.status) 1329 return false; 1330 if (triggerInData != other.triggerInData) 1331 return false; 1332 if (captureFormat == null) { 1333 if (other.captureFormat != null) 1334 return false; 1335 } else { 1336 if (other.captureFormat == null) 1337 return false; 1338 if (captureFormat.getSampleRate() != other.captureFormat.getSampleRate()) 1339 return false; 1340 if (captureFormat.getEncoding() != other.captureFormat.getEncoding()) 1341 return false; 1342 if (captureFormat.getChannelMask() != other.captureFormat.getChannelMask()) 1343 return false; 1344 } 1345 return true; 1346 } 1347 1348 @NonNull 1349 @Override toString()1350 public String toString() { 1351 return "RecognitionEvent [status=" + status + ", soundModelHandle=" + soundModelHandle 1352 + ", captureAvailable=" + captureAvailable + ", captureSession=" 1353 + captureSession + ", captureDelayMs=" + captureDelayMs 1354 + ", capturePreambleMs=" + capturePreambleMs 1355 + ", triggerInData=" + triggerInData 1356 + ((captureFormat == null) ? "" : 1357 (", sampleRate=" + captureFormat.getSampleRate())) 1358 + ((captureFormat == null) ? "" : 1359 (", encoding=" + captureFormat.getEncoding())) 1360 + ((captureFormat == null) ? "" : 1361 (", channelMask=" + captureFormat.getChannelMask())) 1362 + ", data=" + (data == null ? 0 : data.length) + "]"; 1363 } 1364 } 1365 1366 /** 1367 * A RecognitionConfig is provided to 1368 * {@link SoundTriggerModule#startRecognition(int, RecognitionConfig)} to configure the 1369 * recognition request. 1370 * 1371 * @hide 1372 */ 1373 public static class RecognitionConfig implements Parcelable { 1374 /** True if the DSP should capture the trigger sound and make it available for further 1375 * capture. */ 1376 @UnsupportedAppUsage 1377 public final boolean captureRequested; 1378 /** 1379 * True if the service should restart listening after the DSP triggers. 1380 * Note: This config flag is currently used at the service layer rather than by the DSP. 1381 */ 1382 public final boolean allowMultipleTriggers; 1383 /** List of all keyphrases in the sound model for which recognition should be performed with 1384 * options for each keyphrase. */ 1385 @UnsupportedAppUsage 1386 @NonNull 1387 public final KeyphraseRecognitionExtra keyphrases[]; 1388 /** Opaque data for use by system applications who know about voice engine internals, 1389 * typically during enrollment. */ 1390 @UnsupportedAppUsage 1391 @NonNull 1392 public final byte[] data; 1393 1394 /** 1395 * Bit field encoding of the AudioCapabilities 1396 * supported by the firmware. 1397 */ 1398 @ModuleProperties.AudioCapabilities 1399 public final int audioCapabilities; 1400 RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers, @Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data, int audioCapabilities)1401 public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers, 1402 @Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data, 1403 int audioCapabilities) { 1404 this.captureRequested = captureRequested; 1405 this.allowMultipleTriggers = allowMultipleTriggers; 1406 this.keyphrases = keyphrases != null ? keyphrases : new KeyphraseRecognitionExtra[0]; 1407 this.data = data != null ? data : new byte[0]; 1408 this.audioCapabilities = audioCapabilities; 1409 } 1410 1411 @UnsupportedAppUsage RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers, @Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data)1412 public RecognitionConfig(boolean captureRequested, boolean allowMultipleTriggers, 1413 @Nullable KeyphraseRecognitionExtra[] keyphrases, @Nullable byte[] data) { 1414 this(captureRequested, allowMultipleTriggers, keyphrases, data, 0); 1415 } 1416 1417 public static final @android.annotation.NonNull Parcelable.Creator<RecognitionConfig> CREATOR 1418 = new Parcelable.Creator<RecognitionConfig>() { 1419 public RecognitionConfig createFromParcel(Parcel in) { 1420 return RecognitionConfig.fromParcel(in); 1421 } 1422 1423 public RecognitionConfig[] newArray(int size) { 1424 return new RecognitionConfig[size]; 1425 } 1426 }; 1427 fromParcel(Parcel in)1428 private static RecognitionConfig fromParcel(Parcel in) { 1429 boolean captureRequested = in.readByte() == 1; 1430 boolean allowMultipleTriggers = in.readByte() == 1; 1431 KeyphraseRecognitionExtra[] keyphrases = 1432 in.createTypedArray(KeyphraseRecognitionExtra.CREATOR); 1433 byte[] data = in.readBlob(); 1434 int audioCapabilities = in.readInt(); 1435 return new RecognitionConfig(captureRequested, allowMultipleTriggers, keyphrases, data, 1436 audioCapabilities); 1437 } 1438 1439 @Override writeToParcel(Parcel dest, int flags)1440 public void writeToParcel(Parcel dest, int flags) { 1441 dest.writeByte((byte) (captureRequested ? 1 : 0)); 1442 dest.writeByte((byte) (allowMultipleTriggers ? 1 : 0)); 1443 dest.writeTypedArray(keyphrases, flags); 1444 dest.writeBlob(data); 1445 dest.writeInt(audioCapabilities); 1446 } 1447 1448 @Override describeContents()1449 public int describeContents() { 1450 return 0; 1451 } 1452 1453 @Override toString()1454 public String toString() { 1455 return "RecognitionConfig [captureRequested=" + captureRequested 1456 + ", allowMultipleTriggers=" + allowMultipleTriggers + ", keyphrases=" 1457 + Arrays.toString(keyphrases) + ", data=" + Arrays.toString(data) 1458 + ", audioCapabilities=" + Integer.toHexString(audioCapabilities) + "]"; 1459 } 1460 } 1461 1462 /** 1463 * Confidence level for users defined in a keyphrase. 1464 * - The confidence level is expressed in percent (0% -100%). 1465 * When used in a {@link KeyphraseRecognitionEvent} it indicates the detected confidence level 1466 * When used in a {@link RecognitionConfig} it indicates the minimum confidence level that 1467 * should trigger a recognition. 1468 * - The user ID is derived from the system ID {@link android.os.UserHandle#getIdentifier()}. 1469 * 1470 * @hide 1471 */ 1472 public static class ConfidenceLevel implements Parcelable { 1473 @UnsupportedAppUsage 1474 public final int userId; 1475 @UnsupportedAppUsage 1476 public final int confidenceLevel; 1477 1478 @UnsupportedAppUsage ConfidenceLevel(int userId, int confidenceLevel)1479 public ConfidenceLevel(int userId, int confidenceLevel) { 1480 this.userId = userId; 1481 this.confidenceLevel = confidenceLevel; 1482 } 1483 1484 public static final @android.annotation.NonNull Parcelable.Creator<ConfidenceLevel> CREATOR 1485 = new Parcelable.Creator<ConfidenceLevel>() { 1486 public ConfidenceLevel createFromParcel(Parcel in) { 1487 return ConfidenceLevel.fromParcel(in); 1488 } 1489 1490 public ConfidenceLevel[] newArray(int size) { 1491 return new ConfidenceLevel[size]; 1492 } 1493 }; 1494 fromParcel(Parcel in)1495 private static ConfidenceLevel fromParcel(Parcel in) { 1496 int userId = in.readInt(); 1497 int confidenceLevel = in.readInt(); 1498 return new ConfidenceLevel(userId, confidenceLevel); 1499 } 1500 1501 @Override writeToParcel(Parcel dest, int flags)1502 public void writeToParcel(Parcel dest, int flags) { 1503 dest.writeInt(userId); 1504 dest.writeInt(confidenceLevel); 1505 } 1506 1507 @Override describeContents()1508 public int describeContents() { 1509 return 0; 1510 } 1511 1512 @Override hashCode()1513 public int hashCode() { 1514 final int prime = 31; 1515 int result = 1; 1516 result = prime * result + confidenceLevel; 1517 result = prime * result + userId; 1518 return result; 1519 } 1520 1521 @Override equals(Object obj)1522 public boolean equals(Object obj) { 1523 if (this == obj) 1524 return true; 1525 if (obj == null) 1526 return false; 1527 if (getClass() != obj.getClass()) 1528 return false; 1529 ConfidenceLevel other = (ConfidenceLevel) obj; 1530 if (confidenceLevel != other.confidenceLevel) 1531 return false; 1532 if (userId != other.userId) 1533 return false; 1534 return true; 1535 } 1536 1537 @Override toString()1538 public String toString() { 1539 return "ConfidenceLevel [userId=" + userId 1540 + ", confidenceLevel=" + confidenceLevel + "]"; 1541 } 1542 } 1543 1544 /** 1545 * Additional data conveyed by a {@link KeyphraseRecognitionEvent} 1546 * for a key phrase detection. 1547 * 1548 * @hide 1549 */ 1550 public static class KeyphraseRecognitionExtra implements Parcelable { 1551 /** The keyphrase ID */ 1552 @UnsupportedAppUsage 1553 public final int id; 1554 1555 /** Recognition modes matched for this event */ 1556 @UnsupportedAppUsage 1557 public final int recognitionModes; 1558 1559 /** Confidence level for mode RECOGNITION_MODE_VOICE_TRIGGER when user identification 1560 * is not performed */ 1561 @UnsupportedAppUsage 1562 public final int coarseConfidenceLevel; 1563 1564 /** Confidence levels for all users recognized (KeyphraseRecognitionEvent) or to 1565 * be recognized (RecognitionConfig) */ 1566 @UnsupportedAppUsage 1567 @NonNull 1568 public final ConfidenceLevel[] confidenceLevels; 1569 1570 @UnsupportedAppUsage KeyphraseRecognitionExtra(int id, int recognitionModes, int coarseConfidenceLevel, @Nullable ConfidenceLevel[] confidenceLevels)1571 public KeyphraseRecognitionExtra(int id, int recognitionModes, int coarseConfidenceLevel, 1572 @Nullable ConfidenceLevel[] confidenceLevels) { 1573 this.id = id; 1574 this.recognitionModes = recognitionModes; 1575 this.coarseConfidenceLevel = coarseConfidenceLevel; 1576 this.confidenceLevels = 1577 confidenceLevels != null ? confidenceLevels : new ConfidenceLevel[0]; 1578 } 1579 1580 public static final @android.annotation.NonNull Parcelable.Creator<KeyphraseRecognitionExtra> CREATOR 1581 = new Parcelable.Creator<KeyphraseRecognitionExtra>() { 1582 public KeyphraseRecognitionExtra createFromParcel(Parcel in) { 1583 return KeyphraseRecognitionExtra.fromParcel(in); 1584 } 1585 1586 public KeyphraseRecognitionExtra[] newArray(int size) { 1587 return new KeyphraseRecognitionExtra[size]; 1588 } 1589 }; 1590 fromParcel(Parcel in)1591 private static KeyphraseRecognitionExtra fromParcel(Parcel in) { 1592 int id = in.readInt(); 1593 int recognitionModes = in.readInt(); 1594 int coarseConfidenceLevel = in.readInt(); 1595 ConfidenceLevel[] confidenceLevels = in.createTypedArray(ConfidenceLevel.CREATOR); 1596 return new KeyphraseRecognitionExtra(id, recognitionModes, coarseConfidenceLevel, 1597 confidenceLevels); 1598 } 1599 1600 @Override writeToParcel(Parcel dest, int flags)1601 public void writeToParcel(Parcel dest, int flags) { 1602 dest.writeInt(id); 1603 dest.writeInt(recognitionModes); 1604 dest.writeInt(coarseConfidenceLevel); 1605 dest.writeTypedArray(confidenceLevels, flags); 1606 } 1607 1608 @Override describeContents()1609 public int describeContents() { 1610 return 0; 1611 } 1612 1613 @Override hashCode()1614 public int hashCode() { 1615 final int prime = 31; 1616 int result = 1; 1617 result = prime * result + Arrays.hashCode(confidenceLevels); 1618 result = prime * result + id; 1619 result = prime * result + recognitionModes; 1620 result = prime * result + coarseConfidenceLevel; 1621 return result; 1622 } 1623 1624 @Override equals(Object obj)1625 public boolean equals(Object obj) { 1626 if (this == obj) 1627 return true; 1628 if (obj == null) 1629 return false; 1630 if (getClass() != obj.getClass()) 1631 return false; 1632 KeyphraseRecognitionExtra other = (KeyphraseRecognitionExtra) obj; 1633 if (!Arrays.equals(confidenceLevels, other.confidenceLevels)) 1634 return false; 1635 if (id != other.id) 1636 return false; 1637 if (recognitionModes != other.recognitionModes) 1638 return false; 1639 if (coarseConfidenceLevel != other.coarseConfidenceLevel) 1640 return false; 1641 return true; 1642 } 1643 1644 @Override toString()1645 public String toString() { 1646 return "KeyphraseRecognitionExtra [id=" + id + ", recognitionModes=" + recognitionModes 1647 + ", coarseConfidenceLevel=" + coarseConfidenceLevel 1648 + ", confidenceLevels=" + Arrays.toString(confidenceLevels) + "]"; 1649 } 1650 } 1651 1652 /** 1653 * Specialized {@link RecognitionEvent} for a key phrase detection. 1654 * 1655 * @hide 1656 */ 1657 public static class KeyphraseRecognitionEvent extends RecognitionEvent implements Parcelable { 1658 /** Indicates if the key phrase is present in the buffered audio available for capture */ 1659 @UnsupportedAppUsage 1660 @NonNull 1661 public final KeyphraseRecognitionExtra[] keyphraseExtras; 1662 1663 @UnsupportedAppUsage KeyphraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable, int captureSession, int captureDelayMs, int capturePreambleMs, boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data, @Nullable KeyphraseRecognitionExtra[] keyphraseExtras)1664 public KeyphraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable, 1665 int captureSession, int captureDelayMs, int capturePreambleMs, 1666 boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data, 1667 @Nullable KeyphraseRecognitionExtra[] keyphraseExtras) { 1668 super(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs, 1669 capturePreambleMs, triggerInData, captureFormat, data); 1670 this.keyphraseExtras = 1671 keyphraseExtras != null ? keyphraseExtras : new KeyphraseRecognitionExtra[0]; 1672 } 1673 1674 public static final @android.annotation.NonNull Parcelable.Creator<KeyphraseRecognitionEvent> CREATOR 1675 = new Parcelable.Creator<KeyphraseRecognitionEvent>() { 1676 public KeyphraseRecognitionEvent createFromParcel(Parcel in) { 1677 return KeyphraseRecognitionEvent.fromParcelForKeyphrase(in); 1678 } 1679 1680 public KeyphraseRecognitionEvent[] newArray(int size) { 1681 return new KeyphraseRecognitionEvent[size]; 1682 } 1683 }; 1684 fromParcelForKeyphrase(Parcel in)1685 private static KeyphraseRecognitionEvent fromParcelForKeyphrase(Parcel in) { 1686 int status = in.readInt(); 1687 int soundModelHandle = in.readInt(); 1688 boolean captureAvailable = in.readByte() == 1; 1689 int captureSession = in.readInt(); 1690 int captureDelayMs = in.readInt(); 1691 int capturePreambleMs = in.readInt(); 1692 boolean triggerInData = in.readByte() == 1; 1693 AudioFormat captureFormat = null; 1694 if (in.readByte() == 1) { 1695 int sampleRate = in.readInt(); 1696 int encoding = in.readInt(); 1697 int channelMask = in.readInt(); 1698 captureFormat = (new AudioFormat.Builder()) 1699 .setChannelMask(channelMask) 1700 .setEncoding(encoding) 1701 .setSampleRate(sampleRate) 1702 .build(); 1703 } 1704 byte[] data = in.readBlob(); 1705 KeyphraseRecognitionExtra[] keyphraseExtras = 1706 in.createTypedArray(KeyphraseRecognitionExtra.CREATOR); 1707 return new KeyphraseRecognitionEvent(status, soundModelHandle, captureAvailable, 1708 captureSession, captureDelayMs, capturePreambleMs, triggerInData, 1709 captureFormat, data, keyphraseExtras); 1710 } 1711 1712 @Override writeToParcel(Parcel dest, int flags)1713 public void writeToParcel(Parcel dest, int flags) { 1714 dest.writeInt(status); 1715 dest.writeInt(soundModelHandle); 1716 dest.writeByte((byte) (captureAvailable ? 1 : 0)); 1717 dest.writeInt(captureSession); 1718 dest.writeInt(captureDelayMs); 1719 dest.writeInt(capturePreambleMs); 1720 dest.writeByte((byte) (triggerInData ? 1 : 0)); 1721 if (captureFormat != null) { 1722 dest.writeByte((byte)1); 1723 dest.writeInt(captureFormat.getSampleRate()); 1724 dest.writeInt(captureFormat.getEncoding()); 1725 dest.writeInt(captureFormat.getChannelMask()); 1726 } else { 1727 dest.writeByte((byte)0); 1728 } 1729 dest.writeBlob(data); 1730 dest.writeTypedArray(keyphraseExtras, flags); 1731 } 1732 1733 @Override describeContents()1734 public int describeContents() { 1735 return 0; 1736 } 1737 1738 @Override hashCode()1739 public int hashCode() { 1740 final int prime = 31; 1741 int result = super.hashCode(); 1742 result = prime * result + Arrays.hashCode(keyphraseExtras); 1743 return result; 1744 } 1745 1746 @Override equals(Object obj)1747 public boolean equals(Object obj) { 1748 if (this == obj) 1749 return true; 1750 if (!super.equals(obj)) 1751 return false; 1752 if (getClass() != obj.getClass()) 1753 return false; 1754 KeyphraseRecognitionEvent other = (KeyphraseRecognitionEvent) obj; 1755 if (!Arrays.equals(keyphraseExtras, other.keyphraseExtras)) 1756 return false; 1757 return true; 1758 } 1759 1760 @Override toString()1761 public String toString() { 1762 return "KeyphraseRecognitionEvent [keyphraseExtras=" + Arrays.toString(keyphraseExtras) 1763 + ", status=" + status 1764 + ", soundModelHandle=" + soundModelHandle + ", captureAvailable=" 1765 + captureAvailable + ", captureSession=" + captureSession + ", captureDelayMs=" 1766 + captureDelayMs + ", capturePreambleMs=" + capturePreambleMs 1767 + ", triggerInData=" + triggerInData 1768 + ((captureFormat == null) ? "" : 1769 (", sampleRate=" + captureFormat.getSampleRate())) 1770 + ((captureFormat == null) ? "" : 1771 (", encoding=" + captureFormat.getEncoding())) 1772 + ((captureFormat == null) ? "" : 1773 (", channelMask=" + captureFormat.getChannelMask())) 1774 + ", data=" + (data == null ? 0 : data.length) + "]"; 1775 } 1776 } 1777 1778 /** 1779 * Sub-class of RecognitionEvent specifically for sound-trigger based sound 1780 * models(non-keyphrase). Currently does not contain any additional fields. 1781 * 1782 * @hide 1783 */ 1784 public static class GenericRecognitionEvent extends RecognitionEvent implements Parcelable { 1785 @UnsupportedAppUsage GenericRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable, int captureSession, int captureDelayMs, int capturePreambleMs, boolean triggerInData, @NonNull AudioFormat captureFormat, @Nullable byte[] data)1786 public GenericRecognitionEvent(int status, int soundModelHandle, 1787 boolean captureAvailable, int captureSession, int captureDelayMs, 1788 int capturePreambleMs, boolean triggerInData, @NonNull AudioFormat captureFormat, 1789 @Nullable byte[] data) { 1790 super(status, soundModelHandle, captureAvailable, captureSession, 1791 captureDelayMs, capturePreambleMs, triggerInData, captureFormat, 1792 data); 1793 } 1794 1795 public static final @android.annotation.NonNull Parcelable.Creator<GenericRecognitionEvent> CREATOR 1796 = new Parcelable.Creator<GenericRecognitionEvent>() { 1797 public GenericRecognitionEvent createFromParcel(Parcel in) { 1798 return GenericRecognitionEvent.fromParcelForGeneric(in); 1799 } 1800 1801 public GenericRecognitionEvent[] newArray(int size) { 1802 return new GenericRecognitionEvent[size]; 1803 } 1804 }; 1805 fromParcelForGeneric(Parcel in)1806 private static GenericRecognitionEvent fromParcelForGeneric(Parcel in) { 1807 RecognitionEvent event = RecognitionEvent.fromParcel(in); 1808 return new GenericRecognitionEvent(event.status, event.soundModelHandle, 1809 event.captureAvailable, event.captureSession, event.captureDelayMs, 1810 event.capturePreambleMs, event.triggerInData, event.captureFormat, event.data); 1811 } 1812 1813 @Override equals(Object obj)1814 public boolean equals(Object obj) { 1815 if (this == obj) 1816 return true; 1817 if (obj == null) 1818 return false; 1819 if (getClass() != obj.getClass()) return false; 1820 RecognitionEvent other = (RecognitionEvent) obj; 1821 return super.equals(obj); 1822 } 1823 1824 @Override toString()1825 public String toString() { 1826 return "GenericRecognitionEvent ::" + super.toString(); 1827 } 1828 } 1829 1830 /** 1831 * Status codes for {@link SoundModelEvent} 1832 */ 1833 /** 1834 * Sound Model was updated 1835 * 1836 * @hide 1837 */ 1838 public static final int SOUNDMODEL_STATUS_UPDATED = 0; 1839 1840 /** 1841 * A SoundModelEvent is provided by the 1842 * {@link StatusListener#onSoundModelUpdate(SoundModelEvent)} 1843 * callback when a sound model has been updated by the implementation 1844 * 1845 * @hide 1846 */ 1847 public static class SoundModelEvent implements Parcelable { 1848 /** Status e.g {@link #SOUNDMODEL_STATUS_UPDATED} */ 1849 public final int status; 1850 /** The updated sound model handle */ 1851 public final int soundModelHandle; 1852 /** New sound model data */ 1853 @NonNull 1854 public final byte[] data; 1855 1856 @UnsupportedAppUsage SoundModelEvent(int status, int soundModelHandle, @Nullable byte[] data)1857 SoundModelEvent(int status, int soundModelHandle, @Nullable byte[] data) { 1858 this.status = status; 1859 this.soundModelHandle = soundModelHandle; 1860 this.data = data != null ? data : new byte[0]; 1861 } 1862 1863 public static final @android.annotation.NonNull Parcelable.Creator<SoundModelEvent> CREATOR 1864 = new Parcelable.Creator<SoundModelEvent>() { 1865 public SoundModelEvent createFromParcel(Parcel in) { 1866 return SoundModelEvent.fromParcel(in); 1867 } 1868 1869 public SoundModelEvent[] newArray(int size) { 1870 return new SoundModelEvent[size]; 1871 } 1872 }; 1873 fromParcel(Parcel in)1874 private static SoundModelEvent fromParcel(Parcel in) { 1875 int status = in.readInt(); 1876 int soundModelHandle = in.readInt(); 1877 byte[] data = in.readBlob(); 1878 return new SoundModelEvent(status, soundModelHandle, data); 1879 } 1880 1881 @Override describeContents()1882 public int describeContents() { 1883 return 0; 1884 } 1885 1886 @Override writeToParcel(Parcel dest, int flags)1887 public void writeToParcel(Parcel dest, int flags) { 1888 dest.writeInt(status); 1889 dest.writeInt(soundModelHandle); 1890 dest.writeBlob(data); 1891 } 1892 1893 @Override hashCode()1894 public int hashCode() { 1895 final int prime = 31; 1896 int result = 1; 1897 result = prime * result + Arrays.hashCode(data); 1898 result = prime * result + soundModelHandle; 1899 result = prime * result + status; 1900 return result; 1901 } 1902 1903 @Override equals(Object obj)1904 public boolean equals(Object obj) { 1905 if (this == obj) 1906 return true; 1907 if (obj == null) 1908 return false; 1909 if (getClass() != obj.getClass()) 1910 return false; 1911 SoundModelEvent other = (SoundModelEvent) obj; 1912 if (!Arrays.equals(data, other.data)) 1913 return false; 1914 if (soundModelHandle != other.soundModelHandle) 1915 return false; 1916 if (status != other.status) 1917 return false; 1918 return true; 1919 } 1920 1921 @Override toString()1922 public String toString() { 1923 return "SoundModelEvent [status=" + status + ", soundModelHandle=" + soundModelHandle 1924 + ", data=" + (data == null ? 0 : data.length) + "]"; 1925 } 1926 } 1927 1928 /** 1929 * Native service state. {@link StatusListener#onServiceStateChange(int)} 1930 */ 1931 // Keep in sync with system/core/include/system/sound_trigger.h 1932 /** 1933 * Sound trigger service is enabled 1934 * 1935 * @hide 1936 */ 1937 public static final int SERVICE_STATE_ENABLED = 0; 1938 /** 1939 * Sound trigger service is disabled 1940 * 1941 * @hide 1942 */ 1943 public static final int SERVICE_STATE_DISABLED = 1; 1944 private static Object mServiceLock = new Object(); 1945 private static ISoundTriggerMiddlewareService mService; 1946 /** 1947 * @return returns current package name. 1948 */ getCurrentOpPackageName()1949 static String getCurrentOpPackageName() { 1950 String packageName = ActivityThread.currentOpPackageName(); 1951 if (packageName == null) { 1952 return ""; 1953 } 1954 return packageName; 1955 } 1956 1957 /** 1958 * Translate an exception thrown from interaction with the underlying service to an error code. 1959 * Throws a runtime exception for unexpected conditions. 1960 * @param e The caught exception. 1961 * @return The error code. 1962 * 1963 * @hide 1964 */ handleException(Exception e)1965 static int handleException(Exception e) { 1966 Log.w(TAG, "Exception caught", e); 1967 if (e instanceof RemoteException) { 1968 return STATUS_DEAD_OBJECT; 1969 } 1970 if (e instanceof ServiceSpecificException) { 1971 switch (((ServiceSpecificException) e).errorCode) { 1972 case Status.OPERATION_NOT_SUPPORTED: 1973 return STATUS_INVALID_OPERATION; 1974 case Status.TEMPORARY_PERMISSION_DENIED: 1975 return STATUS_PERMISSION_DENIED; 1976 case Status.DEAD_OBJECT: 1977 return STATUS_DEAD_OBJECT; 1978 case Status.INTERNAL_ERROR: 1979 return STATUS_ERROR; 1980 } 1981 return STATUS_ERROR; 1982 } 1983 if (e instanceof SecurityException) { 1984 return STATUS_PERMISSION_DENIED; 1985 } 1986 if (e instanceof IllegalStateException) { 1987 return STATUS_INVALID_OPERATION; 1988 } 1989 if (e instanceof IllegalArgumentException || e instanceof NullPointerException) { 1990 return STATUS_BAD_VALUE; 1991 } 1992 // This is not one of the conditions represented by our error code, escalate to a 1993 // RuntimeException. 1994 Log.e(TAG, "Escalating unexpected exception: ", e); 1995 throw new RuntimeException(e); 1996 } 1997 1998 /** 1999 * Returns a list of descriptors for all hardware modules loaded. 2000 * @param modules A ModuleProperties array where the list will be returned. 2001 * @return - {@link #STATUS_OK} in case of success 2002 * - {@link #STATUS_ERROR} in case of unspecified error 2003 * - {@link #STATUS_PERMISSION_DENIED} if the caller does not have system permission 2004 * - {@link #STATUS_NO_INIT} if the native service cannot be reached 2005 * - {@link #STATUS_BAD_VALUE} if modules is null 2006 * - {@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails 2007 * 2008 * @hide 2009 */ 2010 @UnsupportedAppUsage listModules(@onNull ArrayList<ModuleProperties> modules)2011 public static int listModules(@NonNull ArrayList<ModuleProperties> modules) { 2012 try { 2013 SoundTriggerModuleDescriptor[] descs = getService().listModules(); 2014 modules.clear(); 2015 modules.ensureCapacity(descs.length); 2016 for (SoundTriggerModuleDescriptor desc : descs) { 2017 modules.add(ConversionUtil.aidl2apiModuleDescriptor(desc)); 2018 } 2019 return STATUS_OK; 2020 } catch (Exception e) { 2021 return handleException(e); 2022 } 2023 } 2024 2025 /** 2026 * Get an interface on a hardware module to control sound models and recognition on 2027 * this module. 2028 * @param moduleId Sound module system identifier {@link ModuleProperties#mId}. mandatory. 2029 * @param listener {@link StatusListener} interface. Mandatory. 2030 * @param handler the Handler that will receive the callabcks. Can be null if default handler 2031 * is OK. 2032 * @return a valid sound module in case of success or null in case of error. 2033 * 2034 * @hide 2035 */ 2036 @UnsupportedAppUsage attachModule(int moduleId, @NonNull StatusListener listener, @Nullable Handler handler)2037 public static @NonNull SoundTriggerModule attachModule(int moduleId, 2038 @NonNull StatusListener listener, 2039 @Nullable Handler handler) { 2040 Looper looper = handler != null ? handler.getLooper() : Looper.getMainLooper(); 2041 try { 2042 return new SoundTriggerModule(getService(), moduleId, listener, looper); 2043 } catch (Exception e) { 2044 Log.e(TAG, "", e); 2045 return null; 2046 } 2047 } 2048 getService()2049 private static ISoundTriggerMiddlewareService getService() { 2050 synchronized (mServiceLock) { 2051 while (true) { 2052 IBinder binder = null; 2053 try { 2054 binder = 2055 ServiceManager.getServiceOrThrow( 2056 Context.SOUND_TRIGGER_MIDDLEWARE_SERVICE); 2057 binder.linkToDeath(() -> { 2058 synchronized (mServiceLock) { 2059 mService = null; 2060 } 2061 }, 0); 2062 mService = ISoundTriggerMiddlewareService.Stub.asInterface(binder); 2063 break; 2064 } catch (Exception e) { 2065 Log.e(TAG, "Failed to bind to soundtrigger service", e); 2066 } 2067 } 2068 return mService; 2069 } 2070 2071 } 2072 2073 /** 2074 * Interface provided by the client application when attaching to a {@link SoundTriggerModule} 2075 * to received recognition and error notifications. 2076 * 2077 * @hide 2078 */ 2079 public static interface StatusListener { 2080 /** 2081 * Called when recognition succeeds of fails 2082 */ onRecognition(RecognitionEvent event)2083 public abstract void onRecognition(RecognitionEvent event); 2084 2085 /** 2086 * Called when a sound model has been updated 2087 */ onSoundModelUpdate(SoundModelEvent event)2088 public abstract void onSoundModelUpdate(SoundModelEvent event); 2089 2090 /** 2091 * Called when the sound trigger native service state changes. 2092 * @param state Native service state. One of {@link SoundTrigger#SERVICE_STATE_ENABLED}, 2093 * {@link SoundTrigger#SERVICE_STATE_DISABLED} 2094 */ onServiceStateChange(int state)2095 public abstract void onServiceStateChange(int state); 2096 2097 /** 2098 * Called when the sound trigger native service dies 2099 */ onServiceDied()2100 public abstract void onServiceDied(); 2101 } 2102 } 2103