1 /* 2 * Copyright (C) 2020 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.os; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.hardware.vibrator.Braking; 22 import android.hardware.vibrator.IVibrator; 23 import android.os.vibrator.Flags; 24 import android.util.IndentingPrintWriter; 25 import android.util.MathUtils; 26 import android.util.Pair; 27 import android.util.Range; 28 import android.util.SparseBooleanArray; 29 import android.util.SparseIntArray; 30 31 import com.android.internal.util.Preconditions; 32 33 import java.util.ArrayList; 34 import java.util.Arrays; 35 import java.util.Comparator; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.Objects; 39 import java.util.TreeMap; 40 41 /** 42 * A VibratorInfo describes the capabilities of a {@link Vibrator}. 43 * 44 * <p>This description includes its capabilities, list of supported effects and composition 45 * primitives. 46 * 47 * @hide 48 */ 49 public class VibratorInfo implements Parcelable { 50 private static final String TAG = "VibratorInfo"; 51 52 /** @hide */ 53 public static final VibratorInfo EMPTY_VIBRATOR_INFO = new VibratorInfo.Builder(-1).build(); 54 55 private final int mId; 56 private final long mCapabilities; 57 @Nullable 58 private final SparseBooleanArray mSupportedEffects; 59 @Nullable 60 private final SparseBooleanArray mSupportedBraking; 61 private final SparseIntArray mSupportedPrimitives; 62 private final int mPrimitiveDelayMax; 63 private final int mCompositionSizeMax; 64 private final int mPwlePrimitiveDurationMax; 65 private final int mPwleSizeMax; 66 private final float mQFactor; 67 private final FrequencyProfileLegacy mFrequencyProfileLegacy; 68 private final FrequencyProfile mFrequencyProfile; 69 private final int mMaxEnvelopeEffectSize; 70 private final int mMinEnvelopeEffectControlPointDurationMillis; 71 private final int mMaxEnvelopeEffectControlPointDurationMillis; 72 VibratorInfo(Parcel in)73 VibratorInfo(Parcel in) { 74 mId = in.readInt(); 75 mCapabilities = in.readLong(); 76 mSupportedEffects = in.readSparseBooleanArray(); 77 mSupportedBraking = in.readSparseBooleanArray(); 78 mSupportedPrimitives = in.readSparseIntArray(); 79 mPrimitiveDelayMax = in.readInt(); 80 mCompositionSizeMax = in.readInt(); 81 mPwlePrimitiveDurationMax = in.readInt(); 82 mPwleSizeMax = in.readInt(); 83 mQFactor = in.readFloat(); 84 mFrequencyProfileLegacy = FrequencyProfileLegacy.CREATOR.createFromParcel(in); 85 mFrequencyProfile = FrequencyProfile.CREATOR.createFromParcel(in); 86 mMaxEnvelopeEffectSize = in.readInt(); 87 mMinEnvelopeEffectControlPointDurationMillis = in.readInt(); 88 mMaxEnvelopeEffectControlPointDurationMillis = in.readInt(); 89 } 90 VibratorInfo(int id, @NonNull VibratorInfo baseVibratorInfo)91 public VibratorInfo(int id, @NonNull VibratorInfo baseVibratorInfo) { 92 this(id, baseVibratorInfo.mCapabilities, baseVibratorInfo.mSupportedEffects, 93 baseVibratorInfo.mSupportedBraking, baseVibratorInfo.mSupportedPrimitives, 94 baseVibratorInfo.mPrimitiveDelayMax, baseVibratorInfo.mCompositionSizeMax, 95 baseVibratorInfo.mPwlePrimitiveDurationMax, baseVibratorInfo.mPwleSizeMax, 96 baseVibratorInfo.mQFactor, baseVibratorInfo.mFrequencyProfileLegacy, 97 baseVibratorInfo.mFrequencyProfile, baseVibratorInfo.mMaxEnvelopeEffectSize, 98 baseVibratorInfo.mMinEnvelopeEffectControlPointDurationMillis, 99 baseVibratorInfo.mMaxEnvelopeEffectControlPointDurationMillis); 100 } 101 102 /** 103 * Default constructor. 104 * 105 * @param id The vibrator id. 106 * @param capabilities All capability flags of the vibrator, defined in 107 * IVibrator.CAP_*. 108 * @param supportedEffects All supported predefined effects, enum values from 109 * {@link android.hardware.vibrator.Effect}. 110 * @param supportedBraking All supported braking types, enum values from {@link 111 * Braking}. 112 * @param supportedPrimitives All supported primitive effects, key are enum values from 113 * {@link android.hardware.vibrator.CompositePrimitive} and 114 * values are estimated durations in milliseconds. 115 * @param primitiveDelayMax The maximum delay that can be set to a composition primitive 116 * in milliseconds. 117 * @param compositionSizeMax The maximum number of primitives supported by a composition. 118 * @param pwlePrimitiveDurationMax The maximum duration of a PWLE primitive in milliseconds. 119 * @param pwleSizeMax The maximum number of primitives supported by a PWLE 120 * composition. 121 * @param qFactor The vibrator quality factor. 122 * @param frequencyProfileLegacy The description of the vibrator supported frequencies and max 123 * amplitude mappings. 124 * @param frequencyProfile The description of the vibrator supported frequencies and 125 * output acceleration mappings. 126 * @param maxEnvelopeEffectSize The maximum number of control points supported for an 127 * envelope effect. 128 * @param minEnvelopeEffectControlPointDurationMillis The minimum duration supported 129 * between two control points within an 130 * envelope effect. 131 * @param maxEnvelopeEffectControlPointDurationMillis The maximum duration supported 132 * between two control points within an 133 * envelope effect. 134 * 135 * @hide 136 */ VibratorInfo(int id, long capabilities, @Nullable SparseBooleanArray supportedEffects, @Nullable SparseBooleanArray supportedBraking, @NonNull SparseIntArray supportedPrimitives, int primitiveDelayMax, int compositionSizeMax, int pwlePrimitiveDurationMax, int pwleSizeMax, float qFactor, @NonNull FrequencyProfileLegacy frequencyProfileLegacy, @NonNull FrequencyProfile frequencyProfile, int maxEnvelopeEffectSize, int minEnvelopeEffectControlPointDurationMillis, int maxEnvelopeEffectControlPointDurationMillis)137 public VibratorInfo(int id, long capabilities, @Nullable SparseBooleanArray supportedEffects, 138 @Nullable SparseBooleanArray supportedBraking, 139 @NonNull SparseIntArray supportedPrimitives, int primitiveDelayMax, 140 int compositionSizeMax, int pwlePrimitiveDurationMax, int pwleSizeMax, 141 float qFactor, @NonNull FrequencyProfileLegacy frequencyProfileLegacy, 142 @NonNull FrequencyProfile frequencyProfile, int maxEnvelopeEffectSize, 143 int minEnvelopeEffectControlPointDurationMillis, 144 int maxEnvelopeEffectControlPointDurationMillis) { 145 Preconditions.checkNotNull(supportedPrimitives); 146 Preconditions.checkNotNull(frequencyProfileLegacy); 147 Preconditions.checkNotNull(frequencyProfile); 148 mId = id; 149 mCapabilities = capabilities; 150 mSupportedEffects = supportedEffects == null ? null : supportedEffects.clone(); 151 mSupportedBraking = supportedBraking == null ? null : supportedBraking.clone(); 152 mSupportedPrimitives = supportedPrimitives.clone(); 153 mPrimitiveDelayMax = primitiveDelayMax; 154 mCompositionSizeMax = compositionSizeMax; 155 mPwlePrimitiveDurationMax = pwlePrimitiveDurationMax; 156 mPwleSizeMax = pwleSizeMax; 157 mQFactor = qFactor; 158 mFrequencyProfileLegacy = frequencyProfileLegacy; 159 mFrequencyProfile = frequencyProfile; 160 mMaxEnvelopeEffectSize = maxEnvelopeEffectSize; 161 mMinEnvelopeEffectControlPointDurationMillis = 162 minEnvelopeEffectControlPointDurationMillis; 163 mMaxEnvelopeEffectControlPointDurationMillis = 164 maxEnvelopeEffectControlPointDurationMillis; 165 } 166 167 @Override writeToParcel(Parcel dest, int flags)168 public void writeToParcel(Parcel dest, int flags) { 169 dest.writeInt(mId); 170 dest.writeLong(mCapabilities); 171 dest.writeSparseBooleanArray(mSupportedEffects); 172 dest.writeSparseBooleanArray(mSupportedBraking); 173 dest.writeSparseIntArray(mSupportedPrimitives); 174 dest.writeInt(mPrimitiveDelayMax); 175 dest.writeInt(mCompositionSizeMax); 176 dest.writeInt(mPwlePrimitiveDurationMax); 177 dest.writeInt(mPwleSizeMax); 178 dest.writeFloat(mQFactor); 179 mFrequencyProfileLegacy.writeToParcel(dest, flags); 180 mFrequencyProfile.writeToParcel(dest, flags); 181 dest.writeInt(mMaxEnvelopeEffectSize); 182 dest.writeInt(mMinEnvelopeEffectControlPointDurationMillis); 183 dest.writeInt(mMaxEnvelopeEffectControlPointDurationMillis); 184 } 185 186 @Override describeContents()187 public int describeContents() { 188 return 0; 189 } 190 191 @Override equals(Object o)192 public boolean equals(Object o) { 193 if (this == o) { 194 return true; 195 } 196 if (!(o instanceof VibratorInfo)) { 197 return false; 198 } 199 VibratorInfo that = (VibratorInfo) o; 200 return mId == that.mId && equalContent(that); 201 } 202 203 /** 204 * Returns {@code true} only if the properties and capabilities of the provided info, except for 205 * the ID, equals to this info. Returns {@code false} otherwise. 206 * 207 * @hide 208 */ equalContent(VibratorInfo that)209 public boolean equalContent(VibratorInfo that) { 210 int supportedPrimitivesCount = mSupportedPrimitives.size(); 211 if (supportedPrimitivesCount != that.mSupportedPrimitives.size()) { 212 return false; 213 } 214 for (int i = 0; i < supportedPrimitivesCount; i++) { 215 if (mSupportedPrimitives.keyAt(i) != that.mSupportedPrimitives.keyAt(i)) { 216 return false; 217 } 218 if (mSupportedPrimitives.valueAt(i) != that.mSupportedPrimitives.valueAt(i)) { 219 return false; 220 } 221 } 222 return mCapabilities == that.mCapabilities 223 && mPrimitiveDelayMax == that.mPrimitiveDelayMax 224 && mCompositionSizeMax == that.mCompositionSizeMax 225 && mPwlePrimitiveDurationMax == that.mPwlePrimitiveDurationMax 226 && mPwleSizeMax == that.mPwleSizeMax 227 && Objects.equals(mSupportedEffects, that.mSupportedEffects) 228 && Objects.equals(mSupportedBraking, that.mSupportedBraking) 229 && Objects.equals(mQFactor, that.mQFactor) 230 && Objects.equals(mFrequencyProfileLegacy, that.mFrequencyProfileLegacy) 231 && Objects.equals(mFrequencyProfile, that.mFrequencyProfile) 232 && mMaxEnvelopeEffectSize == that.mMaxEnvelopeEffectSize 233 && mMinEnvelopeEffectControlPointDurationMillis 234 == that.mMinEnvelopeEffectControlPointDurationMillis 235 && mMaxEnvelopeEffectControlPointDurationMillis 236 == that.mMaxEnvelopeEffectControlPointDurationMillis; 237 } 238 239 @Override hashCode()240 public int hashCode() { 241 int hashCode = Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedBraking, 242 mQFactor, mFrequencyProfileLegacy, mFrequencyProfile); 243 for (int i = 0; i < mSupportedPrimitives.size(); i++) { 244 hashCode = 31 * hashCode + mSupportedPrimitives.keyAt(i); 245 hashCode = 31 * hashCode + mSupportedPrimitives.valueAt(i); 246 } 247 return hashCode; 248 } 249 250 @Override toString()251 public String toString() { 252 return "VibratorInfo{" 253 + "mId=" + mId 254 + ", mCapabilities=" + Arrays.toString(getCapabilitiesNames()) 255 + ", mCapabilities flags=" + Long.toBinaryString(mCapabilities) 256 + ", mSupportedEffects=" + Arrays.toString(getSupportedEffectsNames()) 257 + ", mSupportedBraking=" + Arrays.toString(getSupportedBrakingNames()) 258 + ", mSupportedPrimitives=" + Arrays.toString(getSupportedPrimitivesNames()) 259 + ", mPrimitiveDelayMax=" + mPrimitiveDelayMax 260 + ", mCompositionSizeMax=" + mCompositionSizeMax 261 + ", mPwlePrimitiveDurationMax=" + mPwlePrimitiveDurationMax 262 + ", mPwleSizeMax=" + mPwleSizeMax 263 + ", mQFactor=" + mQFactor 264 + ", mFrequencyProfileLegacy=" + mFrequencyProfileLegacy 265 + ", mFrequencyProfile=" + mFrequencyProfile 266 + ", mMaxEnvelopeEffectSize=" + mMaxEnvelopeEffectSize 267 + ", mMinEnvelopeEffectControlPointDurationMillis=" 268 + mMinEnvelopeEffectControlPointDurationMillis 269 + ", mMaxEnvelopeEffectControlPointDurationMillis=" 270 + mMaxEnvelopeEffectControlPointDurationMillis 271 + '}'; 272 } 273 274 /** @hide */ dump(IndentingPrintWriter pw)275 public void dump(IndentingPrintWriter pw) { 276 pw.println("VibratorInfo:"); 277 pw.increaseIndent(); 278 pw.println("id = " + mId); 279 pw.println("capabilities = " + Arrays.toString(getCapabilitiesNames())); 280 pw.println("capabilitiesFlags = " + Long.toBinaryString(mCapabilities)); 281 pw.println("supportedEffects = " + Arrays.toString(getSupportedEffectsNames())); 282 pw.println("supportedPrimitives = " + Arrays.toString(getSupportedPrimitivesNames())); 283 pw.println("supportedBraking = " + Arrays.toString(getSupportedBrakingNames())); 284 pw.println("primitiveDelayMax = " + mPrimitiveDelayMax); 285 pw.println("compositionSizeMax = " + mCompositionSizeMax); 286 pw.println("pwlePrimitiveDurationMax = " + mPwlePrimitiveDurationMax); 287 pw.println("pwleSizeMax = " + mPwleSizeMax); 288 pw.println("q-factor = " + mQFactor); 289 pw.println("frequencyProfileLegacy = " + mFrequencyProfileLegacy); 290 pw.println("frequencyProfile = " + mFrequencyProfile); 291 pw.println("mMaxEnvelopeEffectSize = " + mMaxEnvelopeEffectSize); 292 pw.println("mMinEnvelopeEffectControlPointDurationMillis = " 293 + mMinEnvelopeEffectControlPointDurationMillis); 294 pw.println("mMaxEnvelopeEffectControlPointDurationMillis = " 295 + mMaxEnvelopeEffectControlPointDurationMillis); 296 pw.decreaseIndent(); 297 } 298 299 /** Return the id of this vibrator. */ getId()300 public int getId() { 301 return mId; 302 } 303 304 /** 305 * Check whether the vibrator has amplitude control. 306 * 307 * @return True if the hardware can control the amplitude of the vibrations, otherwise false. 308 */ hasAmplitudeControl()309 public boolean hasAmplitudeControl() { 310 return hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL); 311 } 312 313 /** 314 * Check whether the vibrator has frequency control. 315 * 316 * @return True if the hardware can control the frequency of the vibrations, otherwise false. 317 */ hasFrequencyControl()318 public boolean hasFrequencyControl() { 319 return hasCapability(IVibrator.CAP_FREQUENCY_CONTROL); 320 } 321 322 /** 323 * Returns a default value to be applied to composed PWLE effects for braking. 324 * 325 * @return a supported braking value, one of android.hardware.vibrator.Braking.* 326 * @hide 327 */ getDefaultBraking()328 public int getDefaultBraking() { 329 if (mSupportedBraking != null) { 330 int size = mSupportedBraking.size(); 331 for (int i = 0; i < size; i++) { 332 if (mSupportedBraking.keyAt(i) != Braking.NONE) { 333 return mSupportedBraking.keyAt(i); 334 } 335 } 336 } 337 return Braking.NONE; 338 } 339 340 /** @hide */ 341 @Nullable getSupportedBraking()342 public SparseBooleanArray getSupportedBraking() { 343 if (mSupportedBraking == null) { 344 return null; 345 } 346 return mSupportedBraking.clone(); 347 } 348 349 /** @hide */ isBrakingSupportKnown()350 public boolean isBrakingSupportKnown() { 351 return mSupportedBraking != null; 352 } 353 354 /** @hide */ hasBrakingSupport(@raking int braking)355 public boolean hasBrakingSupport(@Braking int braking) { 356 return (mSupportedBraking != null) && mSupportedBraking.get(braking); 357 } 358 359 /** @hide */ isEffectSupportKnown()360 public boolean isEffectSupportKnown() { 361 return mSupportedEffects != null; 362 } 363 364 /** 365 * Query whether the vibrator supports the given effect. 366 * 367 * @param effectId Which effects to query for. 368 * @return {@link Vibrator#VIBRATION_EFFECT_SUPPORT_YES} if the effect is supported, 369 * {@link Vibrator#VIBRATION_EFFECT_SUPPORT_NO} if it isn't supported, or 370 * {@link Vibrator#VIBRATION_EFFECT_SUPPORT_UNKNOWN} if the system can't determine whether it's 371 * supported or not. 372 */ 373 @Vibrator.VibrationEffectSupport isEffectSupported(@ibrationEffect.EffectType int effectId)374 public int isEffectSupported(@VibrationEffect.EffectType int effectId) { 375 if (mSupportedEffects == null) { 376 return Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN; 377 } 378 return mSupportedEffects.get(effectId) ? Vibrator.VIBRATION_EFFECT_SUPPORT_YES 379 : Vibrator.VIBRATION_EFFECT_SUPPORT_NO; 380 } 381 382 /** @hide */ 383 @Nullable getSupportedEffects()384 public SparseBooleanArray getSupportedEffects() { 385 if (mSupportedEffects == null) { 386 return null; 387 } 388 return mSupportedEffects.clone(); 389 } 390 391 /** 392 * Query whether the vibrator supports the given primitive. 393 * 394 * @param primitiveId Which primitives to query for. 395 * @return Whether the primitive is supported. 396 */ isPrimitiveSupported( @ibrationEffect.Composition.PrimitiveType int primitiveId)397 public boolean isPrimitiveSupported( 398 @VibrationEffect.Composition.PrimitiveType int primitiveId) { 399 return hasCapability(IVibrator.CAP_COMPOSE_EFFECTS) 400 && (mSupportedPrimitives.indexOfKey(primitiveId) >= 0); 401 } 402 403 /** 404 * Query whether or not the vibrator supports all components of a given {@link VibrationEffect} 405 * (i.e. the vibrator can play the given effect as intended). 406 * 407 * <p>See {@link Vibrator#areVibrationFeaturesSupported(VibrationEffect)} for more 408 * information on how the vibrator support is determined. 409 * 410 * @param effect the {@link VibrationEffect} to check if it is supported 411 * @return {@code true} if the vibrator can play the given {@code effect} as intended, 412 * {@code false} otherwise. 413 * 414 * @hide 415 */ areVibrationFeaturesSupported(@onNull VibrationEffect effect)416 public boolean areVibrationFeaturesSupported(@NonNull VibrationEffect effect) { 417 return effect.areVibrationFeaturesSupported(this); 418 } 419 420 /** 421 * Query the estimated duration of given primitive. 422 * 423 * @param primitiveId Which primitives to query for. 424 * @return The duration in milliseconds estimated for the primitive, or zero if primitive not 425 * supported. 426 */ getPrimitiveDuration( @ibrationEffect.Composition.PrimitiveType int primitiveId)427 public int getPrimitiveDuration( 428 @VibrationEffect.Composition.PrimitiveType int primitiveId) { 429 return mSupportedPrimitives.get(primitiveId); 430 } 431 432 /** @hide */ getSupportedPrimitives()433 public SparseIntArray getSupportedPrimitives() { 434 return mSupportedPrimitives.clone(); 435 } 436 437 /** 438 * Query the maximum delay supported for a primitive in a composed effect. 439 * 440 * @return The max delay in milliseconds, or zero if unlimited. 441 */ getPrimitiveDelayMax()442 public int getPrimitiveDelayMax() { 443 return mPrimitiveDelayMax; 444 } 445 446 /** 447 * Query the maximum number of primitives supported in a composed effect. 448 * 449 * @return The max number of primitives supported, or zero if unlimited. 450 */ getCompositionSizeMax()451 public int getCompositionSizeMax() { 452 return mCompositionSizeMax; 453 } 454 455 /** 456 * Query the maximum duration supported for a primitive in a PWLE composition. 457 * 458 * @return The max duration in milliseconds, or zero if unlimited. 459 */ getPwlePrimitiveDurationMax()460 public int getPwlePrimitiveDurationMax() { 461 return mPwlePrimitiveDurationMax; 462 } 463 464 /** 465 * Query the maximum number of primitives supported in a PWLE composition. 466 * 467 * @return The max number of primitives supported, or zero if unlimited. 468 */ getPwleSizeMax()469 public int getPwleSizeMax() { 470 return mPwleSizeMax; 471 } 472 473 /** 474 * Check whether the vibrator supports the creation of envelope effects. 475 * 476 * <p>See {@link Vibrator#areEnvelopeEffectsSupported()} for more information on envelope 477 * effects. 478 * 479 * @return True if the hardware supports creating envelope effects, false otherwise. 480 */ areEnvelopeEffectsSupported()481 public boolean areEnvelopeEffectsSupported() { 482 return hasCapability( 483 IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2); 484 } 485 486 /** 487 * Calculates the maximum allowed duration for an envelope effect, measured in milliseconds. 488 * 489 * @return The maximum duration (in milliseconds) that an envelope effect can have. 490 */ getMaxEnvelopeEffectDurationMillis()491 public int getMaxEnvelopeEffectDurationMillis() { 492 return mMaxEnvelopeEffectSize * mMaxEnvelopeEffectControlPointDurationMillis; 493 } 494 495 /** 496 * Gets the maximum number of control points supported for envelope effects on this device. 497 * 498 * @return The maximum number of control points that can be used to define an envelope effect. 499 */ getMaxEnvelopeEffectSize()500 public int getMaxEnvelopeEffectSize() { 501 return mMaxEnvelopeEffectSize; 502 } 503 504 /** 505 * Gets the minimum allowed duration for any individual segment within an envelope effect, 506 * measured in milliseconds. 507 * 508 * @return The minimum duration (in milliseconds) that a segment within an envelope effect 509 * can have. 510 */ getMinEnvelopeEffectControlPointDurationMillis()511 public int getMinEnvelopeEffectControlPointDurationMillis() { 512 return mMinEnvelopeEffectControlPointDurationMillis; 513 } 514 515 /** 516 * Gets the maximum allowed duration for any individual segment within an envelope effect, 517 * measured in milliseconds. 518 * 519 * @return The maximum duration (in milliseconds) that a segment within an envelope effect 520 * can have. 521 */ getMaxEnvelopeEffectControlPointDurationMillis()522 public int getMaxEnvelopeEffectControlPointDurationMillis() { 523 return mMaxEnvelopeEffectControlPointDurationMillis; 524 } 525 526 /** 527 * Check against this vibrator capabilities. 528 * 529 * @param capability one of IVibrator.CAP_* 530 * @return true if this vibrator has this capability, false otherwise 531 * @hide 532 */ hasCapability(long capability)533 public boolean hasCapability(long capability) { 534 return (mCapabilities & capability) == capability; 535 } 536 537 /** 538 * Gets the resonant frequency of the vibrator. 539 * 540 * @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown or 541 * this vibrator is a composite of multiple physical devices. 542 */ getResonantFrequencyHz()543 public float getResonantFrequencyHz() { 544 if (Flags.normalizedPwleEffects()) { 545 return mFrequencyProfile.mResonantFrequencyHz; 546 } 547 return mFrequencyProfileLegacy.mResonantFrequencyHz; 548 } 549 550 /** 551 * Gets the <a href="https://en.wikipedia.org/wiki/Q_factor">Q factor</a> of the vibrator. 552 * 553 * @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown or 554 * this vibrator is a composite of multiple physical devices. 555 */ getQFactor()556 public float getQFactor() { 557 return mQFactor; 558 } 559 560 /** 561 * Gets the profile of supported frequencies, including the measurements of maximum relative 562 * output acceleration for supported vibration frequencies. 563 * 564 * <p>If the devices does not have frequency control then the profile should be empty. 565 */ 566 @NonNull getFrequencyProfileLegacy()567 public FrequencyProfileLegacy getFrequencyProfileLegacy() { 568 return mFrequencyProfileLegacy; 569 } 570 571 /** 572 * Gets the profile of supported frequencies, including the measurements of maximum 573 * output acceleration for supported vibration frequencies. 574 * 575 * <p>If the devices does not have frequency control then the profile should be empty. 576 */ 577 @NonNull getFrequencyProfile()578 public FrequencyProfile getFrequencyProfile() { 579 return mFrequencyProfile; 580 } 581 582 /** Returns a single int representing all the capabilities of the vibrator. */ getCapabilities()583 public long getCapabilities() { 584 return mCapabilities; 585 } 586 getCapabilitiesNames()587 private String[] getCapabilitiesNames() { 588 List<String> names = new ArrayList<>(); 589 if (hasCapability(IVibrator.CAP_ON_CALLBACK)) { 590 names.add("ON_CALLBACK"); 591 } 592 if (hasCapability(IVibrator.CAP_PERFORM_CALLBACK)) { 593 names.add("PERFORM_CALLBACK"); 594 } 595 if (hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) { 596 names.add("COMPOSE_EFFECTS"); 597 } 598 if (hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) { 599 names.add("COMPOSE_PWLE_EFFECTS"); 600 } 601 if (hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) { 602 names.add("ALWAYS_ON_CONTROL"); 603 } 604 if (hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) { 605 names.add("AMPLITUDE_CONTROL"); 606 } 607 if (hasCapability(IVibrator.CAP_FREQUENCY_CONTROL)) { 608 names.add("FREQUENCY_CONTROL"); 609 } 610 if (hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) { 611 names.add("EXTERNAL_CONTROL"); 612 } 613 if (hasCapability(IVibrator.CAP_EXTERNAL_AMPLITUDE_CONTROL)) { 614 names.add("EXTERNAL_AMPLITUDE_CONTROL"); 615 } 616 if (hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2)) { 617 names.add("CAP_COMPOSE_PWLE_EFFECTS_V2"); 618 } 619 return names.toArray(new String[names.size()]); 620 } 621 getSupportedEffectsNames()622 private String[] getSupportedEffectsNames() { 623 if (mSupportedEffects == null) { 624 return new String[0]; 625 } 626 String[] names = new String[mSupportedEffects.size()]; 627 for (int i = 0; i < mSupportedEffects.size(); i++) { 628 names[i] = VibrationEffect.effectIdToString(mSupportedEffects.keyAt(i)); 629 } 630 return names; 631 } 632 getSupportedBrakingNames()633 private String[] getSupportedBrakingNames() { 634 if (mSupportedBraking == null) { 635 return new String[0]; 636 } 637 String[] names = new String[mSupportedBraking.size()]; 638 for (int i = 0; i < mSupportedBraking.size(); i++) { 639 switch (mSupportedBraking.keyAt(i)) { 640 case Braking.NONE: 641 names[i] = "NONE"; 642 break; 643 case Braking.CLAB: 644 names[i] = "CLAB"; 645 break; 646 default: 647 names[i] = Integer.toString(mSupportedBraking.keyAt(i)); 648 } 649 } 650 return names; 651 } 652 getSupportedPrimitivesNames()653 private String[] getSupportedPrimitivesNames() { 654 int supportedPrimitivesCount = mSupportedPrimitives.size(); 655 String[] names = new String[supportedPrimitivesCount]; 656 for (int i = 0; i < supportedPrimitivesCount; i++) { 657 names[i] = VibrationEffect.Composition.primitiveToString(mSupportedPrimitives.keyAt(i)) 658 + "(" + mSupportedPrimitives.valueAt(i) + "ms)"; 659 } 660 return names; 661 } 662 663 /** 664 * Describes the maximum output acceleration that can be achieved for each supported 665 * frequency in a specific vibrator. 666 * 667 * @hide 668 */ 669 public static final class FrequencyProfile implements Parcelable { 670 671 private final float[] mFrequenciesHz; 672 private final float[] mOutputAccelerationsGs; 673 private final float mResonantFrequencyHz; 674 private final float mMaxOutputAccelerationGs; 675 private final float mMinFrequencyHz; 676 private final float mMaxFrequencyHz; 677 FrequencyProfile(Parcel in)678 public FrequencyProfile(Parcel in) { 679 this(in.readFloat(), in.createFloatArray(), in.createFloatArray()); 680 } 681 682 /** 683 * Default constructor. 684 * 685 * @param resonantFrequencyHz The vibrator resonant frequency, in hertz. 686 * @param frequenciesHz The supported vibration frequencies, in hertz. 687 * @param outputAccelerationsGs The maximum achievable output acceleration (in Gs) the 688 * device can reach at the supported frequencies. 689 */ FrequencyProfile(float resonantFrequencyHz, float[] frequenciesHz, float[] outputAccelerationsGs)690 public FrequencyProfile(float resonantFrequencyHz, float[] frequenciesHz, 691 float[] outputAccelerationsGs) { 692 693 mResonantFrequencyHz = resonantFrequencyHz; 694 695 boolean isValid = !Float.isNaN(resonantFrequencyHz) 696 && (resonantFrequencyHz > 0) 697 && (frequenciesHz != null && outputAccelerationsGs != null) 698 && (frequenciesHz.length == outputAccelerationsGs.length) 699 && (frequenciesHz.length > 0); 700 701 if (!isValid) { 702 mFrequenciesHz = null; 703 mOutputAccelerationsGs = null; 704 mMinFrequencyHz = Float.NaN; 705 mMaxFrequencyHz = Float.NaN; 706 mMaxOutputAccelerationGs = Float.NaN; 707 return; 708 } 709 710 TreeMap<Float, Float> frequencyToOutputAccelerationMap = new TreeMap<>(); 711 712 for (int i = 0; i < frequenciesHz.length; i++) { 713 frequencyToOutputAccelerationMap.putIfAbsent(frequenciesHz[i], 714 outputAccelerationsGs[i]); 715 } 716 717 float[] frequencies = new float[frequencyToOutputAccelerationMap.size()]; 718 float[] accelerations = new float[frequencyToOutputAccelerationMap.size()]; 719 float maxOutputAccelerationGs = 0; 720 int i = 0; 721 for (Map.Entry<Float, Float> entry : frequencyToOutputAccelerationMap.entrySet()) { 722 frequencies[i] = entry.getKey(); 723 accelerations[i] = entry.getValue(); 724 maxOutputAccelerationGs = Math.max(maxOutputAccelerationGs, entry.getValue()); 725 i++; 726 } 727 728 mFrequenciesHz = frequencies; 729 mOutputAccelerationsGs = accelerations; 730 mMinFrequencyHz = mFrequenciesHz[0]; 731 mMaxFrequencyHz = mFrequenciesHz[mFrequenciesHz.length - 1]; 732 mMaxOutputAccelerationGs = maxOutputAccelerationGs; 733 } 734 735 /** Returns true if the supported frequency range is null. */ isEmpty()736 public boolean isEmpty() { 737 return mFrequenciesHz == null; 738 } 739 740 /** 741 * Returns a list of available frequencies. 742 */ 743 @Nullable getFrequenciesHz()744 public float[] getFrequenciesHz() { 745 return mFrequenciesHz; 746 } 747 748 /** Returns the list of available output accelerations */ 749 @Nullable getOutputAccelerationsGs()750 public float[] getOutputAccelerationsGs() { 751 return mOutputAccelerationsGs; 752 } 753 754 /** Maximum output acceleration reachable in Gs when amplitude is 1.0f. */ getMaxOutputAccelerationGs()755 public float getMaxOutputAccelerationGs() { 756 return mMaxOutputAccelerationGs; 757 } 758 759 /** 760 * Calculates the maximum output acceleration for a given frequency using linear 761 * interpolation. 762 * 763 * @param frequencyHz frequency, in hertz, for query. 764 * @return the maximum output acceleration for the given frequency. 765 */ getOutputAccelerationGs(float frequencyHz)766 public float getOutputAccelerationGs(float frequencyHz) { 767 if (mFrequenciesHz == null) { 768 return Float.NaN; 769 } 770 771 if (frequencyHz < mMinFrequencyHz || frequencyHz > mMaxFrequencyHz) { 772 // Outside supported frequency range, not able to vibrate at this frequency. 773 return 0; 774 } 775 776 int idx = Arrays.binarySearch(mFrequenciesHz, frequencyHz); 777 if (idx >= 0) { 778 return mOutputAccelerationsGs[idx]; 779 } 780 781 // This indicates that the value was not found in the list. Adjust index of the 782 // insertion point to be at the lower bound. 783 idx = -idx - 2; 784 785 // Linearly interpolate the output acceleration based on the frequency. 786 return MathUtils.constrainedMap( 787 mOutputAccelerationsGs[idx], mOutputAccelerationsGs[idx + 1], 788 mFrequenciesHz[idx], mFrequenciesHz[idx + 1], 789 frequencyHz); 790 } 791 792 /** The minimum frequency supported, in hertz. */ getMinFrequencyHz()793 public float getMinFrequencyHz() { 794 return mMinFrequencyHz; 795 } 796 797 /** The maximum frequency supported, in hertz. */ getMaxFrequencyHz()798 public float getMaxFrequencyHz() { 799 return mMaxFrequencyHz; 800 } 801 802 /** 803 * Returns the frequency range that supports the specified minimum output 804 * acceleration. 805 * 806 * @return The frequency range, or null if the specified acceleration 807 * is not achievable on the device. 808 */ 809 @Nullable getFrequencyRangeHz(float minOutputAcceleration)810 public Range<Float> getFrequencyRangeHz(float minOutputAcceleration) { 811 if (mFrequenciesHz == null || mOutputAccelerationsGs == null 812 || minOutputAcceleration > mMaxOutputAccelerationGs) { 813 return null; // No frequency range available 814 } 815 816 if (minOutputAcceleration <= 0) { 817 return new Range<>(mMinFrequencyHz, mMaxFrequencyHz); 818 } 819 820 float minFrequency = Float.NaN; 821 float maxFrequency = Float.NaN; 822 int lowerFrequencyBoundIndex = 0; 823 // Find the lower frequency bound 824 for (int i = 0; i < mOutputAccelerationsGs.length; i++) { 825 if (mOutputAccelerationsGs[i] >= minOutputAcceleration) { 826 if (i == 0) { 827 minFrequency = mMinFrequencyHz; 828 } else { 829 minFrequency = MathUtils.constrainedMap( 830 mFrequenciesHz[i - 1], mFrequenciesHz[i], 831 mOutputAccelerationsGs[i - 1], mOutputAccelerationsGs[i], 832 minOutputAcceleration); 833 } 834 lowerFrequencyBoundIndex = i; 835 break; // Found the lower bound 836 } 837 } 838 839 if (Float.isNaN(minFrequency)) { 840 // Lower bound was not found 841 return null; 842 } 843 844 // Find the upper frequency bound 845 for (int i = lowerFrequencyBoundIndex; i < mOutputAccelerationsGs.length; i++) { 846 if (mOutputAccelerationsGs[i] <= minOutputAcceleration) { 847 maxFrequency = MathUtils.constrainedMap( 848 mFrequenciesHz[i - 1], mFrequenciesHz[i], 849 mOutputAccelerationsGs[i - 1], mOutputAccelerationsGs[i], 850 minOutputAcceleration); 851 break; // Found the upper bound 852 } 853 } 854 855 if (Float.isNaN(maxFrequency)) { 856 // If the upper bound was not found, the specified output acceleration is 857 // achievable at all remaining frequencies. 858 maxFrequency = mMaxFrequencyHz; 859 } 860 861 return new Range<>(minFrequency, maxFrequency); 862 } 863 864 @Override writeToParcel(Parcel dest, int flags)865 public void writeToParcel(Parcel dest, int flags) { 866 dest.writeFloat(mResonantFrequencyHz); 867 dest.writeFloatArray(mFrequenciesHz); 868 dest.writeFloatArray(mOutputAccelerationsGs); 869 } 870 871 @Override describeContents()872 public int describeContents() { 873 return 0; 874 } 875 876 @Override equals(Object o)877 public boolean equals(Object o) { 878 if (this == o) { 879 return true; 880 } 881 if (!(o instanceof FrequencyProfile that)) { 882 return false; 883 } 884 return Float.compare(mResonantFrequencyHz, that.mResonantFrequencyHz) == 0 885 && Arrays.equals(mFrequenciesHz, that.mFrequenciesHz) 886 && Arrays.equals(mOutputAccelerationsGs, that.mOutputAccelerationsGs); 887 } 888 889 @Override hashCode()890 public int hashCode() { 891 return Objects.hash(mResonantFrequencyHz, Arrays.hashCode(mFrequenciesHz), 892 Arrays.hashCode(mOutputAccelerationsGs)); 893 } 894 895 @Override toString()896 public String toString() { 897 return "FrequencyProfile{" 898 + "mResonantFrequency=" + mResonantFrequencyHz 899 + ", mFrequenciesHz=" + Arrays.toString(mFrequenciesHz) 900 + ", mOutputAccelerationsGs=" + Arrays.toString(mOutputAccelerationsGs) 901 + ", mMinFrequencyHz=" + mMinFrequencyHz 902 + ", mMaxFrequencyHz=" + mMaxFrequencyHz 903 + ", mMaxOutputAccelerationGs=" + mMaxOutputAccelerationGs 904 + '}'; 905 } 906 907 @NonNull 908 public static final Creator<FrequencyProfile> CREATOR = 909 new Creator<FrequencyProfile>() { 910 @Override 911 public FrequencyProfile createFromParcel(Parcel in) { 912 return new FrequencyProfile(in); 913 } 914 915 @Override 916 public FrequencyProfile[] newArray(int size) { 917 return new FrequencyProfile[size]; 918 } 919 }; 920 deduplicateAndSortList(List<Pair<Float, Float>> list)921 private static void deduplicateAndSortList(List<Pair<Float, Float>> list) { 922 if (list == null || list.size() < 2) { 923 return; // Nothing to dedupe 924 } 925 926 list.sort(Comparator.comparing(pair -> pair.first)); 927 928 // Remove duplicates from the list 929 int writeIndex = 1; 930 for (int i = 1; i < list.size(); i++) { 931 Pair<Float, Float> currentPair = list.get(i); 932 Pair<Float, Float> previousPair = list.get(writeIndex - 1); 933 934 if (currentPair.first.compareTo(previousPair.first) != 0) { 935 list.set(writeIndex++, currentPair); 936 } 937 } 938 list.subList(writeIndex, list.size()).clear(); 939 } 940 extractFrequencyToOutputAccelerationData( float[] frequencies, float[] outputAccelerations)941 private static ArrayList<Pair<Float, Float>> extractFrequencyToOutputAccelerationData( 942 float[] frequencies, float[] outputAccelerations) { 943 944 if (frequencies == null || outputAccelerations == null 945 || frequencies.length == 0 946 || frequencies.length != outputAccelerations.length) { 947 return new ArrayList<>(); // Return empty list for invalid or mismatched data 948 } 949 950 ArrayList<Pair<Float, Float>> frequencyToOutputAccelerationList = new ArrayList<>( 951 frequencies.length); 952 for (int i = 0; i < frequencies.length; i++) { 953 frequencyToOutputAccelerationList.add( 954 new Pair<>(frequencies[i], outputAccelerations[i])); 955 } 956 957 return frequencyToOutputAccelerationList; 958 } 959 } 960 961 /** 962 * Describes the maximum relative output acceleration that can be achieved for each supported 963 * frequency in a specific vibrator. 964 * 965 * <p>This profile is defined by the following parameters: 966 * 967 * <ol> 968 * <li>{@code minFrequencyHz}, {@code resonantFrequencyHz} and {@code frequencyResolutionHz} 969 * provided by the vibrator in hertz. 970 * <li>{@code maxAmplitudes} a list of values in [0,1] provided by the vibrator, where 971 * {@code maxAmplitudes[i]} represents max supported amplitude at frequency 972 * {@code minFrequencyHz + frequencyResolutionHz * i}. 973 * <li>{@code maxFrequencyHz = minFrequencyHz 974 * + frequencyResolutionHz * (maxAmplitudes.length-1)} 975 * </ol> 976 * 977 * @hide 978 */ 979 public static final class FrequencyProfileLegacy implements Parcelable { 980 @Nullable 981 private final Range<Float> mFrequencyRangeHz; 982 private final float mMinFrequencyHz; 983 private final float mResonantFrequencyHz; 984 private final float mFrequencyResolutionHz; 985 private final float[] mMaxAmplitudes; 986 FrequencyProfileLegacy(Parcel in)987 FrequencyProfileLegacy(Parcel in) { 988 this(in.readFloat(), in.readFloat(), in.readFloat(), in.createFloatArray()); 989 } 990 991 /** 992 * Default constructor. 993 * 994 * @param resonantFrequencyHz The vibrator resonant frequency, in hertz. 995 * @param minFrequencyHz Minimum supported frequency, in hertz. 996 * @param frequencyResolutionHz The frequency resolution, in hertz, used by the max 997 * amplitude measurements. 998 * @param maxAmplitudes The max amplitude supported by each supported frequency, 999 * starting at minimum frequency with jumps of frequency 1000 * resolution. 1001 * @hide 1002 */ FrequencyProfileLegacy(float resonantFrequencyHz, float minFrequencyHz, float frequencyResolutionHz, float[] maxAmplitudes)1003 public FrequencyProfileLegacy(float resonantFrequencyHz, float minFrequencyHz, 1004 float frequencyResolutionHz, float[] maxAmplitudes) { 1005 mMinFrequencyHz = minFrequencyHz; 1006 mResonantFrequencyHz = resonantFrequencyHz; 1007 mFrequencyResolutionHz = frequencyResolutionHz; 1008 mMaxAmplitudes = new float[maxAmplitudes == null ? 0 : maxAmplitudes.length]; 1009 if (maxAmplitudes != null) { 1010 System.arraycopy(maxAmplitudes, 0, mMaxAmplitudes, 0, maxAmplitudes.length); 1011 } 1012 1013 // If any required field is undefined or has a bad value then this profile is invalid. 1014 boolean isValid = !Float.isNaN(resonantFrequencyHz) 1015 && (resonantFrequencyHz > 0) 1016 && !Float.isNaN(minFrequencyHz) 1017 && (minFrequencyHz > 0) 1018 && !Float.isNaN(frequencyResolutionHz) 1019 && (frequencyResolutionHz > 0) 1020 && (mMaxAmplitudes.length > 0); 1021 1022 // If any max amplitude is outside the allowed range then this profile is invalid. 1023 for (int i = 0; i < mMaxAmplitudes.length; i++) { 1024 isValid &= (mMaxAmplitudes[i] >= 0) && (mMaxAmplitudes[i] <= 1); 1025 } 1026 1027 float maxFrequencyHz = isValid 1028 ? minFrequencyHz + frequencyResolutionHz * (mMaxAmplitudes.length - 1) 1029 : Float.NaN; 1030 1031 // If the constraint min < resonant < max is not met then it is invalid. 1032 isValid &= !Float.isNaN(maxFrequencyHz) 1033 && (resonantFrequencyHz >= minFrequencyHz) 1034 && (resonantFrequencyHz <= maxFrequencyHz) 1035 && (minFrequencyHz < maxFrequencyHz); 1036 1037 mFrequencyRangeHz = isValid ? Range.create(minFrequencyHz, maxFrequencyHz) : null; 1038 } 1039 1040 /** Returns true if the supported frequency range is empty. */ isEmpty()1041 public boolean isEmpty() { 1042 return mFrequencyRangeHz == null; 1043 } 1044 1045 /** Returns the supported frequency range, in hertz. */ 1046 @Nullable getFrequencyRangeHz()1047 public Range<Float> getFrequencyRangeHz() { 1048 return mFrequencyRangeHz; 1049 } 1050 1051 /** 1052 * Returns the maximum relative amplitude the vibrator can reach while playing at the 1053 * given frequency. 1054 * 1055 * @param frequencyHz frequency, in hertz, for query. 1056 * @return A value in [0,1] representing the max relative amplitude supported at the given 1057 * frequency. This will return 0 if the frequency is outside the supported range, or if the 1058 * supported frequency range is empty. 1059 */ getMaxAmplitude(float frequencyHz)1060 public float getMaxAmplitude(float frequencyHz) { 1061 if (isEmpty() || Float.isNaN(frequencyHz) || !mFrequencyRangeHz.contains(frequencyHz)) { 1062 // Unsupported frequency requested, vibrator cannot play at this frequency. 1063 return 0; 1064 } 1065 1066 // Subtract minFrequencyHz to simplify offset calculations. 1067 float mappingFreq = frequencyHz - mMinFrequencyHz; 1068 1069 // Find the bucket to interpolate within. 1070 // Any calculated index should be safe, except exactly equal to max amplitude can be 1071 // one step too high, so constrain it to guarantee safety. 1072 int startIdx = MathUtils.constrain( 1073 /* amount= */ (int) Math.floor(mappingFreq / mFrequencyResolutionHz), 1074 /* low= */ 0, /* high= */ mMaxAmplitudes.length - 1); 1075 int nextIdx = MathUtils.constrain( 1076 /* amount= */ startIdx + 1, 1077 /* low= */ 0, /* high= */ mMaxAmplitudes.length - 1); 1078 1079 // Linearly interpolate the amplitudes based on the frequency range of the bucket. 1080 return MathUtils.constrainedMap( 1081 mMaxAmplitudes[startIdx], mMaxAmplitudes[nextIdx], 1082 startIdx * mFrequencyResolutionHz, nextIdx * mFrequencyResolutionHz, 1083 mappingFreq); 1084 } 1085 1086 /** Returns the raw list of maximum relative output accelerations from the vibrator. */ 1087 @NonNull getMaxAmplitudes()1088 public float[] getMaxAmplitudes() { 1089 return Arrays.copyOf(mMaxAmplitudes, mMaxAmplitudes.length); 1090 } 1091 1092 /** Returns the raw frequency resolution used for max amplitude measurements, in hertz. */ getFrequencyResolutionHz()1093 public float getFrequencyResolutionHz() { 1094 return mFrequencyResolutionHz; 1095 } 1096 1097 @Override writeToParcel(Parcel dest, int flags)1098 public void writeToParcel(Parcel dest, int flags) { 1099 dest.writeFloat(mResonantFrequencyHz); 1100 dest.writeFloat(mMinFrequencyHz); 1101 dest.writeFloat(mFrequencyResolutionHz); 1102 dest.writeFloatArray(mMaxAmplitudes); 1103 } 1104 1105 @Override describeContents()1106 public int describeContents() { 1107 return 0; 1108 } 1109 1110 @Override equals(Object o)1111 public boolean equals(Object o) { 1112 if (this == o) { 1113 return true; 1114 } 1115 if (!(o instanceof FrequencyProfileLegacy)) { 1116 return false; 1117 } 1118 FrequencyProfileLegacy that = (FrequencyProfileLegacy) o; 1119 return Float.compare(mMinFrequencyHz, that.mMinFrequencyHz) == 0 1120 && Float.compare(mResonantFrequencyHz, that.mResonantFrequencyHz) == 0 1121 && Float.compare(mFrequencyResolutionHz, that.mFrequencyResolutionHz) == 0 1122 && Arrays.equals(mMaxAmplitudes, that.mMaxAmplitudes); 1123 } 1124 1125 @Override hashCode()1126 public int hashCode() { 1127 int hashCode = Objects.hash(mMinFrequencyHz, mFrequencyResolutionHz, 1128 mFrequencyResolutionHz); 1129 hashCode = 31 * hashCode + Arrays.hashCode(mMaxAmplitudes); 1130 return hashCode; 1131 } 1132 1133 @Override toString()1134 public String toString() { 1135 return "FrequencyProfileLegacy{" 1136 + "mFrequencyRange=" + mFrequencyRangeHz 1137 + ", mMinFrequency=" + mMinFrequencyHz 1138 + ", mResonantFrequency=" + mResonantFrequencyHz 1139 + ", mFrequencyResolution=" + mFrequencyResolutionHz 1140 + ", mMaxAmplitudes count=" + mMaxAmplitudes.length 1141 + '}'; 1142 } 1143 1144 @NonNull 1145 public static final Creator<FrequencyProfileLegacy> CREATOR = 1146 new Creator<FrequencyProfileLegacy>() { 1147 @Override 1148 public FrequencyProfileLegacy createFromParcel(Parcel in) { 1149 return new FrequencyProfileLegacy(in); 1150 } 1151 1152 @Override 1153 public FrequencyProfileLegacy[] newArray(int size) { 1154 return new FrequencyProfileLegacy[size]; 1155 } 1156 }; 1157 } 1158 1159 /** @hide */ 1160 public static final class Builder { 1161 private final int mId; 1162 private long mCapabilities; 1163 private SparseBooleanArray mSupportedEffects; 1164 private SparseBooleanArray mSupportedBraking; 1165 private SparseIntArray mSupportedPrimitives = new SparseIntArray(); 1166 private int mPrimitiveDelayMax; 1167 private int mCompositionSizeMax; 1168 private int mPwlePrimitiveDurationMax; 1169 private int mPwleSizeMax; 1170 private float mQFactor = Float.NaN; 1171 private FrequencyProfileLegacy mFrequencyProfileLegacy = 1172 new FrequencyProfileLegacy(Float.NaN, Float.NaN, Float.NaN, null); 1173 private FrequencyProfile mFrequencyProfile = new FrequencyProfile(Float.NaN, null, 1174 null); 1175 private int mMaxEnvelopeEffectSize; 1176 private int mMinEnvelopeEffectControlPointDurationMillis; 1177 private int mMaxEnvelopeEffectControlPointDurationMillis; 1178 1179 /** A builder class for a {@link VibratorInfo}. */ Builder(int id)1180 public Builder(int id) { 1181 mId = id; 1182 } 1183 1184 /** Configure the vibrator capabilities with a combination of IVibrator.CAP_* values. */ 1185 @NonNull setCapabilities(long capabilities)1186 public Builder setCapabilities(long capabilities) { 1187 mCapabilities = capabilities; 1188 return this; 1189 } 1190 1191 /** Configure the effects supported with {@link android.hardware.vibrator.Effect} values. */ 1192 @NonNull setSupportedEffects(int... supportedEffects)1193 public Builder setSupportedEffects(int... supportedEffects) { 1194 mSupportedEffects = toSparseBooleanArray(supportedEffects); 1195 return this; 1196 } 1197 1198 /** Configure braking supported with {@link android.hardware.vibrator.Braking} values. */ 1199 @NonNull setSupportedBraking(int... supportedBraking)1200 public Builder setSupportedBraking(int... supportedBraking) { 1201 mSupportedBraking = toSparseBooleanArray(supportedBraking); 1202 return this; 1203 } 1204 1205 /** Configure maximum duration, in milliseconds, of a PWLE primitive. */ 1206 @NonNull setPwlePrimitiveDurationMax(int pwlePrimitiveDurationMax)1207 public Builder setPwlePrimitiveDurationMax(int pwlePrimitiveDurationMax) { 1208 mPwlePrimitiveDurationMax = pwlePrimitiveDurationMax; 1209 return this; 1210 } 1211 1212 /** Configure maximum number of primitives supported in a single PWLE composed effect. */ 1213 @NonNull setPwleSizeMax(int pwleSizeMax)1214 public Builder setPwleSizeMax(int pwleSizeMax) { 1215 mPwleSizeMax = pwleSizeMax; 1216 return this; 1217 } 1218 1219 /** Configure the duration of a {@link android.hardware.vibrator.CompositePrimitive}. */ 1220 @NonNull setSupportedPrimitive(int primitiveId, int duration)1221 public Builder setSupportedPrimitive(int primitiveId, int duration) { 1222 mSupportedPrimitives.put(primitiveId, duration); 1223 return this; 1224 } 1225 1226 /** Configure maximum delay, in milliseconds, supported in a composed effect primitive. */ 1227 @NonNull setPrimitiveDelayMax(int primitiveDelayMax)1228 public Builder setPrimitiveDelayMax(int primitiveDelayMax) { 1229 mPrimitiveDelayMax = primitiveDelayMax; 1230 return this; 1231 } 1232 1233 /** Configure maximum number of primitives supported in a single composed effect. */ 1234 @NonNull setCompositionSizeMax(int compositionSizeMax)1235 public Builder setCompositionSizeMax(int compositionSizeMax) { 1236 mCompositionSizeMax = compositionSizeMax; 1237 return this; 1238 } 1239 1240 /** Configure the vibrator quality factor. */ 1241 @NonNull setQFactor(float qFactor)1242 public Builder setQFactor(float qFactor) { 1243 mQFactor = qFactor; 1244 return this; 1245 } 1246 1247 /** Configure the vibrator frequency information like resonant frequency and bandwidth. */ 1248 @NonNull setFrequencyProfileLegacy(@onNull FrequencyProfileLegacy frequencyProfile)1249 public Builder setFrequencyProfileLegacy(@NonNull FrequencyProfileLegacy frequencyProfile) { 1250 mFrequencyProfileLegacy = frequencyProfile; 1251 return this; 1252 } 1253 1254 /** 1255 * Configure the vibrator frequency information like resonant frequency and frequency to 1256 * output acceleration data. 1257 */ 1258 @NonNull setFrequencyProfile(@onNull FrequencyProfile frequencyProfile)1259 public Builder setFrequencyProfile(@NonNull FrequencyProfile frequencyProfile) { 1260 mFrequencyProfile = frequencyProfile; 1261 return this; 1262 } 1263 1264 /** 1265 * Configure the maximum number of control points supported for envelope effects on this 1266 * device. 1267 */ 1268 @NonNull setMaxEnvelopeEffectSize(int maxEnvelopeEffectSize)1269 public Builder setMaxEnvelopeEffectSize(int maxEnvelopeEffectSize) { 1270 mMaxEnvelopeEffectSize = maxEnvelopeEffectSize; 1271 return this; 1272 } 1273 1274 /** 1275 * Configure the minimum supported duration for any individual segment within an 1276 * envelope effect in milliseconds. 1277 */ 1278 @NonNull setMinEnvelopeEffectControlPointDurationMillis( int minEnvelopeEffectControlPointDuration)1279 public Builder setMinEnvelopeEffectControlPointDurationMillis( 1280 int minEnvelopeEffectControlPointDuration) { 1281 mMinEnvelopeEffectControlPointDurationMillis = minEnvelopeEffectControlPointDuration; 1282 return this; 1283 } 1284 1285 /** 1286 * Configure the maximum supported duration for any individual segment within an 1287 * envelope effect in milliseconds. 1288 */ 1289 @NonNull setMaxEnvelopeEffectControlPointDurationMillis( int maxEnvelopeEffectControlPointDuration)1290 public Builder setMaxEnvelopeEffectControlPointDurationMillis( 1291 int maxEnvelopeEffectControlPointDuration) { 1292 mMaxEnvelopeEffectControlPointDurationMillis = maxEnvelopeEffectControlPointDuration; 1293 return this; 1294 } 1295 1296 /** Build the configured {@link VibratorInfo}. */ 1297 @NonNull build()1298 public VibratorInfo build() { 1299 return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedBraking, 1300 mSupportedPrimitives, mPrimitiveDelayMax, mCompositionSizeMax, 1301 mPwlePrimitiveDurationMax, mPwleSizeMax, mQFactor, mFrequencyProfileLegacy, 1302 mFrequencyProfile, mMaxEnvelopeEffectSize, 1303 mMinEnvelopeEffectControlPointDurationMillis, 1304 mMaxEnvelopeEffectControlPointDurationMillis); 1305 } 1306 1307 /** 1308 * Create a {@link SparseBooleanArray} from given {@code supportedKeys} where each key is 1309 * mapped 1310 * to {@code true}. 1311 */ 1312 @Nullable toSparseBooleanArray(int[] supportedKeys)1313 private static SparseBooleanArray toSparseBooleanArray(int[] supportedKeys) { 1314 if (supportedKeys == null) { 1315 return null; 1316 } 1317 SparseBooleanArray array = new SparseBooleanArray(); 1318 for (int key : supportedKeys) { 1319 array.put(key, true); 1320 } 1321 return array; 1322 } 1323 } 1324 1325 @NonNull 1326 public static final Creator<VibratorInfo> CREATOR = 1327 new Creator<VibratorInfo>() { 1328 @Override 1329 public VibratorInfo createFromParcel(Parcel in) { 1330 return new VibratorInfo(in); 1331 } 1332 1333 @Override 1334 public VibratorInfo[] newArray(int size) { 1335 return new VibratorInfo[size]; 1336 } 1337 }; 1338 } 1339