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