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