1 /*
2  * Copyright (C) 2022 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.vibrator;
18 
19 import static android.os.VibrationAttributes.USAGE_ACCESSIBILITY;
20 import static android.os.VibrationAttributes.USAGE_ALARM;
21 import static android.os.VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
22 import static android.os.VibrationAttributes.USAGE_HARDWARE_FEEDBACK;
23 import static android.os.VibrationAttributes.USAGE_MEDIA;
24 import static android.os.VibrationAttributes.USAGE_NOTIFICATION;
25 import static android.os.VibrationAttributes.USAGE_PHYSICAL_EMULATION;
26 import static android.os.VibrationAttributes.USAGE_RINGTONE;
27 import static android.os.VibrationAttributes.USAGE_TOUCH;
28 import static android.os.VibrationAttributes.USAGE_UNKNOWN;
29 
30 import android.annotation.Nullable;
31 import android.content.res.Resources;
32 import android.os.VibrationAttributes;
33 import android.os.Vibrator;
34 import android.os.Vibrator.VibrationIntensity;
35 import android.util.IndentingPrintWriter;
36 
37 import java.io.PrintWriter;
38 import java.util.Arrays;
39 
40 /**
41  * List of device-specific internal vibration configuration loaded from platform config.xml.
42  *
43  * <p>This should not be public, but some individual values are exposed by {@link Vibrator} by
44  * hidden methods, made available to Settings, SysUI and other platform client code. They can also
45  * be individually exposed with the necessary permissions by the {@link Vibrator} service.
46  *
47  * @hide
48  */
49 public class VibrationConfig {
50 
51     // TODO(b/191150049): move these to vibrator static config file
52     private final float mHapticChannelMaxVibrationAmplitude;
53     private final int mRampStepDurationMs;
54     private final int mRampDownDurationMs;
55     private final int mRequestVibrationParamsTimeoutMs;
56     private final int[] mRequestVibrationParamsForUsages;
57 
58     private final boolean mIgnoreVibrationsOnWirelessCharger;
59 
60     @VibrationIntensity
61     private final int mDefaultAlarmVibrationIntensity;
62     @VibrationIntensity
63     private final int mDefaultHapticFeedbackIntensity;
64     @VibrationIntensity
65     private final int mDefaultMediaVibrationIntensity;
66     @VibrationIntensity
67     private final int mDefaultNotificationVibrationIntensity;
68     @VibrationIntensity
69     private final int mDefaultRingVibrationIntensity;
70 
71     private final boolean mDefaultKeyboardVibrationEnabled;
72 
73     private final boolean mHasFixedKeyboardAmplitude;
74 
75     /** @hide */
VibrationConfig(@ullable Resources resources)76     public VibrationConfig(@Nullable Resources resources) {
77         mHapticChannelMaxVibrationAmplitude = loadFloat(resources,
78                 com.android.internal.R.dimen.config_hapticChannelMaxVibrationAmplitude, 0);
79         mRampDownDurationMs = loadInteger(resources,
80                 com.android.internal.R.integer.config_vibrationWaveformRampDownDuration, 0);
81         mRampStepDurationMs = loadInteger(resources,
82                 com.android.internal.R.integer.config_vibrationWaveformRampStepDuration, 0);
83         mRequestVibrationParamsTimeoutMs = loadInteger(resources,
84                 com.android.internal.R.integer.config_requestVibrationParamsTimeout, 0);
85         mRequestVibrationParamsForUsages = loadIntArray(resources,
86                 com.android.internal.R.array.config_requestVibrationParamsForUsages);
87 
88         mIgnoreVibrationsOnWirelessCharger = loadBoolean(resources,
89                 com.android.internal.R.bool.config_ignoreVibrationsOnWirelessCharger, false);
90         mDefaultKeyboardVibrationEnabled = loadBoolean(resources,
91                 com.android.internal.R.bool.config_defaultKeyboardVibrationEnabled, true);
92         mHasFixedKeyboardAmplitude = loadFloat(resources,
93                 com.android.internal.R.dimen.config_keyboardHapticFeedbackFixedAmplitude, -1) > 0;
94 
95         mDefaultAlarmVibrationIntensity = loadDefaultIntensity(resources,
96                 com.android.internal.R.integer.config_defaultAlarmVibrationIntensity);
97         mDefaultHapticFeedbackIntensity = loadDefaultIntensity(resources,
98                 com.android.internal.R.integer.config_defaultHapticFeedbackIntensity);
99         mDefaultMediaVibrationIntensity = loadDefaultIntensity(resources,
100                 com.android.internal.R.integer.config_defaultMediaVibrationIntensity);
101         mDefaultNotificationVibrationIntensity = loadDefaultIntensity(resources,
102                 com.android.internal.R.integer.config_defaultNotificationVibrationIntensity);
103         mDefaultRingVibrationIntensity = loadDefaultIntensity(resources,
104                 com.android.internal.R.integer.config_defaultRingVibrationIntensity);
105     }
106 
107     @VibrationIntensity
loadDefaultIntensity(@ullable Resources res, int resId)108     private static int loadDefaultIntensity(@Nullable Resources res, int resId) {
109         int defaultIntensity = Vibrator.VIBRATION_INTENSITY_MEDIUM;
110         int value = loadInteger(res, resId, defaultIntensity);
111         if (value < Vibrator.VIBRATION_INTENSITY_OFF || value > Vibrator.VIBRATION_INTENSITY_HIGH) {
112             return defaultIntensity;
113         }
114         return value;
115     }
116 
loadFloat(@ullable Resources res, int resId, float defaultValue)117     private static float loadFloat(@Nullable Resources res, int resId, float defaultValue) {
118         return res != null ? res.getFloat(resId) : defaultValue;
119     }
120 
loadInteger(@ullable Resources res, int resId, int defaultValue)121     private static int loadInteger(@Nullable Resources res, int resId, int defaultValue) {
122         return res != null ? res.getInteger(resId) : defaultValue;
123     }
124 
loadBoolean(@ullable Resources res, int resId, boolean defaultValue)125     private static boolean loadBoolean(@Nullable Resources res, int resId, boolean defaultValue) {
126         return res != null ? res.getBoolean(resId) : defaultValue;
127     }
128 
loadIntArray(@ullable Resources res, int resId)129     private static int[] loadIntArray(@Nullable Resources res, int resId) {
130         return res != null ? res.getIntArray(resId) : new int[0];
131     }
132 
133     /**
134      * Return the maximum amplitude the vibrator can play using the audio haptic channels.
135      *
136      * @return a positive value representing the maximum absolute value the device can play signals
137      * from audio haptic channels, or {@link Float#NaN NaN} if it's unknown.
138      */
getHapticChannelMaximumAmplitude()139     public float getHapticChannelMaximumAmplitude() {
140         if (mHapticChannelMaxVibrationAmplitude <= 0) {
141             return Float.NaN;
142         }
143         return mHapticChannelMaxVibrationAmplitude;
144     }
145 
146     /**
147      * The duration, in milliseconds, that should be applied to the ramp to turn off the vibrator
148      * when a vibration is cancelled or finished at non-zero amplitude.
149      */
getRampDownDurationMs()150     public int getRampDownDurationMs() {
151         if (mRampDownDurationMs < 0) {
152             return 0;
153         }
154         return mRampDownDurationMs;
155     }
156 
157     /**
158      * The duration, in milliseconds, that the vibrator control service will wait for new
159      * vibration params.
160      */
getRequestVibrationParamsTimeoutMs()161     public int getRequestVibrationParamsTimeoutMs() {
162         return Math.max(mRequestVibrationParamsTimeoutMs, 0);
163     }
164 
165     /**
166      * The list of usages that should request vibration params before they are played. These
167      * usages don't have strong latency requirements, e.g. ringtone and notification, and can be
168      * slightly delayed.
169      */
getRequestVibrationParamsForUsages()170     public int[] getRequestVibrationParamsForUsages() {
171         return mRequestVibrationParamsForUsages;
172     }
173 
174     /**
175      * The duration, in milliseconds, that should be applied to convert vibration effect's
176      * {@link android.os.vibrator.RampSegment} to a {@link android.os.vibrator.StepSegment} on
177      * devices without PWLE support.
178      */
getRampStepDurationMs()179     public int getRampStepDurationMs() {
180         if (mRampStepDurationMs < 0) {
181             return 0;
182         }
183         return mRampStepDurationMs;
184     }
185 
186     /**
187      * Whether or not vibrations are ignored if the device is on a wireless charger.
188      *
189      * <p>This may be the case if vibration during wireless charging causes unwanted results, like
190      * moving the device out of alignment with the charging pad.
191      */
ignoreVibrationsOnWirelessCharger()192     public boolean ignoreVibrationsOnWirelessCharger() {
193         return mIgnoreVibrationsOnWirelessCharger;
194     }
195 
196     /**
197      * Whether keyboard vibration settings is enabled by default.
198      * @hide
199      */
isDefaultKeyboardVibrationEnabled()200     public boolean isDefaultKeyboardVibrationEnabled() {
201         return mDefaultKeyboardVibrationEnabled;
202     }
203 
204     /**
205      * Whether the device has a fixed amplitude for keyboard.
206      * @hide
207      */
hasFixedKeyboardAmplitude()208     public boolean hasFixedKeyboardAmplitude() {
209         return mHasFixedKeyboardAmplitude;
210     }
211 
212     /** Get the default vibration intensity for given usage. */
213     @VibrationIntensity
getDefaultVibrationIntensity(@ibrationAttributes.Usage int usage)214     public int getDefaultVibrationIntensity(@VibrationAttributes.Usage int usage) {
215         switch (usage) {
216             case USAGE_ALARM:
217                 return mDefaultAlarmVibrationIntensity;
218             case USAGE_NOTIFICATION:
219             case USAGE_COMMUNICATION_REQUEST:
220                 return mDefaultNotificationVibrationIntensity;
221             case USAGE_RINGTONE:
222                 return mDefaultRingVibrationIntensity;
223             case USAGE_TOUCH:
224             case USAGE_HARDWARE_FEEDBACK:
225             case USAGE_PHYSICAL_EMULATION:
226             case USAGE_ACCESSIBILITY:
227                 return mDefaultHapticFeedbackIntensity;
228             case USAGE_MEDIA:
229             case USAGE_UNKNOWN:
230                 // fall through
231             default:
232                 return mDefaultMediaVibrationIntensity;
233         }
234     }
235 
236     @Override
toString()237     public String toString() {
238         return "VibrationConfig{"
239                 + "mIgnoreVibrationsOnWirelessCharger=" + mIgnoreVibrationsOnWirelessCharger
240                 + ", mHapticChannelMaxVibrationAmplitude=" + mHapticChannelMaxVibrationAmplitude
241                 + ", mRampStepDurationMs=" + mRampStepDurationMs
242                 + ", mRampDownDurationMs=" + mRampDownDurationMs
243                 + ", mRequestVibrationParamsForUsages="
244                 + Arrays.toString(getRequestVibrationParamsForUsagesNames())
245                 + ", mRequestVibrationParamsTimeoutMs=" + mRequestVibrationParamsTimeoutMs
246                 + ", mDefaultAlarmIntensity=" + mDefaultAlarmVibrationIntensity
247                 + ", mDefaultHapticFeedbackIntensity=" + mDefaultHapticFeedbackIntensity
248                 + ", mDefaultMediaIntensity=" + mDefaultMediaVibrationIntensity
249                 + ", mDefaultNotificationIntensity=" + mDefaultNotificationVibrationIntensity
250                 + ", mDefaultRingIntensity=" + mDefaultRingVibrationIntensity
251                 + ", mDefaultKeyboardVibrationEnabled=" + mDefaultKeyboardVibrationEnabled
252                 + "}";
253     }
254 
255     /**
256      * Write current settings into given {@link PrintWriter}, skipping the default settings.
257      *
258      * @hide
259      */
dumpWithoutDefaultSettings(IndentingPrintWriter pw)260     public void dumpWithoutDefaultSettings(IndentingPrintWriter pw) {
261         pw.println("VibrationConfig:");
262         pw.increaseIndent();
263         pw.println("ignoreVibrationsOnWirelessCharger = " + mIgnoreVibrationsOnWirelessCharger);
264         pw.println("hapticChannelMaxAmplitude = " + mHapticChannelMaxVibrationAmplitude);
265         pw.println("rampStepDurationMs = " + mRampStepDurationMs);
266         pw.println("rampDownDurationMs = " + mRampDownDurationMs);
267         pw.println("requestVibrationParamsForUsages = "
268                 + Arrays.toString(getRequestVibrationParamsForUsagesNames()));
269         pw.println("requestVibrationParamsTimeoutMs = " + mRequestVibrationParamsTimeoutMs);
270         pw.decreaseIndent();
271     }
272 
getRequestVibrationParamsForUsagesNames()273     private String[] getRequestVibrationParamsForUsagesNames() {
274         int usagesCount = mRequestVibrationParamsForUsages.length;
275         String[] names = new String[usagesCount];
276         for (int i = 0; i < usagesCount; i++) {
277             names[i] = VibrationAttributes.usageToString(mRequestVibrationParamsForUsages[i]);
278         }
279 
280         return names;
281     }
282 }
283