• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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