blob: 5d0fa26f640ed2191bc7cf47211041b3f8546ee2 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2008 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
17package com.android.server;
18
Alexey Kuzmin3fe97b02018-12-12 14:21:55 +000019import android.app.ActivityManager;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080020import android.app.AppOpsManager;
Alexey Kuzmin3fe97b02018-12-12 14:21:55 +000021import android.app.IUidObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.content.pm.PackageManager;
Michael Wright71216972017-01-31 18:33:54 +000027import android.content.res.Resources;
Jeff Brown7f6c2312012-04-13 20:38:38 -070028import android.database.ContentObserver;
29import android.hardware.input.InputManager;
Michael Wrightf268bf52018-02-07 23:23:34 +000030import android.hardware.vibrator.V1_0.EffectStrength;
Michael Wright36d873f2018-01-08 15:54:05 +000031import android.icu.text.DateFormat;
Bookatza7020bd2018-08-28 16:29:35 -070032import android.media.AudioAttributes;
Brad Ebinger2d1c3b32016-05-12 18:05:17 -070033import android.media.AudioManager;
Dianne Hackborn91268cf2013-06-13 19:06:50 -070034import android.os.BatteryStats;
Bookatza7020bd2018-08-28 16:29:35 -070035import android.os.Binder;
Michael Wrightc390fbe2018-12-12 19:45:09 +000036import android.os.ExternalVibration;
Joe Onorato95e4f702009-03-24 19:29:09 -070037import android.os.Handler;
Bookatza7020bd2018-08-28 16:29:35 -070038import android.os.IBinder;
Michael Wrightc390fbe2018-12-12 19:45:09 +000039import android.os.IExternalVibratorService;
Mike Lockwood3a322132009-11-24 00:30:52 -050040import android.os.IVibratorService;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.os.PowerManager;
Bookatza7020bd2018-08-28 16:29:35 -070042import android.os.PowerManager.ServiceType;
Dianne Hackborneb94fa72014-06-03 17:48:12 -070043import android.os.PowerManagerInternal;
Bookatza7020bd2018-08-28 16:29:35 -070044import android.os.PowerSaveState;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import android.os.Process;
46import android.os.RemoteException;
Felipe Lemea5281002017-02-10 15:13:48 -080047import android.os.ResultReceiver;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080048import android.os.ServiceManager;
Felipe Lemea5281002017-02-10 15:13:48 -080049import android.os.ShellCallback;
50import android.os.ShellCommand;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.os.SystemClock;
Alexey Kuzmine59145a2018-02-10 15:19:03 +000052import android.os.Trace;
Jeff Brownd4935962012-09-25 13:27:20 -070053import android.os.UserHandle;
Michael Wright71216972017-01-31 18:33:54 +000054import android.os.VibrationEffect;
Bookatza7020bd2018-08-28 16:29:35 -070055import android.os.Vibrator;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070056import android.os.WorkSource;
Jeff Brown7f6c2312012-04-13 20:38:38 -070057import android.provider.Settings;
58import android.provider.Settings.SettingNotFoundException;
Felipe Leme5e2e6322017-07-14 17:25:59 -070059import android.util.DebugUtils;
Joe Onorato8a9b2202010-02-26 18:56:32 -080060import android.util.Slog;
Alexey Kuzmin5a0a26f2018-03-20 18:25:51 +000061import android.util.SparseArray;
Bookatza7020bd2018-08-28 16:29:35 -070062import android.util.StatsLog;
Jeff Brown7f6c2312012-04-13 20:38:38 -070063import android.view.InputDevice;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064
Alexey Kuzmin7f38fb12018-02-07 13:51:28 +000065import com.android.internal.annotations.GuardedBy;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080066import com.android.internal.app.IBatteryStats;
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060067import com.android.internal.util.DumpUtils;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080068
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -070069import java.io.FileDescriptor;
70import java.io.PrintWriter;
Jeff Brown7f6c2312012-04-13 20:38:38 -070071import java.util.ArrayList;
Michael Wright36d873f2018-01-08 15:54:05 +000072import java.util.Date;
Bookatza7020bd2018-08-28 16:29:35 -070073import java.util.LinkedList;
Patrick Scott18dd5f02009-07-02 11:31:12 -040074
Jeff Brown7f6c2312012-04-13 20:38:38 -070075public class VibratorService extends IVibratorService.Stub
76 implements InputManager.InputDeviceListener {
Mike Lockwood3a322132009-11-24 00:30:52 -050077 private static final String TAG = "VibratorService";
Jeff Brown82379ba2014-07-25 19:03:28 -070078 private static final boolean DEBUG = false;
Jorim Jaggi18f18ae2015-09-10 15:48:21 -070079 private static final String SYSTEM_UI_PACKAGE = "com.android.systemui";
Michael Wrightc390fbe2018-12-12 19:45:09 +000080 private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service";
Mike Lockwoodcc9a63d2009-11-10 07:50:28 -050081
Michael Wright36d873f2018-01-08 15:54:05 +000082 private static final long[] DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS = { 0, 30, 100, 30 };
83
Michael Wrightc390fbe2018-12-12 19:45:09 +000084 // Scale levels. Each level, except MUTE, is defined as the delta between the current setting
85 // and the default intensity for that type of vibration (i.e. current - default).
86 private static final int SCALE_MUTE = IExternalVibratorService.SCALE_MUTE; // -100
87 private static final int SCALE_VERY_LOW = IExternalVibratorService.SCALE_VERY_LOW; // -2
88 private static final int SCALE_LOW = IExternalVibratorService.SCALE_LOW; // -1
89 private static final int SCALE_NONE = IExternalVibratorService.SCALE_NONE; // 0
90 private static final int SCALE_HIGH = IExternalVibratorService.SCALE_HIGH; // 1
91 private static final int SCALE_VERY_HIGH = IExternalVibratorService.SCALE_VERY_HIGH; // 2
Michael Wright35a0c672018-01-24 00:32:53 +000092
Michael Wright0dbb5162018-05-25 15:13:36 +010093 // Gamma adjustments for scale levels.
94 private static final float SCALE_VERY_LOW_GAMMA = 2.0f;
95 private static final float SCALE_LOW_GAMMA = 1.5f;
96 private static final float SCALE_NONE_GAMMA = 1.0f;
97 private static final float SCALE_HIGH_GAMMA = 0.5f;
98 private static final float SCALE_VERY_HIGH_GAMMA = 0.25f;
99
100 // Max amplitudes for scale levels. If one is not listed, then the max amplitude is the default
101 // max amplitude.
102 private static final int SCALE_VERY_LOW_MAX_AMPLITUDE = 168; // 2/3 * 255
103 private static final int SCALE_LOW_MAX_AMPLITUDE = 192; // 3/4 * 255
Michael Wright35a0c672018-01-24 00:32:53 +0000104
105 // If a vibration is playing for longer than 5s, it's probably not haptic feedback.
106 private static final long MAX_HAPTIC_FEEDBACK_DURATION = 5000;
107
Michael Wright0dbb5162018-05-25 15:13:36 +0100108
109 // A mapping from the intensity adjustment to the scaling to apply, where the intensity
110 // adjustment is defined as the delta between the default intensity level and the user selected
111 // intensity level. It's important that we apply the scaling on the delta between the two so
112 // that the default intensity level applies no scaling to application provided effects.
113 private final SparseArray<ScaleLevel> mScaleLevels;
Arthur Hung85ed7da2019-03-05 13:58:19 +0800114 private final LinkedList<VibrationInfo> mPreviousRingVibrations;
115 private final LinkedList<VibrationInfo> mPreviousNotificationVibrations;
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700116 private final LinkedList<VibrationInfo> mPreviousVibrations;
117 private final int mPreviousVibrationsLimit;
Tyler Freeman319a34a2017-05-04 17:23:35 -0700118 private final boolean mAllowPriorityVibrationsInLowPowerMode;
Michael Wright71216972017-01-31 18:33:54 +0000119 private final boolean mSupportsAmplitudeControl;
Michael Wrightc390fbe2018-12-12 19:45:09 +0000120 private final boolean mSupportsExternalControl;
Michael Wright71216972017-01-31 18:33:54 +0000121 private final int mDefaultVibrationAmplitude;
Alexey Kuzmin5a0a26f2018-03-20 18:25:51 +0000122 private final SparseArray<VibrationEffect> mFallbackEffects;
Alexey Kuzmin3fe97b02018-12-12 14:21:55 +0000123 private final SparseArray<Integer> mProcStatesCache = new SparseArray();
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700124 private final WorkSource mTmpWorkSource = new WorkSource();
Jeff Brown7f6c2312012-04-13 20:38:38 -0700125 private final Handler mH = new Handler();
Michael Wright71216972017-01-31 18:33:54 +0000126 private final Object mLock = new Object();
Jeff Brown7f6c2312012-04-13 20:38:38 -0700127
128 private final Context mContext;
129 private final PowerManager.WakeLock mWakeLock;
Svet Ganovf7b47252018-02-26 11:11:27 -0800130 private final AppOpsManager mAppOps;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800131 private final IBatteryStats mBatteryStatsService;
Dianne Hackborneb94fa72014-06-03 17:48:12 -0700132 private PowerManagerInternal mPowerManagerInternal;
Jeff Brown7f6c2312012-04-13 20:38:38 -0700133 private InputManager mIm;
Michael Wright35a0c672018-01-24 00:32:53 +0000134 private Vibrator mVibrator;
135 private SettingsObserver mSettingObserver;
Jeff Brown7f6c2312012-04-13 20:38:38 -0700136
Michael Wright71216972017-01-31 18:33:54 +0000137 private volatile VibrateThread mThread;
Jeff Brown7f6c2312012-04-13 20:38:38 -0700138
Michael Wright71216972017-01-31 18:33:54 +0000139 // mInputDeviceVibrators lock should be acquired after mLock, if both are
Jeff Brown7f6c2312012-04-13 20:38:38 -0700140 // to be acquired
141 private final ArrayList<Vibrator> mInputDeviceVibrators = new ArrayList<Vibrator>();
142 private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators
143 private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators
144
Alexey Kuzmin7f38fb12018-02-07 13:51:28 +0000145 @GuardedBy("mLock")
Michael Wright71216972017-01-31 18:33:54 +0000146 private Vibration mCurrentVibration;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800147 private int mCurVibUid = -1;
Michael Wrightc390fbe2018-12-12 19:45:09 +0000148 private ExternalVibration mCurrentExternalVibration;
149 private boolean mVibratorUnderExternalControl;
Ruchi Kandoi13b03af2014-05-07 20:10:32 -0700150 private boolean mLowPowerMode;
Michael Wright35a0c672018-01-24 00:32:53 +0000151 private int mHapticFeedbackIntensity;
152 private int mNotificationIntensity;
Alexey Kuzminccdaebb2018-12-10 12:02:51 +0000153 private int mRingIntensity;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800154
Michael Wrightc390fbe2018-12-12 19:45:09 +0000155 static native boolean vibratorExists();
156 static native void vibratorInit();
157 static native void vibratorOn(long milliseconds);
158 static native void vibratorOff();
159 static native boolean vibratorSupportsAmplitudeControl();
160 static native void vibratorSetAmplitude(int amplitude);
161 static native long vibratorPerformEffect(long effect, long strength);
Harpreet "Eli" Sanghaa456f082018-12-14 12:06:10 +0900162 static native boolean vibratorSupportsExternalControl();
163 static native void vibratorSetExternalControl(boolean enabled);
Patrick Scott18dd5f02009-07-02 11:31:12 -0400164
Alexey Kuzmin3fe97b02018-12-12 14:21:55 +0000165 private final IUidObserver mUidObserver = new IUidObserver.Stub() {
166 @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
167 mProcStatesCache.put(uid, procState);
168 }
169
170 @Override public void onUidGone(int uid, boolean disabled) {
171 mProcStatesCache.delete(uid);
172 }
173
174 @Override public void onUidActive(int uid) {
175 }
176
177 @Override public void onUidIdle(int uid, boolean disabled) {
178 }
179
180 @Override public void onUidCachedChanged(int uid, boolean cached) {
181 }
182 };
183
Patrick Scott18dd5f02009-07-02 11:31:12 -0400184 private class Vibration implements IBinder.DeathRecipient {
Michael Wright35a0c672018-01-24 00:32:53 +0000185 public final IBinder token;
Michael Wright36d873f2018-01-08 15:54:05 +0000186 // Start time in CLOCK_BOOTTIME base.
Michael Wright35a0c672018-01-24 00:32:53 +0000187 public final long startTime;
Michael Wright36d873f2018-01-08 15:54:05 +0000188 // Start time in unix epoch time. Only to be used for debugging purposes and to correlate
Michael Wright35a0c672018-01-24 00:32:53 +0000189 // with other system events, any duration calculations should be done use startTime so as
Michael Wright36d873f2018-01-08 15:54:05 +0000190 // not to be affected by discontinuities created by RTC adjustments.
Michael Wright35a0c672018-01-24 00:32:53 +0000191 public final long startTimeDebug;
192 public final int usageHint;
193 public final int uid;
194 public final String opPkg;
Alexey Kuzmine1f06b82018-06-20 17:48:43 +0100195 public final String reason;
Michael Wright35a0c672018-01-24 00:32:53 +0000196
197 // The actual effect to be played.
198 public VibrationEffect effect;
199 // The original effect that was requested. This is non-null only when the original effect
200 // differs from the effect that's being played. Typically these two things differ because
201 // the effect was scaled based on the users vibration intensity settings.
202 public VibrationEffect originalEffect;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400203
Michael Wright71216972017-01-31 18:33:54 +0000204 private Vibration(IBinder token, VibrationEffect effect,
Alexey Kuzmine1f06b82018-06-20 17:48:43 +0100205 int usageHint, int uid, String opPkg, String reason) {
Michael Wright35a0c672018-01-24 00:32:53 +0000206 this.token = token;
207 this.effect = effect;
208 this.startTime = SystemClock.elapsedRealtime();
209 this.startTimeDebug = System.currentTimeMillis();
210 this.usageHint = usageHint;
211 this.uid = uid;
212 this.opPkg = opPkg;
Alexey Kuzmine1f06b82018-06-20 17:48:43 +0100213 this.reason = reason;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400214 }
215
216 public void binderDied() {
Michael Wright71216972017-01-31 18:33:54 +0000217 synchronized (mLock) {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400218 if (this == mCurrentVibration) {
219 doCancelVibrateLocked();
Patrick Scott18dd5f02009-07-02 11:31:12 -0400220 }
221 }
222 }
223
Michael Wright35a0c672018-01-24 00:32:53 +0000224 public boolean hasTimeoutLongerThan(long millis) {
225 final long duration = effect.getDuration();
226 return duration >= 0 && duration > millis;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400227 }
Jeff Brown969579b2014-05-20 19:29:29 -0700228
Michael Wright35a0c672018-01-24 00:32:53 +0000229 public boolean isHapticFeedback() {
Michael Wrightc390fbe2018-12-12 19:45:09 +0000230 if (VibratorService.this.isHapticFeedback(usageHint)) {
231 return true;
232 }
Michael Wright35a0c672018-01-24 00:32:53 +0000233 if (effect instanceof VibrationEffect.Prebaked) {
234 VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect;
235 switch (prebaked.getId()) {
236 case VibrationEffect.EFFECT_CLICK:
237 case VibrationEffect.EFFECT_DOUBLE_CLICK:
Alexey Kuzmin5a0a26f2018-03-20 18:25:51 +0000238 case VibrationEffect.EFFECT_HEAVY_CLICK:
Michael Wright35a0c672018-01-24 00:32:53 +0000239 case VibrationEffect.EFFECT_TICK:
Michael Wright0dbb5162018-05-25 15:13:36 +0100240 case VibrationEffect.EFFECT_POP:
241 case VibrationEffect.EFFECT_THUD:
Michael Wright35a0c672018-01-24 00:32:53 +0000242 return true;
243 default:
244 Slog.w(TAG, "Unknown prebaked vibration effect, "
245 + "assuming it isn't haptic feedback.");
246 return false;
247 }
Michael Wright71216972017-01-31 18:33:54 +0000248 }
Michael Wright35a0c672018-01-24 00:32:53 +0000249 final long duration = effect.getDuration();
250 return duration >= 0 && duration < MAX_HAPTIC_FEEDBACK_DURATION;
251 }
252
253 public boolean isNotification() {
Michael Wrightc390fbe2018-12-12 19:45:09 +0000254 return VibratorService.this.isNotification(usageHint);
Michael Wright35a0c672018-01-24 00:32:53 +0000255 }
256
257 public boolean isRingtone() {
Michael Wrightc390fbe2018-12-12 19:45:09 +0000258 return VibratorService.this.isRingtone(usageHint);
Michael Wright35a0c672018-01-24 00:32:53 +0000259 }
260
261 public boolean isFromSystem() {
262 return uid == Process.SYSTEM_UID || uid == 0 || SYSTEM_UI_PACKAGE.equals(opPkg);
Jeff Brown969579b2014-05-20 19:29:29 -0700263 }
Michael Wright36d873f2018-01-08 15:54:05 +0000264
265 public VibrationInfo toInfo() {
Michael Wright35a0c672018-01-24 00:32:53 +0000266 return new VibrationInfo(
Alexey Kuzmine1f06b82018-06-20 17:48:43 +0100267 startTimeDebug, effect, originalEffect, usageHint, uid, opPkg, reason);
Michael Wright36d873f2018-01-08 15:54:05 +0000268 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400269 }
270
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700271 private static class VibrationInfo {
Michael Wright36d873f2018-01-08 15:54:05 +0000272 private final long mStartTimeDebug;
Michael Wright71216972017-01-31 18:33:54 +0000273 private final VibrationEffect mEffect;
Michael Wright35a0c672018-01-24 00:32:53 +0000274 private final VibrationEffect mOriginalEffect;
Michael Wright71216972017-01-31 18:33:54 +0000275 private final int mUsageHint;
276 private final int mUid;
277 private final String mOpPkg;
Alexey Kuzmine1f06b82018-06-20 17:48:43 +0100278 private final String mReason;
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700279
Michael Wright36d873f2018-01-08 15:54:05 +0000280 public VibrationInfo(long startTimeDebug, VibrationEffect effect,
Alexey Kuzmine1f06b82018-06-20 17:48:43 +0100281 VibrationEffect originalEffect, int usageHint, int uid,
282 String opPkg, String reason) {
Michael Wright36d873f2018-01-08 15:54:05 +0000283 mStartTimeDebug = startTimeDebug;
Michael Wright71216972017-01-31 18:33:54 +0000284 mEffect = effect;
Michael Wright35a0c672018-01-24 00:32:53 +0000285 mOriginalEffect = originalEffect;
Michael Wright71216972017-01-31 18:33:54 +0000286 mUsageHint = usageHint;
287 mUid = uid;
288 mOpPkg = opPkg;
Alexey Kuzmine1f06b82018-06-20 17:48:43 +0100289 mReason = reason;
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700290 }
291
292 @Override
293 public String toString() {
294 return new StringBuilder()
Michael Wright36d873f2018-01-08 15:54:05 +0000295 .append("startTime: ")
296 .append(DateFormat.getDateTimeInstance().format(new Date(mStartTimeDebug)))
Michael Wright71216972017-01-31 18:33:54 +0000297 .append(", effect: ")
298 .append(mEffect)
Michael Wright35a0c672018-01-24 00:32:53 +0000299 .append(", originalEffect: ")
300 .append(mOriginalEffect)
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700301 .append(", usageHint: ")
Michael Wright71216972017-01-31 18:33:54 +0000302 .append(mUsageHint)
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700303 .append(", uid: ")
Michael Wright71216972017-01-31 18:33:54 +0000304 .append(mUid)
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700305 .append(", opPkg: ")
Michael Wright71216972017-01-31 18:33:54 +0000306 .append(mOpPkg)
Alexey Kuzmine1f06b82018-06-20 17:48:43 +0100307 .append(", reason: ")
308 .append(mReason)
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700309 .toString();
310 }
311 }
312
Michael Wright0dbb5162018-05-25 15:13:36 +0100313 private static final class ScaleLevel {
314 public final float gamma;
315 public final int maxAmplitude;
316
317 public ScaleLevel(float gamma) {
318 this(gamma, VibrationEffect.MAX_AMPLITUDE);
319 }
320
321 public ScaleLevel(float gamma, int maxAmplitude) {
322 this.gamma = gamma;
323 this.maxAmplitude = maxAmplitude;
324 }
325
326 @Override
327 public String toString() {
328 return "ScaleLevel{gamma=" + gamma + ", maxAmplitude=" + maxAmplitude + "}";
329 }
330 }
331
Mike Lockwood3a322132009-11-24 00:30:52 -0500332 VibratorService(Context context) {
Vincent Beckere6904fb2012-08-10 14:17:33 +0200333 vibratorInit();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 // Reset the hardware to a default state, in case this is a runtime
335 // restart instead of a fresh boot.
336 vibratorOff();
337
Michael Wright71216972017-01-31 18:33:54 +0000338 mSupportsAmplitudeControl = vibratorSupportsAmplitudeControl();
Michael Wrightc390fbe2018-12-12 19:45:09 +0000339 mSupportsExternalControl = vibratorSupportsExternalControl();
Michael Wright71216972017-01-31 18:33:54 +0000340
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 mContext = context;
Michael Wright71216972017-01-31 18:33:54 +0000342 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700343 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344 mWakeLock.setReferenceCounted(true);
345
Svet Ganovf7b47252018-02-26 11:11:27 -0800346 mAppOps = mContext.getSystemService(AppOpsManager.class);
Dianne Hackborn91268cf2013-06-13 19:06:50 -0700347 mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
348 BatteryStats.SERVICE_NAME));
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800349
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700350 mPreviousVibrationsLimit = mContext.getResources().getInteger(
351 com.android.internal.R.integer.config_previousVibrationsDumpLimit);
352
Michael Wright71216972017-01-31 18:33:54 +0000353 mDefaultVibrationAmplitude = mContext.getResources().getInteger(
354 com.android.internal.R.integer.config_defaultVibrationAmplitude);
355
Tyler Freeman319a34a2017-05-04 17:23:35 -0700356 mAllowPriorityVibrationsInLowPowerMode = mContext.getResources().getBoolean(
357 com.android.internal.R.bool.config_allowPriorityVibrationsInLowPowerMode);
358
Arthur Hung85ed7da2019-03-05 13:58:19 +0800359 mPreviousRingVibrations = new LinkedList<>();
360 mPreviousNotificationVibrations = new LinkedList<>();
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700361 mPreviousVibrations = new LinkedList<>();
Patrick Scott18dd5f02009-07-02 11:31:12 -0400362
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 IntentFilter filter = new IntentFilter();
364 filter.addAction(Intent.ACTION_SCREEN_OFF);
365 context.registerReceiver(mIntentReceiver, filter);
Michael Wright71216972017-01-31 18:33:54 +0000366
Michael Wrightd39cbec2018-04-16 19:35:13 +0100367 VibrationEffect clickEffect = createEffectFromResource(
Michael Wright71216972017-01-31 18:33:54 +0000368 com.android.internal.R.array.config_virtualKeyVibePattern);
Michael Wright71216972017-01-31 18:33:54 +0000369 VibrationEffect doubleClickEffect = VibrationEffect.createWaveform(
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000370 DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS, -1 /*repeatIndex*/);
Michael Wrightd39cbec2018-04-16 19:35:13 +0100371 VibrationEffect heavyClickEffect = createEffectFromResource(
372 com.android.internal.R.array.config_longPressVibePattern);
373 VibrationEffect tickEffect = createEffectFromResource(
Michael Wright57d94d92017-05-31 14:44:45 +0100374 com.android.internal.R.array.config_clockTickVibePattern);
Michael Wright71216972017-01-31 18:33:54 +0000375
Michael Wright0dbb5162018-05-25 15:13:36 +0100376 mFallbackEffects = new SparseArray<>();
Alexey Kuzmin5a0a26f2018-03-20 18:25:51 +0000377 mFallbackEffects.put(VibrationEffect.EFFECT_CLICK, clickEffect);
378 mFallbackEffects.put(VibrationEffect.EFFECT_DOUBLE_CLICK, doubleClickEffect);
379 mFallbackEffects.put(VibrationEffect.EFFECT_TICK, tickEffect);
Michael Wrightd39cbec2018-04-16 19:35:13 +0100380 mFallbackEffects.put(VibrationEffect.EFFECT_HEAVY_CLICK, heavyClickEffect);
Michael Wright0dbb5162018-05-25 15:13:36 +0100381
382 mScaleLevels = new SparseArray<>();
383 mScaleLevels.put(SCALE_VERY_LOW,
384 new ScaleLevel(SCALE_VERY_LOW_GAMMA, SCALE_VERY_LOW_MAX_AMPLITUDE));
385 mScaleLevels.put(SCALE_LOW, new ScaleLevel(SCALE_LOW_GAMMA, SCALE_LOW_MAX_AMPLITUDE));
386 mScaleLevels.put(SCALE_NONE, new ScaleLevel(SCALE_NONE_GAMMA));
387 mScaleLevels.put(SCALE_HIGH, new ScaleLevel(SCALE_HIGH_GAMMA));
388 mScaleLevels.put(SCALE_VERY_HIGH, new ScaleLevel(SCALE_VERY_HIGH_GAMMA));
Michael Wrightc390fbe2018-12-12 19:45:09 +0000389
390 ServiceManager.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService());
Michael Wright57d94d92017-05-31 14:44:45 +0100391 }
392
Michael Wrightd39cbec2018-04-16 19:35:13 +0100393 private VibrationEffect createEffectFromResource(int resId) {
394 long[] timings = getLongIntArray(mContext.getResources(), resId);
395 return createEffectFromTimings(timings);
396 }
397
398 private static VibrationEffect createEffectFromTimings(long[] timings) {
Michael Wright57d94d92017-05-31 14:44:45 +0100399 if (timings == null || timings.length == 0) {
400 return null;
401 } else if (timings.length == 1) {
402 return VibrationEffect.createOneShot(timings[0], VibrationEffect.DEFAULT_AMPLITUDE);
403 } else {
404 return VibrationEffect.createWaveform(timings, -1);
405 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800406 }
407
Jeff Brown7f6c2312012-04-13 20:38:38 -0700408 public void systemReady() {
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000409 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorService#systemReady");
410 try {
411 mIm = mContext.getSystemService(InputManager.class);
412 mVibrator = mContext.getSystemService(Vibrator.class);
413 mSettingObserver = new SettingsObserver(mH);
Jeff Brownd4935962012-09-25 13:27:20 -0700414
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000415 mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
416 mPowerManagerInternal.registerLowPowerModeObserver(
417 new PowerManagerInternal.LowPowerModeListener() {
418 @Override
419 public int getServiceType() {
420 return ServiceType.VIBRATION;
421 }
jackqdyulei455e90a2017-02-09 15:29:16 -0800422
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000423 @Override
424 public void onLowPowerModeChanged(PowerSaveState result) {
425 updateVibrators();
426 }
427 });
Dianne Hackborneb94fa72014-06-03 17:48:12 -0700428
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000429 mContext.getContentResolver().registerContentObserver(
430 Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES),
431 true, mSettingObserver, UserHandle.USER_ALL);
Ruchi Kandoi13b03af2014-05-07 20:10:32 -0700432
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000433 mContext.getContentResolver().registerContentObserver(
434 Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_INTENSITY),
435 true, mSettingObserver, UserHandle.USER_ALL);
Michael Wright35a0c672018-01-24 00:32:53 +0000436
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000437 mContext.getContentResolver().registerContentObserver(
438 Settings.System.getUriFor(Settings.System.NOTIFICATION_VIBRATION_INTENSITY),
439 true, mSettingObserver, UserHandle.USER_ALL);
Michael Wright35a0c672018-01-24 00:32:53 +0000440
Alexey Kuzminccdaebb2018-12-10 12:02:51 +0000441 mContext.getContentResolver().registerContentObserver(
442 Settings.System.getUriFor(Settings.System.RING_VIBRATION_INTENSITY),
443 true, mSettingObserver, UserHandle.USER_ALL);
444
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000445 mContext.registerReceiver(new BroadcastReceiver() {
446 @Override
447 public void onReceive(Context context, Intent intent) {
448 updateVibrators();
449 }
450 }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mH);
Jeff Brownd4935962012-09-25 13:27:20 -0700451
Alexey Kuzmin3fe97b02018-12-12 14:21:55 +0000452 try {
453 ActivityManager.getService().registerUidObserver(mUidObserver,
454 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE,
455 ActivityManager.PROCESS_STATE_UNKNOWN, null);
456 } catch (RemoteException e) {
457 // ignored; both services live in system_server
458 }
459
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000460 updateVibrators();
461 } finally {
462 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
463 }
Dianne Hackbornea9020e2010-11-04 11:39:12 -0700464 }
Jeff Brown7f6c2312012-04-13 20:38:38 -0700465
Ruchi Kandoi13b03af2014-05-07 20:10:32 -0700466 private final class SettingsObserver extends ContentObserver {
467 public SettingsObserver(Handler handler) {
468 super(handler);
469 }
470
471 @Override
472 public void onChange(boolean SelfChange) {
Michael Wright71216972017-01-31 18:33:54 +0000473 updateVibrators();
Ruchi Kandoi13b03af2014-05-07 20:10:32 -0700474 }
475 }
476
Jeff Brown82379ba2014-07-25 19:03:28 -0700477 @Override // Binder call
Jeff Brown7f6c2312012-04-13 20:38:38 -0700478 public boolean hasVibrator() {
479 return doVibratorExists();
480 }
481
Michael Wright71216972017-01-31 18:33:54 +0000482 @Override // Binder call
483 public boolean hasAmplitudeControl() {
484 synchronized (mInputDeviceVibrators) {
485 // Input device vibrators don't support amplitude controls yet, but are still used over
486 // the system vibrator when connected.
487 return mSupportsAmplitudeControl && mInputDeviceVibrators.isEmpty();
488 }
489 }
490
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800491 private void verifyIncomingUid(int uid) {
492 if (uid == Binder.getCallingUid()) {
493 return;
494 }
495 if (Binder.getCallingPid() == Process.myPid()) {
496 return;
497 }
498 mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
499 Binder.getCallingPid(), Binder.getCallingUid(), null);
500 }
501
Michael Wright71216972017-01-31 18:33:54 +0000502 /**
503 * Validate the incoming VibrationEffect.
504 *
505 * We can't throw exceptions here since we might be called from some system_server component,
506 * which would bring the whole system down.
507 *
508 * @return whether the VibrationEffect is valid
509 */
510 private static boolean verifyVibrationEffect(VibrationEffect effect) {
511 if (effect == null) {
512 // Effect must not be null.
513 Slog.wtf(TAG, "effect must not be null");
514 return false;
515 }
516 try {
517 effect.validate();
518 } catch (Exception e) {
519 Slog.wtf(TAG, "Encountered issue when verifying VibrationEffect.", e);
520 return false;
521 }
522 return true;
523 }
524
525 private static long[] getLongIntArray(Resources r, int resid) {
526 int[] ar = r.getIntArray(resid);
527 if (ar == null) {
528 return null;
529 }
530 long[] out = new long[ar.length];
531 for (int i = 0; i < ar.length; i++) {
532 out[i] = ar[i];
533 }
534 return out;
535 }
536
Jeff Brown82379ba2014-07-25 19:03:28 -0700537 @Override // Binder call
Alexey Kuzmine1f06b82018-06-20 17:48:43 +0100538 public void vibrate(int uid, String opPkg, VibrationEffect effect, int usageHint, String reason,
John Spurlock1af30c72014-03-10 08:33:35 -0400539 IBinder token) {
Alexey Kuzmine1f06b82018-06-20 17:48:43 +0100540 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000541 try {
542 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
543 != PackageManager.PERMISSION_GRANTED) {
544 throw new SecurityException("Requires VIBRATE permission");
Alexey Kuzmin7f38fb12018-02-07 13:51:28 +0000545 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000546 if (token == null) {
547 Slog.e(TAG, "token must not be null");
548 return;
549 }
550 verifyIncomingUid(uid);
551 if (!verifyVibrationEffect(effect)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800552 return;
553 }
554
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000555 // If our current vibration is longer than the new vibration and is the same amplitude,
556 // then just let the current one finish.
557 synchronized (mLock) {
558 if (effect instanceof VibrationEffect.OneShot
559 && mCurrentVibration != null
560 && mCurrentVibration.effect instanceof VibrationEffect.OneShot) {
561 VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect;
562 VibrationEffect.OneShot currentOneShot =
563 (VibrationEffect.OneShot) mCurrentVibration.effect;
564 if (mCurrentVibration.hasTimeoutLongerThan(newOneShot.getDuration())
565 && newOneShot.getAmplitude() == currentOneShot.getAmplitude()) {
566 if (DEBUG) {
567 Slog.d(TAG,
568 "Ignoring incoming vibration in favor of current vibration");
569 }
570 return;
571 }
572 }
573
Michael Wrightc390fbe2018-12-12 19:45:09 +0000574
575 // If something has external control of the vibrator, assume that it's more
576 // important for now.
577 if (mCurrentExternalVibration != null) {
578 if (DEBUG) {
579 Slog.d(TAG, "Ignoring incoming vibration for current external vibration");
580 }
581 return;
582 }
583
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000584 // If the current vibration is repeating and the incoming one is non-repeating,
585 // then ignore the non-repeating vibration. This is so that we don't cancel
586 // vibrations that are meant to grab the attention of the user, like ringtones and
587 // alarms, in favor of one-shot vibrations that are likely quite short.
588 if (!isRepeatingVibration(effect)
589 && mCurrentVibration != null
590 && isRepeatingVibration(mCurrentVibration.effect)) {
591 if (DEBUG) {
592 Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
593 }
594 return;
595 }
596
Alexey Kuzmine1f06b82018-06-20 17:48:43 +0100597 Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg, reason);
Alexey Kuzminddebe592019-01-08 16:07:41 +0000598 if (mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)
599 > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
Alexey Kuzmine5822992019-01-29 19:50:26 +0000600 && !vib.isNotification() && !vib.isRingtone()) {
Arthur Hungd8a71c92019-02-26 14:40:17 +0800601 Slog.e(TAG, "Ignoring incoming vibration as process with"
602 + " uid = " + uid + " is background,"
603 + " usage = " + AudioAttributes.usageToString(vib.usageHint));
Alexey Kuzminddebe592019-01-08 16:07:41 +0000604 return;
605 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000606 linkVibration(vib);
607 long ident = Binder.clearCallingIdentity();
608 try {
Alexey Kuzmine1f06b82018-06-20 17:48:43 +0100609
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000610 doCancelVibrateLocked();
611 startVibrationLocked(vib);
612 addToPreviousVibrationsLocked(vib);
613 } finally {
614 Binder.restoreCallingIdentity(ident);
615 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800616 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000617 } finally {
618 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800619 }
620 }
621
Michael Wright58c46312017-10-05 14:04:14 -0400622 private static boolean isRepeatingVibration(VibrationEffect effect) {
Michael Wright35a0c672018-01-24 00:32:53 +0000623 return effect.getDuration() == Long.MAX_VALUE;
Michael Wright58c46312017-10-05 14:04:14 -0400624 }
625
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700626 private void addToPreviousVibrationsLocked(Vibration vib) {
Arthur Hung85ed7da2019-03-05 13:58:19 +0800627 final LinkedList<VibrationInfo> previousVibrations;
628 if (vib.isRingtone()) {
629 previousVibrations = mPreviousRingVibrations;
630 } else if (vib.isNotification()) {
631 previousVibrations = mPreviousNotificationVibrations;
632 } else {
633 previousVibrations = mPreviousVibrations;
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700634 }
Arthur Hung85ed7da2019-03-05 13:58:19 +0800635
636 if (previousVibrations.size() > mPreviousVibrationsLimit) {
637 previousVibrations.removeFirst();
638 }
639 previousVibrations.addLast(vib.toInfo());
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700640 }
641
Jeff Brown82379ba2014-07-25 19:03:28 -0700642 @Override // Binder call
Patrick Scott18dd5f02009-07-02 11:31:12 -0400643 public void cancelVibrate(IBinder token) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800644 mContext.enforceCallingOrSelfPermission(
645 android.Manifest.permission.VIBRATE,
646 "cancelVibrate");
647
Michael Wright71216972017-01-31 18:33:54 +0000648 synchronized (mLock) {
Michael Wright35a0c672018-01-24 00:32:53 +0000649 if (mCurrentVibration != null && mCurrentVibration.token == token) {
Michael Wright71216972017-01-31 18:33:54 +0000650 if (DEBUG) {
651 Slog.d(TAG, "Canceling vibration.");
652 }
653 long ident = Binder.clearCallingIdentity();
654 try {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400655 doCancelVibrateLocked();
Michael Wright71216972017-01-31 18:33:54 +0000656 } finally {
657 Binder.restoreCallingIdentity(ident);
Patrick Scott18dd5f02009-07-02 11:31:12 -0400658 }
659 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800660 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800661 }
Eric Olsenf42f15c2009-10-29 16:42:03 -0700662
Michael Wright71216972017-01-31 18:33:54 +0000663 private final Runnable mVibrationEndRunnable = new Runnable() {
Jeff Brown82379ba2014-07-25 19:03:28 -0700664 @Override
Patrick Scott18dd5f02009-07-02 11:31:12 -0400665 public void run() {
Michael Wright71216972017-01-31 18:33:54 +0000666 onVibrationFinished();
Patrick Scott18dd5f02009-07-02 11:31:12 -0400667 }
668 };
669
Alexey Kuzmin7f38fb12018-02-07 13:51:28 +0000670 @GuardedBy("mLock")
Patrick Scott18dd5f02009-07-02 11:31:12 -0400671 private void doCancelVibrateLocked() {
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000672 Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
673 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doCancelVibrateLocked");
674 try {
675 mH.removeCallbacks(mVibrationEndRunnable);
676 if (mThread != null) {
677 mThread.cancel();
678 mThread = null;
679 }
Michael Wrightc390fbe2018-12-12 19:45:09 +0000680 if (mCurrentExternalVibration != null) {
681 mCurrentExternalVibration.mute();
682 mCurrentExternalVibration = null;
683 setVibratorUnderExternalControl(false);
684 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000685 doVibratorOff();
686 reportFinishVibrationLocked();
687 } finally {
688 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
Patrick Scott18dd5f02009-07-02 11:31:12 -0400689 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400690 }
691
Michael Wright71216972017-01-31 18:33:54 +0000692 // Callback for whenever the current vibration has finished played out
693 public void onVibrationFinished() {
694 if (DEBUG) {
695 Slog.e(TAG, "Vibration finished, cleaning up");
Patrick Scott18dd5f02009-07-02 11:31:12 -0400696 }
Michael Wright71216972017-01-31 18:33:54 +0000697 synchronized (mLock) {
698 // Make sure the vibration is really done. This also reports that the vibration is
699 // finished.
700 doCancelVibrateLocked();
701 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400702 }
703
Alexey Kuzmin7f38fb12018-02-07 13:51:28 +0000704 @GuardedBy("mLock")
Patrick Scott18dd5f02009-07-02 11:31:12 -0400705 private void startVibrationLocked(final Vibration vib) {
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000706 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked");
707 try {
708 if (!isAllowedToVibrateLocked(vib)) {
709 return;
Michael Wright71216972017-01-31 18:33:54 +0000710 }
Michael Wright71216972017-01-31 18:33:54 +0000711
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000712 final int intensity = getCurrentIntensityLocked(vib);
713 if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
714 return;
Michael Wright71216972017-01-31 18:33:54 +0000715 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000716
717 if (vib.isRingtone() && !shouldVibrateForRingtone()) {
718 if (DEBUG) {
719 Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones");
720 }
721 return;
722 }
723
724 final int mode = getAppOpMode(vib);
725 if (mode != AppOpsManager.MODE_ALLOWED) {
726 if (mode == AppOpsManager.MODE_ERRORED) {
727 // We might be getting calls from within system_server, so we don't actually
728 // want to throw a SecurityException here.
729 Slog.w(TAG, "Would be an error: vibrate from uid " + vib.uid);
730 }
731 return;
732 }
733 applyVibrationIntensityScalingLocked(vib, intensity);
734 startVibrationInnerLocked(vib);
735 } finally {
736 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
Michael Wright71216972017-01-31 18:33:54 +0000737 }
Michael Wright71216972017-01-31 18:33:54 +0000738 }
739
Alexey Kuzmin7f38fb12018-02-07 13:51:28 +0000740 @GuardedBy("mLock")
Michael Wright71216972017-01-31 18:33:54 +0000741 private void startVibrationInnerLocked(Vibration vib) {
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000742 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationInnerLocked");
743 try {
744 mCurrentVibration = vib;
745 if (vib.effect instanceof VibrationEffect.OneShot) {
746 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
747 VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect;
748 doVibratorOn(oneShot.getDuration(), oneShot.getAmplitude(), vib.uid, vib.usageHint);
749 mH.postDelayed(mVibrationEndRunnable, oneShot.getDuration());
750 } else if (vib.effect instanceof VibrationEffect.Waveform) {
751 // mThread better be null here. doCancelVibrate should always be
752 // called before startNextVibrationLocked or startVibrationLocked.
753 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
754 VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect;
755 mThread = new VibrateThread(waveform, vib.uid, vib.usageHint);
756 mThread.start();
757 } else if (vib.effect instanceof VibrationEffect.Prebaked) {
758 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
759 long timeout = doVibratorPrebakedEffectLocked(vib);
760 if (timeout > 0) {
761 mH.postDelayed(mVibrationEndRunnable, timeout);
762 }
763 } else {
764 Slog.e(TAG, "Unknown vibration type, ignoring");
Michael Wright71216972017-01-31 18:33:54 +0000765 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000766 } finally {
767 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800768 }
769 }
770
Michael Wright35a0c672018-01-24 00:32:53 +0000771 private boolean isAllowedToVibrateLocked(Vibration vib) {
Tyler Freeman319a34a2017-05-04 17:23:35 -0700772 if (!mLowPowerMode) {
773 return true;
774 }
Michael Wright35a0c672018-01-24 00:32:53 +0000775
776 if (vib.usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
Tyler Freeman319a34a2017-05-04 17:23:35 -0700777 return true;
778 }
Tyler Freeman319a34a2017-05-04 17:23:35 -0700779
Michael Wright35a0c672018-01-24 00:32:53 +0000780 if (vib.usageHint == AudioAttributes.USAGE_ALARM ||
781 vib.usageHint == AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY ||
782 vib.usageHint == AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) {
Tyler Freeman319a34a2017-05-04 17:23:35 -0700783 return true;
784 }
785
786 return false;
787 }
788
Michael Wright35a0c672018-01-24 00:32:53 +0000789 private int getCurrentIntensityLocked(Vibration vib) {
Alexey Kuzminccdaebb2018-12-10 12:02:51 +0000790 if (vib.isRingtone()) {
791 return mRingIntensity;
792 } else if (vib.isNotification()) {
Michael Wright35a0c672018-01-24 00:32:53 +0000793 return mNotificationIntensity;
794 } else if (vib.isHapticFeedback()) {
795 return mHapticFeedbackIntensity;
796 } else {
797 return Vibrator.VIBRATION_INTENSITY_MEDIUM;
798 }
799 }
800
801 /**
802 * Scale the vibration effect by the intensity as appropriate based its intent.
803 */
804 private void applyVibrationIntensityScalingLocked(Vibration vib, int intensity) {
805 if (vib.effect instanceof VibrationEffect.Prebaked) {
806 // Prebaked effects are always just a direct translation from intensity to
807 // EffectStrength.
808 VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked)vib.effect;
809 prebaked.setEffectStrength(intensityToEffectStrength(intensity));
810 return;
811 }
812
Michael Wright0dbb5162018-05-25 15:13:36 +0100813 final int defaultIntensity;
Alexey Kuzminccdaebb2018-12-10 12:02:51 +0000814 if (vib.isRingtone()) {
815 defaultIntensity = mVibrator.getDefaultRingVibrationIntensity();
816 } else if (vib.isNotification()) {
Michael Wright0dbb5162018-05-25 15:13:36 +0100817 defaultIntensity = mVibrator.getDefaultNotificationVibrationIntensity();
818 } else if (vib.isHapticFeedback()) {
819 defaultIntensity = mVibrator.getDefaultHapticFeedbackIntensity();
Michael Wright35a0c672018-01-24 00:32:53 +0000820 } else {
Michael Wright0dbb5162018-05-25 15:13:36 +0100821 // If we don't know what kind of vibration we're playing then just skip scaling for
822 // now.
823 return;
824 }
825
826 final ScaleLevel scale = mScaleLevels.get(intensity - defaultIntensity);
827 if (scale == null) {
828 // We should have scaling levels for all cases, so not being able to scale because of a
829 // missing level is unexpected.
830 Slog.e(TAG, "No configured scaling level!"
831 + " (current=" + intensity + ", default= " + defaultIntensity + ")");
832 return;
Michael Wright35a0c672018-01-24 00:32:53 +0000833 }
834
835 VibrationEffect scaledEffect = null;
836 if (vib.effect instanceof VibrationEffect.OneShot) {
837 VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect;
Alexey Kuzmin59efe972018-04-24 12:58:13 +0100838 oneShot = oneShot.resolve(mDefaultVibrationAmplitude);
Michael Wright0dbb5162018-05-25 15:13:36 +0100839 scaledEffect = oneShot.scale(scale.gamma, scale.maxAmplitude);
Michael Wright35a0c672018-01-24 00:32:53 +0000840 } else if (vib.effect instanceof VibrationEffect.Waveform) {
841 VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect;
Alexey Kuzmin59efe972018-04-24 12:58:13 +0100842 waveform = waveform.resolve(mDefaultVibrationAmplitude);
Michael Wright0dbb5162018-05-25 15:13:36 +0100843 scaledEffect = waveform.scale(scale.gamma, scale.maxAmplitude);
Michael Wright35a0c672018-01-24 00:32:53 +0000844 } else {
845 Slog.w(TAG, "Unable to apply intensity scaling, unknown VibrationEffect type");
846 }
847
848 if (scaledEffect != null) {
849 vib.originalEffect = vib.effect;
850 vib.effect = scaledEffect;
851 }
852 }
853
Brad Ebinger2d1c3b32016-05-12 18:05:17 -0700854 private boolean shouldVibrateForRingtone() {
Michael Wright35a0c672018-01-24 00:32:53 +0000855 AudioManager audioManager = mContext.getSystemService(AudioManager.class);
Brad Ebingerdcbdc0d2016-06-23 17:42:30 -0700856 int ringerMode = audioManager.getRingerModeInternal();
Brad Ebinger2d1c3b32016-05-12 18:05:17 -0700857 // "Also vibrate for calls" Setting in Sound
858 if (Settings.System.getInt(
859 mContext.getContentResolver(), Settings.System.VIBRATE_WHEN_RINGING, 0) != 0) {
860 return ringerMode != AudioManager.RINGER_MODE_SILENT;
861 } else {
862 return ringerMode == AudioManager.RINGER_MODE_VIBRATE;
863 }
864 }
865
Michael Wright71216972017-01-31 18:33:54 +0000866 private int getAppOpMode(Vibration vib) {
Svet Ganovf7b47252018-02-26 11:11:27 -0800867 int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE,
868 vib.usageHint, vib.uid, vib.opPkg);
869 if (mode == AppOpsManager.MODE_ALLOWED) {
870 mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, vib.uid, vib.opPkg);
Michael Wright71216972017-01-31 18:33:54 +0000871 }
872 return mode;
873 }
874
Alexey Kuzmin7f38fb12018-02-07 13:51:28 +0000875 @GuardedBy("mLock")
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800876 private void reportFinishVibrationLocked() {
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000877 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
878 try {
879 if (mCurrentVibration != null) {
Svet Ganovf7b47252018-02-26 11:11:27 -0800880 mAppOps.finishOp(AppOpsManager.OP_VIBRATE, mCurrentVibration.uid,
881 mCurrentVibration.opPkg);
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000882 unlinkVibration(mCurrentVibration);
883 mCurrentVibration = null;
884 }
885 } finally {
886 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800887 }
888 }
889
Michael Wright36d873f2018-01-08 15:54:05 +0000890 private void linkVibration(Vibration vib) {
891 // Only link against waveforms since they potentially don't have a finish if
892 // they're repeating. Let other effects just play out until they're done.
Michael Wright35a0c672018-01-24 00:32:53 +0000893 if (vib.effect instanceof VibrationEffect.Waveform) {
Michael Wright36d873f2018-01-08 15:54:05 +0000894 try {
Michael Wright35a0c672018-01-24 00:32:53 +0000895 vib.token.linkToDeath(vib, 0);
Michael Wright36d873f2018-01-08 15:54:05 +0000896 } catch (RemoteException e) {
897 return;
898 }
899 }
900 }
901
Mathias Jeppssonb23949b2010-09-28 14:45:23 +0200902 private void unlinkVibration(Vibration vib) {
Michael Wright35a0c672018-01-24 00:32:53 +0000903 if (vib.effect instanceof VibrationEffect.Waveform) {
904 vib.token.unlinkToDeath(vib, 0);
Mathias Jeppssonb23949b2010-09-28 14:45:23 +0200905 }
906 }
907
Michael Wright71216972017-01-31 18:33:54 +0000908 private void updateVibrators() {
909 synchronized (mLock) {
910 boolean devicesUpdated = updateInputDeviceVibratorsLocked();
911 boolean lowPowerModeUpdated = updateLowPowerModeLocked();
Michael Wright35a0c672018-01-24 00:32:53 +0000912 updateVibrationIntensityLocked();
Jeff Brown7f6c2312012-04-13 20:38:38 -0700913
Michael Wright71216972017-01-31 18:33:54 +0000914 if (devicesUpdated || lowPowerModeUpdated) {
915 // If the state changes out from under us then just reset.
916 doCancelVibrateLocked();
917 }
918 }
919 }
Jeff Brown82065252012-04-16 13:19:05 -0700920
Michael Wright71216972017-01-31 18:33:54 +0000921 private boolean updateInputDeviceVibratorsLocked() {
922 boolean changed = false;
923 boolean vibrateInputDevices = false;
924 try {
925 vibrateInputDevices = Settings.System.getIntForUser(
926 mContext.getContentResolver(),
927 Settings.System.VIBRATE_INPUT_DEVICES, UserHandle.USER_CURRENT) > 0;
928 } catch (SettingNotFoundException snfe) {
929 }
930 if (vibrateInputDevices != mVibrateInputDevicesSetting) {
931 changed = true;
932 mVibrateInputDevicesSetting = vibrateInputDevices;
933 }
Ruchi Kandoi13b03af2014-05-07 20:10:32 -0700934
Michael Wright71216972017-01-31 18:33:54 +0000935 if (mVibrateInputDevicesSetting) {
936 if (!mInputDeviceListenerRegistered) {
937 mInputDeviceListenerRegistered = true;
938 mIm.registerInputDeviceListener(this, mH);
939 }
940 } else {
941 if (mInputDeviceListenerRegistered) {
942 mInputDeviceListenerRegistered = false;
943 mIm.unregisterInputDeviceListener(this);
944 }
945 }
Jeff Brown82065252012-04-16 13:19:05 -0700946
Michael Wright71216972017-01-31 18:33:54 +0000947 mInputDeviceVibrators.clear();
948 if (mVibrateInputDevicesSetting) {
949 int[] ids = mIm.getInputDeviceIds();
950 for (int i = 0; i < ids.length; i++) {
951 InputDevice device = mIm.getInputDevice(ids[i]);
952 Vibrator vibrator = device.getVibrator();
953 if (vibrator.hasVibrator()) {
954 mInputDeviceVibrators.add(vibrator);
Jeff Brown7f6c2312012-04-13 20:38:38 -0700955 }
956 }
Michael Wright71216972017-01-31 18:33:54 +0000957 return true;
Jeff Brown7f6c2312012-04-13 20:38:38 -0700958 }
Michael Wright71216972017-01-31 18:33:54 +0000959 return changed;
960 }
961
962 private boolean updateLowPowerModeLocked() {
963 boolean lowPowerMode = mPowerManagerInternal
964 .getLowPowerState(ServiceType.VIBRATION).batterySaverEnabled;
965 if (lowPowerMode != mLowPowerMode) {
966 mLowPowerMode = lowPowerMode;
967 return true;
968 }
969 return false;
Jeff Brown7f6c2312012-04-13 20:38:38 -0700970 }
971
Michael Wright35a0c672018-01-24 00:32:53 +0000972 private void updateVibrationIntensityLocked() {
973 mHapticFeedbackIntensity = Settings.System.getIntForUser(mContext.getContentResolver(),
974 Settings.System.HAPTIC_FEEDBACK_INTENSITY,
975 mVibrator.getDefaultHapticFeedbackIntensity(), UserHandle.USER_CURRENT);
976 mNotificationIntensity = Settings.System.getIntForUser(mContext.getContentResolver(),
977 Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
978 mVibrator.getDefaultNotificationVibrationIntensity(), UserHandle.USER_CURRENT);
Alexey Kuzminccdaebb2018-12-10 12:02:51 +0000979 mRingIntensity = Settings.System.getIntForUser(mContext.getContentResolver(),
980 Settings.System.RING_VIBRATION_INTENSITY,
981 mVibrator.getDefaultRingVibrationIntensity(), UserHandle.USER_CURRENT);
Michael Wright35a0c672018-01-24 00:32:53 +0000982 }
983
Jeff Brown7f6c2312012-04-13 20:38:38 -0700984 @Override
985 public void onInputDeviceAdded(int deviceId) {
Michael Wright71216972017-01-31 18:33:54 +0000986 updateVibrators();
Jeff Brown7f6c2312012-04-13 20:38:38 -0700987 }
988
989 @Override
990 public void onInputDeviceChanged(int deviceId) {
Michael Wright71216972017-01-31 18:33:54 +0000991 updateVibrators();
Jeff Brown7f6c2312012-04-13 20:38:38 -0700992 }
993
994 @Override
995 public void onInputDeviceRemoved(int deviceId) {
Michael Wright71216972017-01-31 18:33:54 +0000996 updateVibrators();
Jeff Brown7f6c2312012-04-13 20:38:38 -0700997 }
998
999 private boolean doVibratorExists() {
Jeff Brown1064a502012-05-02 16:51:37 -07001000 // For now, we choose to ignore the presence of input devices that have vibrators
1001 // when reporting whether the device has a vibrator. Applications often use this
1002 // information to decide whether to enable certain features so they expect the
1003 // result of hasVibrator() to be constant. For now, just report whether
1004 // the device has a built-in vibrator.
1005 //synchronized (mInputDeviceVibrators) {
1006 // return !mInputDeviceVibrators.isEmpty() || vibratorExists();
1007 //}
Dianne Hackbornc2293022013-02-06 23:14:49 -08001008 return vibratorExists();
Jeff Brown7f6c2312012-04-13 20:38:38 -07001009 }
1010
Michael Wright71216972017-01-31 18:33:54 +00001011 private void doVibratorOn(long millis, int amplitude, int uid, int usageHint) {
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001012 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn");
1013 try {
1014 synchronized (mInputDeviceVibrators) {
1015 if (amplitude == VibrationEffect.DEFAULT_AMPLITUDE) {
1016 amplitude = mDefaultVibrationAmplitude;
Jeff Brown7f6c2312012-04-13 20:38:38 -07001017 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001018 if (DEBUG) {
1019 Slog.d(TAG, "Turning vibrator on for " + millis + " ms" +
1020 " with amplitude " + amplitude + ".");
1021 }
1022 noteVibratorOnLocked(uid, millis);
1023 final int vibratorCount = mInputDeviceVibrators.size();
1024 if (vibratorCount != 0) {
1025 final AudioAttributes attributes =
1026 new AudioAttributes.Builder().setUsage(usageHint).build();
1027 for (int i = 0; i < vibratorCount; i++) {
1028 mInputDeviceVibrators.get(i).vibrate(millis, attributes);
1029 }
1030 } else {
1031 // Note: ordering is important here! Many haptic drivers will reset their
1032 // amplitude when enabled, so we always have to enable frst, then set the
1033 // amplitude.
1034 vibratorOn(millis);
1035 doVibratorSetAmplitude(amplitude);
1036 }
Jeff Brown7f6c2312012-04-13 20:38:38 -07001037 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001038 } finally {
1039 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
Jeff Brown7f6c2312012-04-13 20:38:38 -07001040 }
1041 }
1042
Michael Wright71216972017-01-31 18:33:54 +00001043 private void doVibratorSetAmplitude(int amplitude) {
1044 if (mSupportsAmplitudeControl) {
1045 vibratorSetAmplitude(amplitude);
1046 }
1047 }
1048
Jeff Brown7f6c2312012-04-13 20:38:38 -07001049 private void doVibratorOff() {
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001050 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOff");
1051 try {
1052 synchronized (mInputDeviceVibrators) {
1053 if (DEBUG) {
1054 Slog.d(TAG, "Turning vibrator off.");
Jeff Brown7f6c2312012-04-13 20:38:38 -07001055 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001056 noteVibratorOffLocked();
1057 final int vibratorCount = mInputDeviceVibrators.size();
1058 if (vibratorCount != 0) {
1059 for (int i = 0; i < vibratorCount; i++) {
1060 mInputDeviceVibrators.get(i).cancel();
1061 }
1062 } else {
1063 vibratorOff();
1064 }
Jeff Brown7f6c2312012-04-13 20:38:38 -07001065 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001066 } finally {
1067 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
Jeff Brown7f6c2312012-04-13 20:38:38 -07001068 }
1069 }
1070
Alexey Kuzmin7f38fb12018-02-07 13:51:28 +00001071 @GuardedBy("mLock")
Michael Wright71216972017-01-31 18:33:54 +00001072 private long doVibratorPrebakedEffectLocked(Vibration vib) {
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001073 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorPrebakedEffectLocked");
1074 try {
1075 final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect;
1076 final boolean usingInputDeviceVibrators;
1077 synchronized (mInputDeviceVibrators) {
1078 usingInputDeviceVibrators = !mInputDeviceVibrators.isEmpty();
Michael Wright35a0c672018-01-24 00:32:53 +00001079 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001080 // Input devices don't support prebaked effect, so skip trying it with them.
1081 if (!usingInputDeviceVibrators) {
1082 long timeout = vibratorPerformEffect(prebaked.getId(),
1083 prebaked.getEffectStrength());
1084 if (timeout > 0) {
1085 noteVibratorOnLocked(vib.uid, timeout);
1086 return timeout;
1087 }
1088 }
1089 if (!prebaked.shouldFallback()) {
1090 return 0;
1091 }
1092 VibrationEffect effect = getFallbackEffect(prebaked.getId());
1093 if (effect == null) {
1094 Slog.w(TAG, "Failed to play prebaked effect, no fallback");
1095 return 0;
1096 }
Alexey Kuzmine1f06b82018-06-20 17:48:43 +01001097 Vibration fallbackVib = new Vibration(vib.token, effect, vib.usageHint, vib.uid,
1098 vib.opPkg, vib.reason + " (fallback)");
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001099 final int intensity = getCurrentIntensityLocked(fallbackVib);
1100 linkVibration(fallbackVib);
1101 applyVibrationIntensityScalingLocked(fallbackVib, intensity);
1102 startVibrationInnerLocked(fallbackVib);
Michael Wright35a0c672018-01-24 00:32:53 +00001103 return 0;
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001104 } finally {
1105 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
Michael Wright35a0c672018-01-24 00:32:53 +00001106 }
Michael Wright71216972017-01-31 18:33:54 +00001107 }
Eric Olsenf42f15c2009-10-29 16:42:03 -07001108
Michael Wright36d873f2018-01-08 15:54:05 +00001109 private VibrationEffect getFallbackEffect(int effectId) {
Alexey Kuzmin5a0a26f2018-03-20 18:25:51 +00001110 return mFallbackEffects.get(effectId);
Michael Wright36d873f2018-01-08 15:54:05 +00001111 }
1112
Michael Wright35a0c672018-01-24 00:32:53 +00001113 /**
1114 * Return the current desired effect strength.
1115 *
1116 * If the returned value is &lt; 0 then the vibration shouldn't be played at all.
1117 */
1118 private static int intensityToEffectStrength(int intensity) {
1119 switch (intensity) {
1120 case Vibrator.VIBRATION_INTENSITY_LOW:
1121 return EffectStrength.LIGHT;
1122 case Vibrator.VIBRATION_INTENSITY_MEDIUM:
1123 return EffectStrength.MEDIUM;
1124 case Vibrator.VIBRATION_INTENSITY_HIGH:
1125 return EffectStrength.STRONG;
1126 default:
1127 Slog.w(TAG, "Got unexpected vibration intensity: " + intensity);
1128 return EffectStrength.STRONG;
1129 }
1130 }
1131
Michael Wrightc390fbe2018-12-12 19:45:09 +00001132 private static boolean isNotification(int usageHint) {
1133 switch (usageHint) {
1134 case AudioAttributes.USAGE_NOTIFICATION:
1135 case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
1136 case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
1137 case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
1138 return true;
1139 default:
1140 return false;
1141 }
1142 }
1143
1144 private static boolean isRingtone(int usageHint) {
1145 return usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
1146 }
1147
1148 private static boolean isHapticFeedback(int usageHint) {
1149 return usageHint == AudioAttributes.USAGE_ASSISTANCE_SONIFICATION;
1150 }
1151
Michael Wright71216972017-01-31 18:33:54 +00001152 private void noteVibratorOnLocked(int uid, long millis) {
1153 try {
1154 mBatteryStatsService.noteVibratorOn(uid, millis);
Bookatza7020bd2018-08-28 16:29:35 -07001155 StatsLog.write_non_chained(StatsLog.VIBRATOR_STATE_CHANGED, uid, null,
1156 StatsLog.VIBRATOR_STATE_CHANGED__STATE__ON, millis);
Michael Wright71216972017-01-31 18:33:54 +00001157 mCurVibUid = uid;
1158 } catch (RemoteException e) {
1159 }
1160 }
1161
1162 private void noteVibratorOffLocked() {
1163 if (mCurVibUid >= 0) {
1164 try {
1165 mBatteryStatsService.noteVibratorOff(mCurVibUid);
Bookatza7020bd2018-08-28 16:29:35 -07001166 StatsLog.write_non_chained(StatsLog.VIBRATOR_STATE_CHANGED, mCurVibUid, null,
1167 StatsLog.VIBRATOR_STATE_CHANGED__STATE__OFF, 0);
Michael Wright71216972017-01-31 18:33:54 +00001168 } catch (RemoteException e) { }
1169 mCurVibUid = -1;
1170 }
1171 }
1172
Michael Wrightc390fbe2018-12-12 19:45:09 +00001173 private void setVibratorUnderExternalControl(boolean externalControl) {
1174 if (DEBUG) {
1175 if (externalControl) {
1176 Slog.d(TAG, "Vibrator going under external control.");
1177 } else {
1178 Slog.d(TAG, "Taking back control of vibrator.");
1179 }
1180 }
1181 mVibratorUnderExternalControl = externalControl;
1182 vibratorSetExternalControl(externalControl);
1183 }
1184
Michael Wright71216972017-01-31 18:33:54 +00001185 private class VibrateThread extends Thread {
1186 private final VibrationEffect.Waveform mWaveform;
1187 private final int mUid;
1188 private final int mUsageHint;
1189
1190 private boolean mForceStop;
1191
1192 VibrateThread(VibrationEffect.Waveform waveform, int uid, int usageHint) {
1193 mWaveform = waveform;
1194 mUid = uid;
1195 mUsageHint = usageHint;
1196 mTmpWorkSource.set(uid);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001197 mWakeLock.setWorkSource(mTmpWorkSource);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001198 }
1199
Michael Wright71216972017-01-31 18:33:54 +00001200 private long delayLocked(long duration) {
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001201 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "delayLocked");
1202 try {
1203 long durationRemaining = duration;
1204 if (duration > 0) {
1205 final long bedtime = duration + SystemClock.uptimeMillis();
1206 do {
1207 try {
1208 this.wait(durationRemaining);
1209 }
1210 catch (InterruptedException e) { }
1211 if (mForceStop) {
1212 break;
1213 }
1214 durationRemaining = bedtime - SystemClock.uptimeMillis();
1215 } while (durationRemaining > 0);
1216 return duration - durationRemaining;
1217 }
1218 return 0;
1219 } finally {
1220 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001221 }
1222 }
1223
1224 public void run() {
1225 Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
Michael Wright71216972017-01-31 18:33:54 +00001226 mWakeLock.acquire();
1227 try {
1228 boolean finished = playWaveform();
1229 if (finished) {
1230 onVibrationFinished();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001231 }
Michael Wright71216972017-01-31 18:33:54 +00001232 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001233 mWakeLock.release();
1234 }
Michael Wright71216972017-01-31 18:33:54 +00001235 }
1236
1237 /**
1238 * Play the waveform.
1239 *
1240 * @return true if it finished naturally, false otherwise (e.g. it was canceled).
1241 */
1242 public boolean playWaveform() {
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001243 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playWaveform");
1244 try {
1245 synchronized (this) {
1246 final long[] timings = mWaveform.getTimings();
1247 final int[] amplitudes = mWaveform.getAmplitudes();
1248 final int len = timings.length;
1249 final int repeat = mWaveform.getRepeatIndex();
Michael Wright71216972017-01-31 18:33:54 +00001250
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001251 int index = 0;
1252 long onDuration = 0;
1253 while (!mForceStop) {
1254 if (index < len) {
1255 final int amplitude = amplitudes[index];
1256 final long duration = timings[index++];
1257 if (duration <= 0) {
1258 continue;
Michael Wright71216972017-01-31 18:33:54 +00001259 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001260 if (amplitude != 0) {
1261 if (onDuration <= 0) {
1262 // Telling the vibrator to start multiple times usually causes
1263 // effects to feel "choppy" because the motor resets at every on
1264 // command. Instead we figure out how long our next "on" period
1265 // is going to be, tell the motor to stay on for the full
1266 // duration, and then wake up to change the amplitude at the
1267 // appropriate intervals.
1268 onDuration = getTotalOnDuration(timings, amplitudes, index - 1,
1269 repeat);
1270 doVibratorOn(onDuration, amplitude, mUid, mUsageHint);
1271 } else {
1272 doVibratorSetAmplitude(amplitude);
1273 }
1274 }
Michael Wright71216972017-01-31 18:33:54 +00001275
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001276 long waitTime = delayLocked(duration);
1277 if (amplitude != 0) {
1278 onDuration -= waitTime;
1279 }
1280 } else if (repeat < 0) {
1281 break;
1282 } else {
1283 index = repeat;
Michael Wright71216972017-01-31 18:33:54 +00001284 }
Michael Wright71216972017-01-31 18:33:54 +00001285 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001286 return !mForceStop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001287 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001288 } finally {
1289 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
Michael Wright71216972017-01-31 18:33:54 +00001290 }
1291 }
1292
1293 public void cancel() {
1294 synchronized (this) {
1295 mThread.mForceStop = true;
1296 mThread.notify();
1297 }
1298 }
1299
1300 /**
1301 * Get the duration the vibrator will be on starting at startIndex until the next time it's
1302 * off.
1303 */
1304 private long getTotalOnDuration(
1305 long[] timings, int[] amplitudes, int startIndex, int repeatIndex) {
1306 int i = startIndex;
1307 long timing = 0;
1308 while(amplitudes[i] != 0) {
1309 timing += timings[i++];
1310 if (i >= timings.length) {
1311 if (repeatIndex >= 0) {
1312 i = repeatIndex;
Harpreet "Eli" Sanghac5526dd2018-10-30 16:14:33 +09001313 // prevent infinite loop
1314 repeatIndex = -1;
Michael Wright71216972017-01-31 18:33:54 +00001315 } else {
1316 break;
1317 }
1318 }
1319 if (i == startIndex) {
1320 return 1000;
Patrick Scott18dd5f02009-07-02 11:31:12 -04001321 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001322 }
Michael Wright71216972017-01-31 18:33:54 +00001323 return timing;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001324 }
Jeff Brown969579b2014-05-20 19:29:29 -07001325 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001326
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001327 BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
Jeff Brown969579b2014-05-20 19:29:29 -07001328 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001329 public void onReceive(Context context, Intent intent) {
1330 if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
Michael Wright71216972017-01-31 18:33:54 +00001331 synchronized (mLock) {
Jeff Brown969579b2014-05-20 19:29:29 -07001332 // When the system is entering a non-interactive state, we want
1333 // to cancel vibrations in case a misbehaving app has abandoned
1334 // them. However it may happen that the system is currently playing
1335 // haptic feedback as part of the transition. So we don't cancel
1336 // system vibrations.
1337 if (mCurrentVibration != null
Michael Wright35a0c672018-01-24 00:32:53 +00001338 && !(mCurrentVibration.isHapticFeedback()
1339 && mCurrentVibration.isFromSystem())) {
Jeff Brown969579b2014-05-20 19:29:29 -07001340 doCancelVibrateLocked();
Vairavan Srinivasan8a61f492011-05-13 10:47:20 -07001341 }
Patrick Scott18dd5f02009-07-02 11:31:12 -04001342 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001343 }
1344 }
1345 };
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -07001346
1347 @Override
1348 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -06001349 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -07001350
Michael Wright35a0c672018-01-24 00:32:53 +00001351 pw.println("Vibrator Service:");
Michael Wright71216972017-01-31 18:33:54 +00001352 synchronized (mLock) {
Michael Wright35a0c672018-01-24 00:32:53 +00001353 pw.print(" mCurrentVibration=");
1354 if (mCurrentVibration != null) {
1355 pw.println(mCurrentVibration.toInfo().toString());
1356 } else {
1357 pw.println("null");
1358 }
Michael Wrightc390fbe2018-12-12 19:45:09 +00001359 pw.print(" mCurrentExternalVibration=");
1360 if (mCurrentExternalVibration != null) {
1361 pw.println(mCurrentExternalVibration.toString());
1362 } else {
1363 pw.println("null");
1364 }
1365 pw.println(" mVibratorUnderExternalControl=" + mVibratorUnderExternalControl);
Michael Wright35a0c672018-01-24 00:32:53 +00001366 pw.println(" mLowPowerMode=" + mLowPowerMode);
1367 pw.println(" mHapticFeedbackIntensity=" + mHapticFeedbackIntensity);
1368 pw.println(" mNotificationIntensity=" + mNotificationIntensity);
Alexey Kuzminccdaebb2018-12-10 12:02:51 +00001369 pw.println(" mRingIntensity=" + mRingIntensity);
Michael Wright35a0c672018-01-24 00:32:53 +00001370 pw.println("");
Arthur Hung85ed7da2019-03-05 13:58:19 +08001371 pw.println(" Previous ring vibrations:");
1372 for (VibrationInfo info : mPreviousRingVibrations) {
1373 pw.print(" ");
1374 pw.println(info.toString());
1375 }
1376
1377 pw.println(" Previous notification vibrations:");
1378 for (VibrationInfo info : mPreviousNotificationVibrations) {
1379 pw.print(" ");
1380 pw.println(info.toString());
1381 }
1382
Michael Wright35a0c672018-01-24 00:32:53 +00001383 pw.println(" Previous vibrations:");
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -07001384 for (VibrationInfo info : mPreviousVibrations) {
Michael Wright35a0c672018-01-24 00:32:53 +00001385 pw.print(" ");
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -07001386 pw.println(info.toString());
1387 }
1388 }
1389 }
Felipe Lemea5281002017-02-10 15:13:48 -08001390
1391 @Override
1392 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
1393 String[] args, ShellCallback callback, ResultReceiver resultReceiver)
1394 throws RemoteException {
1395 new VibratorShellCommand(this).exec(this, in, out, err, args, callback, resultReceiver);
1396 }
1397
Michael Wrightc390fbe2018-12-12 19:45:09 +00001398 final class ExternalVibratorService extends IExternalVibratorService.Stub {
1399 @Override
1400 public int onExternalVibrationStart(ExternalVibration vib) {
1401 if (!mSupportsExternalControl) {
1402 return SCALE_MUTE;
1403 }
1404 if (ActivityManager.checkComponentPermission(android.Manifest.permission.VIBRATE,
1405 vib.getUid(), -1 /*owningUid*/, true /*exported*/)
1406 != PackageManager.PERMISSION_GRANTED) {
1407 Slog.w(TAG, "pkg=" + vib.getPackage() + ", uid=" + vib.getUid()
1408 + " tried to play externally controlled vibration"
1409 + " without VIBRATE permission, ignoring.");
1410 return SCALE_MUTE;
1411 }
1412
1413 final int scaleLevel;
1414 synchronized (mLock) {
1415 if (!vib.equals(mCurrentExternalVibration)) {
1416 if (mCurrentExternalVibration == null) {
1417 // If we're not under external control right now, then cancel any normal
1418 // vibration that may be playing and ready the vibrator for external
1419 // control.
1420 doCancelVibrateLocked();
1421 setVibratorUnderExternalControl(true);
1422 }
1423 // At this point we either have an externally controlled vibration playing, or
1424 // no vibration playing. Since the interface defines that only one externally
1425 // controlled vibration can play at a time, by returning something other than
1426 // SCALE_MUTE from this function we can be assured that if we are currently
1427 // playing vibration, it will be muted in favor of the new vibration.
1428 //
1429 // Note that this doesn't support multiple concurrent external controls, as we
1430 // would need to mute the old one still if it came from a different controller.
1431 mCurrentExternalVibration = vib;
1432 if (DEBUG) {
1433 Slog.e(TAG, "Playing external vibration: " + vib);
1434 }
1435 }
1436 final int usage = vib.getAudioAttributes().getUsage();
1437 final int defaultIntensity;
1438 final int currentIntensity;
1439 if (isRingtone(usage)) {
1440 defaultIntensity = mVibrator.getDefaultRingVibrationIntensity();
1441 currentIntensity = mRingIntensity;
1442 } else if (isNotification(usage)) {
1443 defaultIntensity = mVibrator.getDefaultNotificationVibrationIntensity();
1444 currentIntensity = mNotificationIntensity;
1445 } else if (isHapticFeedback(usage)) {
1446 defaultIntensity = mVibrator.getDefaultHapticFeedbackIntensity();
1447 currentIntensity = mHapticFeedbackIntensity;
1448 } else {
1449 defaultIntensity = 0;
1450 currentIntensity = 0;
1451 }
1452 scaleLevel = currentIntensity - defaultIntensity;
1453 }
1454 if (scaleLevel >= SCALE_VERY_LOW && scaleLevel <= SCALE_VERY_HIGH) {
1455 return scaleLevel;
1456 } else {
1457 // Presumably we want to play this but something about our scaling has gone
1458 // wrong, so just play with no scaling.
1459 Slog.w(TAG, "Error in scaling calculations, ended up with invalid scale level "
1460 + scaleLevel + " for vibration " + vib);
1461 return SCALE_NONE;
1462 }
1463 }
1464
1465 @Override
1466 public void onExternalVibrationStop(ExternalVibration vib) {
1467 synchronized (mLock) {
1468 if (vib.equals(mCurrentExternalVibration)) {
1469 mCurrentExternalVibration = null;
1470 setVibratorUnderExternalControl(false);
1471 if (DEBUG) {
1472 Slog.e(TAG, "Stopping external vibration" + vib);
1473 }
1474 }
1475 }
1476 }
1477 }
1478
Felipe Lemea5281002017-02-10 15:13:48 -08001479 private final class VibratorShellCommand extends ShellCommand {
1480
Felipe Lemea5281002017-02-10 15:13:48 -08001481 private final IBinder mToken;
1482
Harpreet "Eli" Sanghaf32459a2019-01-30 16:06:09 +09001483 private final class CommonOptions {
1484 public boolean force = false;
1485 public void check(String opt) {
1486 switch (opt) {
1487 case "-f":
1488 force = true;
1489 break;
1490 }
1491 }
1492 }
1493
Felipe Lemea5281002017-02-10 15:13:48 -08001494 private VibratorShellCommand(IBinder token) {
1495 mToken = token;
1496 }
1497
1498 @Override
1499 public int onCommand(String cmd) {
1500 if ("vibrate".equals(cmd)) {
1501 return runVibrate();
Harpreet "Eli" Sanghac5526dd2018-10-30 16:14:33 +09001502 } else if ("waveform".equals(cmd)) {
1503 return runWaveform();
Harpreet "Eli" Sangha5ecc5362018-10-16 17:27:58 +09001504 } else if ("prebaked".equals(cmd)) {
1505 return runPrebaked();
Harpreet "Eli" Sanghac5526dd2018-10-30 16:14:33 +09001506 } else if ("cancel".equals(cmd)) {
1507 cancelVibrate(mToken);
1508 return 0;
Felipe Lemea5281002017-02-10 15:13:48 -08001509 }
1510 return handleDefaultCommands(cmd);
1511 }
1512
Harpreet "Eli" Sanghaf32459a2019-01-30 16:06:09 +09001513 private boolean checkDoNotDisturb(CommonOptions opts) {
Harpreet "Eli" Sangha5ecc5362018-10-16 17:27:58 +09001514 try {
1515 final int zenMode = Settings.Global.getInt(mContext.getContentResolver(),
1516 Settings.Global.ZEN_MODE);
Harpreet "Eli" Sanghaf32459a2019-01-30 16:06:09 +09001517 if (zenMode != Settings.Global.ZEN_MODE_OFF && !opts.force) {
Harpreet "Eli" Sangha5ecc5362018-10-16 17:27:58 +09001518 try (PrintWriter pw = getOutPrintWriter();) {
1519 pw.print("Ignoring because device is on DND mode ");
1520 pw.println(DebugUtils.flagsToString(Settings.Global.class, "ZEN_MODE_",
1521 zenMode));
1522 return true;
1523 }
1524 }
1525 } catch (SettingNotFoundException e) {
1526 // ignore
1527 }
1528
1529 return false;
1530 }
1531
Felipe Lemea5281002017-02-10 15:13:48 -08001532 private int runVibrate() {
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001533 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runVibrate");
Felipe Leme5e2e6322017-07-14 17:25:59 -07001534 try {
Harpreet "Eli" Sanghaf32459a2019-01-30 16:06:09 +09001535 CommonOptions commonOptions = new CommonOptions();
1536
1537 String opt;
1538 while ((opt = getNextOption()) != null) {
1539 commonOptions.check(opt);
1540 }
1541
1542 if (checkDoNotDisturb(commonOptions)) {
Harpreet "Eli" Sangha5ecc5362018-10-16 17:27:58 +09001543 return 0;
Felipe Leme5e2e6322017-07-14 17:25:59 -07001544 }
Felipe Leme5e2e6322017-07-14 17:25:59 -07001545
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001546 final long duration = Long.parseLong(getNextArgRequired());
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001547 String description = getNextArg();
1548 if (description == null) {
1549 description = "Shell command";
1550 }
Michael Wright71216972017-01-31 18:33:54 +00001551
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001552 VibrationEffect effect =
1553 VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE);
1554 vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
Alexey Kuzmine1f06b82018-06-20 17:48:43 +01001555 "Shell Command", mToken);
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001556 return 0;
1557 } finally {
1558 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
1559 }
Felipe Lemea5281002017-02-10 15:13:48 -08001560 }
1561
Harpreet "Eli" Sanghac5526dd2018-10-30 16:14:33 +09001562 private int runWaveform() {
1563 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runWaveform");
1564 try {
Harpreet "Eli" Sanghac5526dd2018-10-30 16:14:33 +09001565 String description = "Shell command";
1566 int repeat = -1;
1567 ArrayList<Integer> amplitudesList = null;
Harpreet "Eli" Sanghaf32459a2019-01-30 16:06:09 +09001568 CommonOptions commonOptions = new CommonOptions();
Harpreet "Eli" Sanghac5526dd2018-10-30 16:14:33 +09001569
1570 String opt;
1571 while ((opt = getNextOption()) != null) {
1572 switch (opt) {
1573 case "-d":
1574 description = getNextArgRequired();
1575 break;
1576 case "-r":
1577 repeat = Integer.parseInt(getNextArgRequired());
1578 break;
1579 case "-a":
1580 if (amplitudesList == null) {
1581 amplitudesList = new ArrayList<Integer>();
1582 }
1583 break;
Harpreet "Eli" Sanghaf32459a2019-01-30 16:06:09 +09001584 default:
1585 commonOptions.check(opt);
1586 break;
Harpreet "Eli" Sanghac5526dd2018-10-30 16:14:33 +09001587 }
1588 }
1589
Harpreet "Eli" Sanghaf32459a2019-01-30 16:06:09 +09001590 if (checkDoNotDisturb(commonOptions)) {
1591 return 0;
1592 }
1593
Harpreet "Eli" Sanghac5526dd2018-10-30 16:14:33 +09001594 ArrayList<Long> timingsList = new ArrayList<Long>();
1595
1596 String arg;
1597 while ((arg = getNextArg()) != null) {
1598 if (amplitudesList != null && amplitudesList.size() < timingsList.size()) {
1599 amplitudesList.add(Integer.parseInt(arg));
1600 } else {
1601 timingsList.add(Long.parseLong(arg));
1602 }
1603 }
1604
1605 VibrationEffect effect;
1606 long[] timings = timingsList.stream().mapToLong(Long::longValue).toArray();
1607 if (amplitudesList == null) {
1608 effect = VibrationEffect.createWaveform(timings, repeat);
1609 } else {
1610 int[] amplitudes =
1611 amplitudesList.stream().mapToInt(Integer::intValue).toArray();
1612 effect = VibrationEffect.createWaveform(timings, amplitudes, repeat);
1613 }
1614 vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
1615 "Shell Command", mToken);
1616 return 0;
1617 } finally {
1618 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
1619 }
1620 }
1621
Harpreet "Eli" Sangha5ecc5362018-10-16 17:27:58 +09001622 private int runPrebaked() {
1623 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runPrebaked");
1624 try {
Harpreet "Eli" Sanghaf32459a2019-01-30 16:06:09 +09001625 CommonOptions commonOptions = new CommonOptions();
1626
1627 String opt;
1628 while ((opt = getNextOption()) != null) {
1629 commonOptions.check(opt);
1630 }
1631
1632 if (checkDoNotDisturb(commonOptions)) {
Harpreet "Eli" Sangha5ecc5362018-10-16 17:27:58 +09001633 return 0;
1634 }
1635
1636 final int id = Integer.parseInt(getNextArgRequired());
1637
1638 String description = getNextArg();
1639 if (description == null) {
1640 description = "Shell command";
1641 }
1642
1643 VibrationEffect effect =
1644 VibrationEffect.get(id, false);
1645 vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
1646 "Shell Command", mToken);
1647 return 0;
1648 } finally {
1649 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
1650 }
1651 }
1652
Felipe Lemea5281002017-02-10 15:13:48 -08001653 @Override
1654 public void onHelp() {
1655 try (PrintWriter pw = getOutPrintWriter();) {
1656 pw.println("Vibrator commands:");
1657 pw.println(" help");
1658 pw.println(" Prints this help text.");
1659 pw.println("");
1660 pw.println(" vibrate duration [description]");
Felipe Leme5e2e6322017-07-14 17:25:59 -07001661 pw.println(" Vibrates for duration milliseconds; ignored when device is on DND ");
1662 pw.println(" (Do Not Disturb) mode.");
Harpreet "Eli" Sanghac5526dd2018-10-30 16:14:33 +09001663 pw.println(" waveform [-d description] [-r index] [-a] duration [amplitude] ...");
1664 pw.println(" Vibrates for durations and amplitudes in list;");
1665 pw.println(" ignored when device is on DND (Do Not Disturb) mode.");
1666 pw.println(" If -r is provided, the waveform loops back to the specified");
1667 pw.println(" index (e.g. 0 loops from the beginning)");
1668 pw.println(" If -a is provided, the command accepts duration-amplitude pairs;");
1669 pw.println(" otherwise, it accepts durations only and alternates off/on");
1670 pw.println(" Duration is in milliseconds; amplitude is a scale of 1-255.");
Harpreet "Eli" Sangha5ecc5362018-10-16 17:27:58 +09001671 pw.println(" prebaked effect-id [description]");
1672 pw.println(" Vibrates with prebaked effect; ignored when device is on DND ");
1673 pw.println(" (Do Not Disturb) mode.");
Harpreet "Eli" Sanghac5526dd2018-10-30 16:14:33 +09001674 pw.println(" cancel");
1675 pw.println(" Cancels any active vibration");
Harpreet "Eli" Sanghaf32459a2019-01-30 16:06:09 +09001676 pw.println("Common Options:");
1677 pw.println(" -f - Force. Ignore Do Not Disturb setting.");
Felipe Lemea5281002017-02-10 15:13:48 -08001678 pw.println("");
1679 }
1680 }
1681 }
1682
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001683}