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