1 /* 2 * Copyright (C) 2019 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.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.TestApi; 23 import android.media.AudioAttributes; 24 import android.os.vibrator.PrebakedSegment; 25 import android.os.vibrator.VibrationEffectSegment; 26 import android.util.Slog; 27 28 import java.lang.annotation.Retention; 29 import java.lang.annotation.RetentionPolicy; 30 import java.util.Objects; 31 32 /** 33 * A class to encapsulate a collection of attributes describing information about a vibration 34 */ 35 public final class VibrationAttributes implements Parcelable { 36 private static final String TAG = "VibrationAttributes"; 37 38 /** 39 * @hide 40 */ 41 @IntDef(prefix = { "USAGE_CLASS_" }, value = { 42 USAGE_CLASS_UNKNOWN, 43 USAGE_CLASS_ALARM, 44 USAGE_CLASS_FEEDBACK, 45 }) 46 @Retention(RetentionPolicy.SOURCE) 47 public @interface UsageClass{} 48 49 /** 50 * @hide 51 */ 52 @IntDef(prefix = { "USAGE_" }, value = { 53 USAGE_UNKNOWN, 54 USAGE_ALARM, 55 USAGE_RINGTONE, 56 USAGE_NOTIFICATION, 57 USAGE_COMMUNICATION_REQUEST, 58 USAGE_TOUCH, 59 USAGE_PHYSICAL_EMULATION, 60 USAGE_HARDWARE_FEEDBACK, 61 }) 62 @Retention(RetentionPolicy.SOURCE) 63 public @interface Usage{} 64 65 /** 66 * Vibration usage filter value to match all usages. 67 * @hide 68 */ 69 public static final int USAGE_FILTER_MATCH_ALL = -1; 70 /** 71 * Vibration usage class value to use when the vibration usage class is unknown. 72 */ 73 public static final int USAGE_CLASS_UNKNOWN = 0x0; 74 /** 75 * Vibration usage class value to use when the vibration is initiated to catch user's 76 * attention, such as alarm, ringtone, and notification vibrations. 77 */ 78 public static final int USAGE_CLASS_ALARM = 0x1; 79 /** 80 * Vibration usage class value to use when the vibration is initiated as a response to user's 81 * actions, such as emulation of physical effects, and texting feedback vibration. 82 */ 83 public static final int USAGE_CLASS_FEEDBACK = 0x2; 84 85 /** 86 * Mask for vibration usage class value. 87 */ 88 public static final int USAGE_CLASS_MASK = 0xF; 89 90 /** 91 * Usage value to use when usage is unknown. 92 */ 93 public static final int USAGE_UNKNOWN = 0x0 | USAGE_CLASS_UNKNOWN; 94 /** 95 * Usage value to use for alarm vibrations. 96 */ 97 public static final int USAGE_ALARM = 0x10 | USAGE_CLASS_ALARM; 98 /** 99 * Usage value to use for ringtone vibrations. 100 */ 101 public static final int USAGE_RINGTONE = 0x20 | USAGE_CLASS_ALARM; 102 /** 103 * Usage value to use for notification vibrations. 104 */ 105 public static final int USAGE_NOTIFICATION = 0x30 | USAGE_CLASS_ALARM; 106 /** 107 * Usage value to use for vibrations which mean a request to enter/end a 108 * communication, such as a VoIP communication or video-conference. 109 */ 110 public static final int USAGE_COMMUNICATION_REQUEST = 0x40 | USAGE_CLASS_ALARM; 111 /** 112 * Usage value to use for touch vibrations. 113 */ 114 public static final int USAGE_TOUCH = 0x10 | USAGE_CLASS_FEEDBACK; 115 /** 116 * Usage value to use for vibrations which emulate physical effects, such as edge squeeze. 117 */ 118 public static final int USAGE_PHYSICAL_EMULATION = 0x20 | USAGE_CLASS_FEEDBACK; 119 /** 120 * Usage value to use for vibrations which provide a feedback for hardware interaction, 121 * such as a fingerprint sensor. 122 */ 123 public static final int USAGE_HARDWARE_FEEDBACK = 0x30 | USAGE_CLASS_FEEDBACK; 124 125 /** 126 * @hide 127 */ 128 @IntDef(prefix = { "FLAG_" }, value = { 129 FLAG_BYPASS_INTERRUPTION_POLICY, 130 }) 131 @Retention(RetentionPolicy.SOURCE) 132 public @interface Flag{} 133 134 /** 135 * Flag requesting vibration effect to be played even under limited interruptions. 136 */ 137 public static final int FLAG_BYPASS_INTERRUPTION_POLICY = 0x1; 138 139 /** 140 * All flags supported by vibrator service, update it when adding new flag. 141 * @hide 142 */ 143 public static final int FLAG_ALL_SUPPORTED = FLAG_BYPASS_INTERRUPTION_POLICY; 144 145 // If a vibration is playing for longer than 5s, it's probably not haptic feedback 146 private static final long MAX_HAPTIC_FEEDBACK_DURATION = 5000; 147 148 private final int mUsage; 149 private final int mFlags; 150 private final int mOriginalAudioUsage; 151 VibrationAttributes(int usage, int audioUsage, int flags)152 private VibrationAttributes(int usage, int audioUsage, int flags) { 153 mUsage = usage; 154 mOriginalAudioUsage = audioUsage; 155 mFlags = flags & FLAG_ALL_SUPPORTED; 156 } 157 158 /** 159 * Return the vibration usage class. 160 * @return USAGE_CLASS_ALARM, USAGE_CLASS_FEEDBACK or USAGE_CLASS_UNKNOWN 161 */ getUsageClass()162 public int getUsageClass() { 163 return mUsage & USAGE_CLASS_MASK; 164 } 165 166 /** 167 * Return the vibration usage. 168 * @return one of the values that can be set in {@link Builder#setUsage(int)} 169 */ getUsage()170 public int getUsage() { 171 return mUsage; 172 } 173 174 /** 175 * Return the flags. 176 * @return a combined mask of all flags 177 */ getFlags()178 public int getFlags() { 179 return mFlags; 180 } 181 182 /** 183 * Check whether a flag is set 184 * @return true if a flag is set and false otherwise 185 */ isFlagSet(int flag)186 public boolean isFlagSet(int flag) { 187 return (mFlags & flag) > 0; 188 } 189 190 /** 191 * Return {@link AudioAttributes} usage equivalent to {@link #getUsage()}. 192 * @return one of {@link AudioAttributes#SDK_USAGES} that represents {@link #getUsage()} 193 * @hide 194 */ 195 @TestApi getAudioUsage()196 public int getAudioUsage() { 197 if (mOriginalAudioUsage != AudioAttributes.USAGE_UNKNOWN) { 198 // Return same audio usage set in the Builder. 199 return mOriginalAudioUsage; 200 } 201 // Return correct audio usage based on the vibration usage set in the Builder. 202 switch (mUsage) { 203 case USAGE_NOTIFICATION: 204 return AudioAttributes.USAGE_NOTIFICATION; 205 case USAGE_COMMUNICATION_REQUEST: 206 return AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST; 207 case USAGE_RINGTONE: 208 return AudioAttributes.USAGE_NOTIFICATION_RINGTONE; 209 case USAGE_TOUCH: 210 return AudioAttributes.USAGE_ASSISTANCE_SONIFICATION; 211 case USAGE_ALARM: 212 return AudioAttributes.USAGE_ALARM; 213 default: 214 return AudioAttributes.USAGE_UNKNOWN; 215 } 216 } 217 218 @Override describeContents()219 public int describeContents() { 220 return 0; 221 } 222 223 @Override writeToParcel(@onNull Parcel dest, int flags)224 public void writeToParcel(@NonNull Parcel dest, int flags) { 225 dest.writeInt(mUsage); 226 dest.writeInt(mOriginalAudioUsage); 227 dest.writeInt(mFlags); 228 } 229 VibrationAttributes(Parcel src)230 private VibrationAttributes(Parcel src) { 231 mUsage = src.readInt(); 232 mOriginalAudioUsage = src.readInt(); 233 mFlags = src.readInt(); 234 } 235 236 public static final @NonNull Parcelable.Creator<VibrationAttributes> 237 CREATOR = new Parcelable.Creator<VibrationAttributes>() { 238 public VibrationAttributes createFromParcel(Parcel p) { 239 return new VibrationAttributes(p); 240 } 241 public VibrationAttributes[] newArray(int size) { 242 return new VibrationAttributes[size]; 243 } 244 }; 245 246 @Override equals(@ullable Object o)247 public boolean equals(@Nullable Object o) { 248 if (this == o) { 249 return true; 250 } 251 if (o == null || getClass() != o.getClass()) { 252 return false; 253 } 254 VibrationAttributes rhs = (VibrationAttributes) o; 255 return mUsage == rhs.mUsage && mOriginalAudioUsage == rhs.mOriginalAudioUsage 256 && mFlags == rhs.mFlags; 257 } 258 259 @Override hashCode()260 public int hashCode() { 261 return Objects.hash(mUsage, mOriginalAudioUsage, mFlags); 262 } 263 264 @Override toString()265 public String toString() { 266 return "VibrationAttributes:" 267 + " Usage=" + usageToString() 268 + " Audio Usage= " + AudioAttributes.usageToString(mOriginalAudioUsage) 269 + " Flags=" + mFlags; 270 } 271 272 /** @hide */ usageToString()273 public String usageToString() { 274 return usageToString(mUsage); 275 } 276 277 /** @hide */ usageToString(int usage)278 public static String usageToString(int usage) { 279 switch (usage) { 280 case USAGE_UNKNOWN: 281 return "UNKNOWN"; 282 case USAGE_ALARM: 283 return "ALARM"; 284 case USAGE_RINGTONE: 285 return "RIGNTONE"; 286 case USAGE_NOTIFICATION: 287 return "NOTIFICATION"; 288 case USAGE_COMMUNICATION_REQUEST: 289 return "COMMUNICATION_REQUEST"; 290 case USAGE_TOUCH: 291 return "TOUCH"; 292 case USAGE_PHYSICAL_EMULATION: 293 return "PHYSICAL_EMULATION"; 294 case USAGE_HARDWARE_FEEDBACK: 295 return "HARDWARE_FEEDBACK"; 296 default: 297 return "unknown usage " + usage; 298 } 299 } 300 301 /** 302 * Builder class for {@link VibrationAttributes} objects. 303 * By default, all information is set to UNKNOWN. 304 */ 305 public static final class Builder { 306 private int mUsage = USAGE_UNKNOWN; 307 private int mOriginalAudioUsage = AudioAttributes.USAGE_UNKNOWN; 308 private int mFlags = 0x0; 309 310 /** 311 * Constructs a new Builder with the defaults. 312 */ Builder()313 public Builder() { 314 } 315 316 /** 317 * Constructs a new Builder from a given VibrationAttributes. 318 */ Builder(@ullable VibrationAttributes vib)319 public Builder(@Nullable VibrationAttributes vib) { 320 if (vib != null) { 321 mUsage = vib.mUsage; 322 mOriginalAudioUsage = vib.mOriginalAudioUsage; 323 mFlags = vib.mFlags; 324 } 325 } 326 327 /** 328 * Constructs a new Builder from AudioAttributes. 329 * @hide 330 */ 331 @TestApi Builder(@onNull AudioAttributes audio, @Nullable VibrationEffect effect)332 public Builder(@NonNull AudioAttributes audio, @Nullable VibrationEffect effect) { 333 setUsage(audio); 334 setFlags(audio); 335 applyHapticFeedbackHeuristics(effect); 336 } 337 applyHapticFeedbackHeuristics(@ullable VibrationEffect effect)338 private void applyHapticFeedbackHeuristics(@Nullable VibrationEffect effect) { 339 if (effect != null) { 340 PrebakedSegment prebaked = extractPrebakedSegment(effect); 341 if (mUsage == USAGE_UNKNOWN && prebaked != null) { 342 switch (prebaked.getEffectId()) { 343 case VibrationEffect.EFFECT_CLICK: 344 case VibrationEffect.EFFECT_DOUBLE_CLICK: 345 case VibrationEffect.EFFECT_HEAVY_CLICK: 346 case VibrationEffect.EFFECT_TEXTURE_TICK: 347 case VibrationEffect.EFFECT_TICK: 348 case VibrationEffect.EFFECT_POP: 349 case VibrationEffect.EFFECT_THUD: 350 mUsage = USAGE_TOUCH; 351 break; 352 default: 353 Slog.w(TAG, "Unknown prebaked vibration effect, assuming it isn't " 354 + "haptic feedback"); 355 } 356 } 357 final long duration = effect.getDuration(); 358 if (mUsage == USAGE_UNKNOWN && duration >= 0 359 && duration < MAX_HAPTIC_FEEDBACK_DURATION) { 360 mUsage = USAGE_TOUCH; 361 } 362 } 363 } 364 365 @Nullable extractPrebakedSegment(VibrationEffect effect)366 private PrebakedSegment extractPrebakedSegment(VibrationEffect effect) { 367 if (effect instanceof VibrationEffect.Composed) { 368 VibrationEffect.Composed composed = (VibrationEffect.Composed) effect; 369 if (composed.getSegments().size() == 1) { 370 VibrationEffectSegment segment = composed.getSegments().get(0); 371 if (segment instanceof PrebakedSegment) { 372 return (PrebakedSegment) segment; 373 } 374 } 375 } 376 return null; 377 } 378 setUsage(@onNull AudioAttributes audio)379 private void setUsage(@NonNull AudioAttributes audio) { 380 mOriginalAudioUsage = audio.getUsage(); 381 switch (audio.getUsage()) { 382 case AudioAttributes.USAGE_NOTIFICATION: 383 case AudioAttributes.USAGE_NOTIFICATION_EVENT: 384 case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED: 385 case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT: 386 mUsage = USAGE_NOTIFICATION; 387 break; 388 case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST: 389 case AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY: 390 mUsage = USAGE_COMMUNICATION_REQUEST; 391 break; 392 case AudioAttributes.USAGE_NOTIFICATION_RINGTONE: 393 mUsage = USAGE_RINGTONE; 394 break; 395 case AudioAttributes.USAGE_ASSISTANCE_SONIFICATION: 396 mUsage = USAGE_TOUCH; 397 break; 398 case AudioAttributes.USAGE_ALARM: 399 mUsage = USAGE_ALARM; 400 break; 401 default: 402 mUsage = USAGE_UNKNOWN; 403 } 404 } 405 setFlags(@onNull AudioAttributes audio)406 private void setFlags(@NonNull AudioAttributes audio) { 407 if ((audio.getAllFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0) { 408 mFlags |= FLAG_BYPASS_INTERRUPTION_POLICY; 409 } 410 } 411 412 /** 413 * Combines all of the attributes that have been set and returns a new 414 * {@link VibrationAttributes} object. 415 * @return a new {@link VibrationAttributes} object 416 */ build()417 public @NonNull VibrationAttributes build() { 418 VibrationAttributes ans = new VibrationAttributes(mUsage, mOriginalAudioUsage, mFlags); 419 return ans; 420 } 421 422 /** 423 * Sets the attribute describing the type of corresponding vibration. 424 * @param usage one of {@link VibrationAttributes#USAGE_ALARM}, 425 * {@link VibrationAttributes#USAGE_RINGTONE}, 426 * {@link VibrationAttributes#USAGE_NOTIFICATION}, 427 * {@link VibrationAttributes#USAGE_COMMUNICATION_REQUEST}, 428 * {@link VibrationAttributes#USAGE_TOUCH}, 429 * {@link VibrationAttributes#USAGE_PHYSICAL_EMULATION}, 430 * {@link VibrationAttributes#USAGE_HARDWARE_FEEDBACK}. 431 * @return the same Builder instance. 432 */ setUsage(int usage)433 public @NonNull Builder setUsage(int usage) { 434 mOriginalAudioUsage = AudioAttributes.USAGE_UNKNOWN; 435 mUsage = usage; 436 return this; 437 } 438 439 /** 440 * Set flags 441 * @param flags combination of flags to be set. 442 * @param mask Bit range that should be changed. 443 * @return the same Builder instance. 444 */ setFlags(int flags, int mask)445 public @NonNull Builder setFlags(int flags, int mask) { 446 mask &= FLAG_ALL_SUPPORTED; 447 mFlags = (mFlags & ~mask) | (flags & mask); 448 return this; 449 } 450 } 451 } 452 453