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