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