blob: 9f353a80a5be970ed97471735bb0c2130b55f434 [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;
Joe Onorato95e4f702009-03-24 19:29:09 -070036import android.os.Handler;
Bookatza7020bd2018-08-28 16:29:35 -070037import android.os.IBinder;
Mike Lockwood3a322132009-11-24 00:30:52 -050038import android.os.IVibratorService;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.os.PowerManager;
Bookatza7020bd2018-08-28 16:29:35 -070040import android.os.PowerManager.ServiceType;
Dianne Hackborneb94fa72014-06-03 17:48:12 -070041import android.os.PowerManagerInternal;
Bookatza7020bd2018-08-28 16:29:35 -070042import android.os.PowerSaveState;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.os.Process;
44import android.os.RemoteException;
Felipe Lemea5281002017-02-10 15:13:48 -080045import android.os.ResultReceiver;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080046import android.os.ServiceManager;
Felipe Lemea5281002017-02-10 15:13:48 -080047import android.os.ShellCallback;
48import android.os.ShellCommand;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.os.SystemClock;
Alexey Kuzmine59145a2018-02-10 15:19:03 +000050import android.os.Trace;
Jeff Brownd4935962012-09-25 13:27:20 -070051import android.os.UserHandle;
Michael Wright71216972017-01-31 18:33:54 +000052import android.os.VibrationEffect;
Bookatza7020bd2018-08-28 16:29:35 -070053import android.os.Vibrator;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070054import android.os.WorkSource;
Jeff Brown7f6c2312012-04-13 20:38:38 -070055import android.provider.Settings;
56import android.provider.Settings.SettingNotFoundException;
Felipe Leme5e2e6322017-07-14 17:25:59 -070057import android.util.DebugUtils;
Joe Onorato8a9b2202010-02-26 18:56:32 -080058import android.util.Slog;
Alexey Kuzmin5a0a26f2018-03-20 18:25:51 +000059import android.util.SparseArray;
Bookatza7020bd2018-08-28 16:29:35 -070060import android.util.StatsLog;
Jeff Brown7f6c2312012-04-13 20:38:38 -070061import android.view.InputDevice;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062
Alexey Kuzmin7f38fb12018-02-07 13:51:28 +000063import com.android.internal.annotations.GuardedBy;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080064import com.android.internal.app.IBatteryStats;
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060065import com.android.internal.util.DumpUtils;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080066
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -070067import java.io.FileDescriptor;
68import java.io.PrintWriter;
Jeff Brown7f6c2312012-04-13 20:38:38 -070069import java.util.ArrayList;
Michael Wright36d873f2018-01-08 15:54:05 +000070import java.util.Date;
Bookatza7020bd2018-08-28 16:29:35 -070071import java.util.LinkedList;
Patrick Scott18dd5f02009-07-02 11:31:12 -040072
Jeff Brown7f6c2312012-04-13 20:38:38 -070073public class VibratorService extends IVibratorService.Stub
74 implements InputManager.InputDeviceListener {
Mike Lockwood3a322132009-11-24 00:30:52 -050075 private static final String TAG = "VibratorService";
Jeff Brown82379ba2014-07-25 19:03:28 -070076 private static final boolean DEBUG = false;
Jorim Jaggi18f18ae2015-09-10 15:48:21 -070077 private static final String SYSTEM_UI_PACKAGE = "com.android.systemui";
Mike Lockwoodcc9a63d2009-11-10 07:50:28 -050078
Michael Wright36d873f2018-01-08 15:54:05 +000079 private static final long[] DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS = { 0, 30, 100, 30 };
80
Michael Wright0dbb5162018-05-25 15:13:36 +010081 // Scale levels. Each level is defined as the delta between the current setting and the default
82 // intensity for that type of vibration (i.e. current - default).
83 private static final int SCALE_VERY_LOW = -2;
84 private static final int SCALE_LOW = -1;
85 private static final int SCALE_NONE = 0;
86 private static final int SCALE_HIGH = 1;
87 private static final int SCALE_VERY_HIGH = 2;
Michael Wright35a0c672018-01-24 00:32:53 +000088
Michael Wright0dbb5162018-05-25 15:13:36 +010089 // Gamma adjustments for scale levels.
90 private static final float SCALE_VERY_LOW_GAMMA = 2.0f;
91 private static final float SCALE_LOW_GAMMA = 1.5f;
92 private static final float SCALE_NONE_GAMMA = 1.0f;
93 private static final float SCALE_HIGH_GAMMA = 0.5f;
94 private static final float SCALE_VERY_HIGH_GAMMA = 0.25f;
95
96 // Max amplitudes for scale levels. If one is not listed, then the max amplitude is the default
97 // max amplitude.
98 private static final int SCALE_VERY_LOW_MAX_AMPLITUDE = 168; // 2/3 * 255
99 private static final int SCALE_LOW_MAX_AMPLITUDE = 192; // 3/4 * 255
Michael Wright35a0c672018-01-24 00:32:53 +0000100
101 // If a vibration is playing for longer than 5s, it's probably not haptic feedback.
102 private static final long MAX_HAPTIC_FEEDBACK_DURATION = 5000;
103
Michael Wright0dbb5162018-05-25 15:13:36 +0100104
105 // A mapping from the intensity adjustment to the scaling to apply, where the intensity
106 // adjustment is defined as the delta between the default intensity level and the user selected
107 // intensity level. It's important that we apply the scaling on the delta between the two so
108 // that the default intensity level applies no scaling to application provided effects.
109 private final SparseArray<ScaleLevel> mScaleLevels;
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700110 private final LinkedList<VibrationInfo> mPreviousVibrations;
111 private final int mPreviousVibrationsLimit;
Tyler Freeman319a34a2017-05-04 17:23:35 -0700112 private final boolean mAllowPriorityVibrationsInLowPowerMode;
Michael Wright71216972017-01-31 18:33:54 +0000113 private final boolean mSupportsAmplitudeControl;
114 private final int mDefaultVibrationAmplitude;
Alexey Kuzmin5a0a26f2018-03-20 18:25:51 +0000115 private final SparseArray<VibrationEffect> mFallbackEffects;
Alexey Kuzmin3fe97b02018-12-12 14:21:55 +0000116 private final SparseArray<Integer> mProcStatesCache = new SparseArray();
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700117 private final WorkSource mTmpWorkSource = new WorkSource();
Jeff Brown7f6c2312012-04-13 20:38:38 -0700118 private final Handler mH = new Handler();
Michael Wright71216972017-01-31 18:33:54 +0000119 private final Object mLock = new Object();
Jeff Brown7f6c2312012-04-13 20:38:38 -0700120
121 private final Context mContext;
122 private final PowerManager.WakeLock mWakeLock;
Svet Ganovf7b47252018-02-26 11:11:27 -0800123 private final AppOpsManager mAppOps;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800124 private final IBatteryStats mBatteryStatsService;
Dianne Hackborneb94fa72014-06-03 17:48:12 -0700125 private PowerManagerInternal mPowerManagerInternal;
Jeff Brown7f6c2312012-04-13 20:38:38 -0700126 private InputManager mIm;
Michael Wright35a0c672018-01-24 00:32:53 +0000127 private Vibrator mVibrator;
128 private SettingsObserver mSettingObserver;
Jeff Brown7f6c2312012-04-13 20:38:38 -0700129
Michael Wright71216972017-01-31 18:33:54 +0000130 private volatile VibrateThread mThread;
Jeff Brown7f6c2312012-04-13 20:38:38 -0700131
Michael Wright71216972017-01-31 18:33:54 +0000132 // mInputDeviceVibrators lock should be acquired after mLock, if both are
Jeff Brown7f6c2312012-04-13 20:38:38 -0700133 // to be acquired
134 private final ArrayList<Vibrator> mInputDeviceVibrators = new ArrayList<Vibrator>();
135 private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators
136 private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators
137
Alexey Kuzmin7f38fb12018-02-07 13:51:28 +0000138 @GuardedBy("mLock")
Michael Wright71216972017-01-31 18:33:54 +0000139 private Vibration mCurrentVibration;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800140 private int mCurVibUid = -1;
Ruchi Kandoi13b03af2014-05-07 20:10:32 -0700141 private boolean mLowPowerMode;
Michael Wright35a0c672018-01-24 00:32:53 +0000142 private int mHapticFeedbackIntensity;
143 private int mNotificationIntensity;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800144
Jeff Brown7f6c2312012-04-13 20:38:38 -0700145 native static boolean vibratorExists();
Vincent Beckere6904fb2012-08-10 14:17:33 +0200146 native static void vibratorInit();
Jeff Brown7f6c2312012-04-13 20:38:38 -0700147 native static void vibratorOn(long milliseconds);
148 native static void vibratorOff();
Michael Wright71216972017-01-31 18:33:54 +0000149 native static boolean vibratorSupportsAmplitudeControl();
150 native static void vibratorSetAmplitude(int amplitude);
151 native static long vibratorPerformEffect(long effect, long strength);
Patrick Scott18dd5f02009-07-02 11:31:12 -0400152
Alexey Kuzmin3fe97b02018-12-12 14:21:55 +0000153 private final IUidObserver mUidObserver = new IUidObserver.Stub() {
154 @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
155 mProcStatesCache.put(uid, procState);
156 }
157
158 @Override public void onUidGone(int uid, boolean disabled) {
159 mProcStatesCache.delete(uid);
160 }
161
162 @Override public void onUidActive(int uid) {
163 }
164
165 @Override public void onUidIdle(int uid, boolean disabled) {
166 }
167
168 @Override public void onUidCachedChanged(int uid, boolean cached) {
169 }
170 };
171
Patrick Scott18dd5f02009-07-02 11:31:12 -0400172 private class Vibration implements IBinder.DeathRecipient {
Michael Wright35a0c672018-01-24 00:32:53 +0000173 public final IBinder token;
Michael Wright36d873f2018-01-08 15:54:05 +0000174 // Start time in CLOCK_BOOTTIME base.
Michael Wright35a0c672018-01-24 00:32:53 +0000175 public final long startTime;
Michael Wright36d873f2018-01-08 15:54:05 +0000176 // Start time in unix epoch time. Only to be used for debugging purposes and to correlate
Michael Wright35a0c672018-01-24 00:32:53 +0000177 // with other system events, any duration calculations should be done use startTime so as
Michael Wright36d873f2018-01-08 15:54:05 +0000178 // not to be affected by discontinuities created by RTC adjustments.
Michael Wright35a0c672018-01-24 00:32:53 +0000179 public final long startTimeDebug;
180 public final int usageHint;
181 public final int uid;
182 public final String opPkg;
Alexey Kuzmine1f06b82018-06-20 17:48:43 +0100183 public final String reason;
Michael Wright35a0c672018-01-24 00:32:53 +0000184
185 // The actual effect to be played.
186 public VibrationEffect effect;
187 // The original effect that was requested. This is non-null only when the original effect
188 // differs from the effect that's being played. Typically these two things differ because
189 // the effect was scaled based on the users vibration intensity settings.
190 public VibrationEffect originalEffect;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400191
Michael Wright71216972017-01-31 18:33:54 +0000192 private Vibration(IBinder token, VibrationEffect effect,
Alexey Kuzmine1f06b82018-06-20 17:48:43 +0100193 int usageHint, int uid, String opPkg, String reason) {
Michael Wright35a0c672018-01-24 00:32:53 +0000194 this.token = token;
195 this.effect = effect;
196 this.startTime = SystemClock.elapsedRealtime();
197 this.startTimeDebug = System.currentTimeMillis();
198 this.usageHint = usageHint;
199 this.uid = uid;
200 this.opPkg = opPkg;
Alexey Kuzmine1f06b82018-06-20 17:48:43 +0100201 this.reason = reason;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400202 }
203
204 public void binderDied() {
Michael Wright71216972017-01-31 18:33:54 +0000205 synchronized (mLock) {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400206 if (this == mCurrentVibration) {
207 doCancelVibrateLocked();
Patrick Scott18dd5f02009-07-02 11:31:12 -0400208 }
209 }
210 }
211
Michael Wright35a0c672018-01-24 00:32:53 +0000212 public boolean hasTimeoutLongerThan(long millis) {
213 final long duration = effect.getDuration();
214 return duration >= 0 && duration > millis;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400215 }
Jeff Brown969579b2014-05-20 19:29:29 -0700216
Michael Wright35a0c672018-01-24 00:32:53 +0000217 public boolean isHapticFeedback() {
218 if (effect instanceof VibrationEffect.Prebaked) {
219 VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect;
220 switch (prebaked.getId()) {
221 case VibrationEffect.EFFECT_CLICK:
222 case VibrationEffect.EFFECT_DOUBLE_CLICK:
Alexey Kuzmin5a0a26f2018-03-20 18:25:51 +0000223 case VibrationEffect.EFFECT_HEAVY_CLICK:
Michael Wright35a0c672018-01-24 00:32:53 +0000224 case VibrationEffect.EFFECT_TICK:
Michael Wright0dbb5162018-05-25 15:13:36 +0100225 case VibrationEffect.EFFECT_POP:
226 case VibrationEffect.EFFECT_THUD:
Michael Wright35a0c672018-01-24 00:32:53 +0000227 return true;
228 default:
229 Slog.w(TAG, "Unknown prebaked vibration effect, "
230 + "assuming it isn't haptic feedback.");
231 return false;
232 }
Michael Wright71216972017-01-31 18:33:54 +0000233 }
Michael Wright35a0c672018-01-24 00:32:53 +0000234 final long duration = effect.getDuration();
235 return duration >= 0 && duration < MAX_HAPTIC_FEEDBACK_DURATION;
236 }
237
238 public boolean isNotification() {
239 switch (usageHint) {
240 case AudioAttributes.USAGE_NOTIFICATION:
241 case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
242 case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
243 case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
244 return true;
245 default:
246 return false;
247 }
248 }
249
250 public boolean isRingtone() {
251 return usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
252 }
253
254 public boolean isFromSystem() {
255 return uid == Process.SYSTEM_UID || uid == 0 || SYSTEM_UI_PACKAGE.equals(opPkg);
Jeff Brown969579b2014-05-20 19:29:29 -0700256 }
Michael Wright36d873f2018-01-08 15:54:05 +0000257
258 public VibrationInfo toInfo() {
Michael Wright35a0c672018-01-24 00:32:53 +0000259 return new VibrationInfo(
Alexey Kuzmine1f06b82018-06-20 17:48:43 +0100260 startTimeDebug, effect, originalEffect, usageHint, uid, opPkg, reason);
Michael Wright36d873f2018-01-08 15:54:05 +0000261 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400262 }
263
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700264 private static class VibrationInfo {
Michael Wright36d873f2018-01-08 15:54:05 +0000265 private final long mStartTimeDebug;
Michael Wright71216972017-01-31 18:33:54 +0000266 private final VibrationEffect mEffect;
Michael Wright35a0c672018-01-24 00:32:53 +0000267 private final VibrationEffect mOriginalEffect;
Michael Wright71216972017-01-31 18:33:54 +0000268 private final int mUsageHint;
269 private final int mUid;
270 private final String mOpPkg;
Alexey Kuzmine1f06b82018-06-20 17:48:43 +0100271 private final String mReason;
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700272
Michael Wright36d873f2018-01-08 15:54:05 +0000273 public VibrationInfo(long startTimeDebug, VibrationEffect effect,
Alexey Kuzmine1f06b82018-06-20 17:48:43 +0100274 VibrationEffect originalEffect, int usageHint, int uid,
275 String opPkg, String reason) {
Michael Wright36d873f2018-01-08 15:54:05 +0000276 mStartTimeDebug = startTimeDebug;
Michael Wright71216972017-01-31 18:33:54 +0000277 mEffect = effect;
Michael Wright35a0c672018-01-24 00:32:53 +0000278 mOriginalEffect = originalEffect;
Michael Wright71216972017-01-31 18:33:54 +0000279 mUsageHint = usageHint;
280 mUid = uid;
281 mOpPkg = opPkg;
Alexey Kuzmine1f06b82018-06-20 17:48:43 +0100282 mReason = reason;
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700283 }
284
285 @Override
286 public String toString() {
287 return new StringBuilder()
Michael Wright36d873f2018-01-08 15:54:05 +0000288 .append("startTime: ")
289 .append(DateFormat.getDateTimeInstance().format(new Date(mStartTimeDebug)))
Michael Wright71216972017-01-31 18:33:54 +0000290 .append(", effect: ")
291 .append(mEffect)
Michael Wright35a0c672018-01-24 00:32:53 +0000292 .append(", originalEffect: ")
293 .append(mOriginalEffect)
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700294 .append(", usageHint: ")
Michael Wright71216972017-01-31 18:33:54 +0000295 .append(mUsageHint)
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700296 .append(", uid: ")
Michael Wright71216972017-01-31 18:33:54 +0000297 .append(mUid)
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700298 .append(", opPkg: ")
Michael Wright71216972017-01-31 18:33:54 +0000299 .append(mOpPkg)
Alexey Kuzmine1f06b82018-06-20 17:48:43 +0100300 .append(", reason: ")
301 .append(mReason)
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700302 .toString();
303 }
304 }
305
Michael Wright0dbb5162018-05-25 15:13:36 +0100306 private static final class ScaleLevel {
307 public final float gamma;
308 public final int maxAmplitude;
309
310 public ScaleLevel(float gamma) {
311 this(gamma, VibrationEffect.MAX_AMPLITUDE);
312 }
313
314 public ScaleLevel(float gamma, int maxAmplitude) {
315 this.gamma = gamma;
316 this.maxAmplitude = maxAmplitude;
317 }
318
319 @Override
320 public String toString() {
321 return "ScaleLevel{gamma=" + gamma + ", maxAmplitude=" + maxAmplitude + "}";
322 }
323 }
324
Mike Lockwood3a322132009-11-24 00:30:52 -0500325 VibratorService(Context context) {
Vincent Beckere6904fb2012-08-10 14:17:33 +0200326 vibratorInit();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800327 // Reset the hardware to a default state, in case this is a runtime
328 // restart instead of a fresh boot.
329 vibratorOff();
330
Michael Wright71216972017-01-31 18:33:54 +0000331 mSupportsAmplitudeControl = vibratorSupportsAmplitudeControl();
332
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800333 mContext = context;
Michael Wright71216972017-01-31 18:33:54 +0000334 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700335 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800336 mWakeLock.setReferenceCounted(true);
337
Svet Ganovf7b47252018-02-26 11:11:27 -0800338 mAppOps = mContext.getSystemService(AppOpsManager.class);
Dianne Hackborn91268cf2013-06-13 19:06:50 -0700339 mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
340 BatteryStats.SERVICE_NAME));
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800341
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700342 mPreviousVibrationsLimit = mContext.getResources().getInteger(
343 com.android.internal.R.integer.config_previousVibrationsDumpLimit);
344
Michael Wright71216972017-01-31 18:33:54 +0000345 mDefaultVibrationAmplitude = mContext.getResources().getInteger(
346 com.android.internal.R.integer.config_defaultVibrationAmplitude);
347
Tyler Freeman319a34a2017-05-04 17:23:35 -0700348 mAllowPriorityVibrationsInLowPowerMode = mContext.getResources().getBoolean(
349 com.android.internal.R.bool.config_allowPriorityVibrationsInLowPowerMode);
350
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700351 mPreviousVibrations = new LinkedList<>();
Patrick Scott18dd5f02009-07-02 11:31:12 -0400352
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 IntentFilter filter = new IntentFilter();
354 filter.addAction(Intent.ACTION_SCREEN_OFF);
355 context.registerReceiver(mIntentReceiver, filter);
Michael Wright71216972017-01-31 18:33:54 +0000356
Michael Wrightd39cbec2018-04-16 19:35:13 +0100357 VibrationEffect clickEffect = createEffectFromResource(
Michael Wright71216972017-01-31 18:33:54 +0000358 com.android.internal.R.array.config_virtualKeyVibePattern);
Michael Wright71216972017-01-31 18:33:54 +0000359 VibrationEffect doubleClickEffect = VibrationEffect.createWaveform(
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000360 DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS, -1 /*repeatIndex*/);
Michael Wrightd39cbec2018-04-16 19:35:13 +0100361 VibrationEffect heavyClickEffect = createEffectFromResource(
362 com.android.internal.R.array.config_longPressVibePattern);
363 VibrationEffect tickEffect = createEffectFromResource(
Michael Wright57d94d92017-05-31 14:44:45 +0100364 com.android.internal.R.array.config_clockTickVibePattern);
Michael Wright71216972017-01-31 18:33:54 +0000365
Michael Wright0dbb5162018-05-25 15:13:36 +0100366 mFallbackEffects = new SparseArray<>();
Alexey Kuzmin5a0a26f2018-03-20 18:25:51 +0000367 mFallbackEffects.put(VibrationEffect.EFFECT_CLICK, clickEffect);
368 mFallbackEffects.put(VibrationEffect.EFFECT_DOUBLE_CLICK, doubleClickEffect);
369 mFallbackEffects.put(VibrationEffect.EFFECT_TICK, tickEffect);
Michael Wrightd39cbec2018-04-16 19:35:13 +0100370 mFallbackEffects.put(VibrationEffect.EFFECT_HEAVY_CLICK, heavyClickEffect);
Michael Wright0dbb5162018-05-25 15:13:36 +0100371
372 mScaleLevels = new SparseArray<>();
373 mScaleLevels.put(SCALE_VERY_LOW,
374 new ScaleLevel(SCALE_VERY_LOW_GAMMA, SCALE_VERY_LOW_MAX_AMPLITUDE));
375 mScaleLevels.put(SCALE_LOW, new ScaleLevel(SCALE_LOW_GAMMA, SCALE_LOW_MAX_AMPLITUDE));
376 mScaleLevels.put(SCALE_NONE, new ScaleLevel(SCALE_NONE_GAMMA));
377 mScaleLevels.put(SCALE_HIGH, new ScaleLevel(SCALE_HIGH_GAMMA));
378 mScaleLevels.put(SCALE_VERY_HIGH, new ScaleLevel(SCALE_VERY_HIGH_GAMMA));
Michael Wright57d94d92017-05-31 14:44:45 +0100379 }
380
Michael Wrightd39cbec2018-04-16 19:35:13 +0100381 private VibrationEffect createEffectFromResource(int resId) {
382 long[] timings = getLongIntArray(mContext.getResources(), resId);
383 return createEffectFromTimings(timings);
384 }
385
386 private static VibrationEffect createEffectFromTimings(long[] timings) {
Michael Wright57d94d92017-05-31 14:44:45 +0100387 if (timings == null || timings.length == 0) {
388 return null;
389 } else if (timings.length == 1) {
390 return VibrationEffect.createOneShot(timings[0], VibrationEffect.DEFAULT_AMPLITUDE);
391 } else {
392 return VibrationEffect.createWaveform(timings, -1);
393 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394 }
395
Jeff Brown7f6c2312012-04-13 20:38:38 -0700396 public void systemReady() {
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000397 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorService#systemReady");
398 try {
399 mIm = mContext.getSystemService(InputManager.class);
400 mVibrator = mContext.getSystemService(Vibrator.class);
401 mSettingObserver = new SettingsObserver(mH);
Jeff Brownd4935962012-09-25 13:27:20 -0700402
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000403 mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
404 mPowerManagerInternal.registerLowPowerModeObserver(
405 new PowerManagerInternal.LowPowerModeListener() {
406 @Override
407 public int getServiceType() {
408 return ServiceType.VIBRATION;
409 }
jackqdyulei455e90a2017-02-09 15:29:16 -0800410
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000411 @Override
412 public void onLowPowerModeChanged(PowerSaveState result) {
413 updateVibrators();
414 }
415 });
Dianne Hackborneb94fa72014-06-03 17:48:12 -0700416
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000417 mContext.getContentResolver().registerContentObserver(
418 Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES),
419 true, mSettingObserver, UserHandle.USER_ALL);
Ruchi Kandoi13b03af2014-05-07 20:10:32 -0700420
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000421 mContext.getContentResolver().registerContentObserver(
422 Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_INTENSITY),
423 true, mSettingObserver, UserHandle.USER_ALL);
Michael Wright35a0c672018-01-24 00:32:53 +0000424
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000425 mContext.getContentResolver().registerContentObserver(
426 Settings.System.getUriFor(Settings.System.NOTIFICATION_VIBRATION_INTENSITY),
427 true, mSettingObserver, UserHandle.USER_ALL);
Michael Wright35a0c672018-01-24 00:32:53 +0000428
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000429 mContext.registerReceiver(new BroadcastReceiver() {
430 @Override
431 public void onReceive(Context context, Intent intent) {
432 updateVibrators();
433 }
434 }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mH);
Jeff Brownd4935962012-09-25 13:27:20 -0700435
Alexey Kuzmin3fe97b02018-12-12 14:21:55 +0000436 try {
437 ActivityManager.getService().registerUidObserver(mUidObserver,
438 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE,
439 ActivityManager.PROCESS_STATE_UNKNOWN, null);
440 } catch (RemoteException e) {
441 // ignored; both services live in system_server
442 }
443
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000444 updateVibrators();
445 } finally {
446 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
447 }
Dianne Hackbornea9020e2010-11-04 11:39:12 -0700448 }
Jeff Brown7f6c2312012-04-13 20:38:38 -0700449
Ruchi Kandoi13b03af2014-05-07 20:10:32 -0700450 private final class SettingsObserver extends ContentObserver {
451 public SettingsObserver(Handler handler) {
452 super(handler);
453 }
454
455 @Override
456 public void onChange(boolean SelfChange) {
Michael Wright71216972017-01-31 18:33:54 +0000457 updateVibrators();
Ruchi Kandoi13b03af2014-05-07 20:10:32 -0700458 }
459 }
460
Jeff Brown82379ba2014-07-25 19:03:28 -0700461 @Override // Binder call
Jeff Brown7f6c2312012-04-13 20:38:38 -0700462 public boolean hasVibrator() {
463 return doVibratorExists();
464 }
465
Michael Wright71216972017-01-31 18:33:54 +0000466 @Override // Binder call
467 public boolean hasAmplitudeControl() {
468 synchronized (mInputDeviceVibrators) {
469 // Input device vibrators don't support amplitude controls yet, but are still used over
470 // the system vibrator when connected.
471 return mSupportsAmplitudeControl && mInputDeviceVibrators.isEmpty();
472 }
473 }
474
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800475 private void verifyIncomingUid(int uid) {
476 if (uid == Binder.getCallingUid()) {
477 return;
478 }
479 if (Binder.getCallingPid() == Process.myPid()) {
480 return;
481 }
482 mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
483 Binder.getCallingPid(), Binder.getCallingUid(), null);
484 }
485
Michael Wright71216972017-01-31 18:33:54 +0000486 /**
487 * Validate the incoming VibrationEffect.
488 *
489 * We can't throw exceptions here since we might be called from some system_server component,
490 * which would bring the whole system down.
491 *
492 * @return whether the VibrationEffect is valid
493 */
494 private static boolean verifyVibrationEffect(VibrationEffect effect) {
495 if (effect == null) {
496 // Effect must not be null.
497 Slog.wtf(TAG, "effect must not be null");
498 return false;
499 }
500 try {
501 effect.validate();
502 } catch (Exception e) {
503 Slog.wtf(TAG, "Encountered issue when verifying VibrationEffect.", e);
504 return false;
505 }
506 return true;
507 }
508
509 private static long[] getLongIntArray(Resources r, int resid) {
510 int[] ar = r.getIntArray(resid);
511 if (ar == null) {
512 return null;
513 }
514 long[] out = new long[ar.length];
515 for (int i = 0; i < ar.length; i++) {
516 out[i] = ar[i];
517 }
518 return out;
519 }
520
Jeff Brown82379ba2014-07-25 19:03:28 -0700521 @Override // Binder call
Alexey Kuzmine1f06b82018-06-20 17:48:43 +0100522 public void vibrate(int uid, String opPkg, VibrationEffect effect, int usageHint, String reason,
John Spurlock1af30c72014-03-10 08:33:35 -0400523 IBinder token) {
Alexey Kuzmine1f06b82018-06-20 17:48:43 +0100524 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000525 try {
526 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
527 != PackageManager.PERMISSION_GRANTED) {
528 throw new SecurityException("Requires VIBRATE permission");
Alexey Kuzmin7f38fb12018-02-07 13:51:28 +0000529 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000530 if (token == null) {
531 Slog.e(TAG, "token must not be null");
532 return;
533 }
534 verifyIncomingUid(uid);
Alexey Kuzmin3fe97b02018-12-12 14:21:55 +0000535 if (mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)
536 > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
537 Slog.e(TAG, "Ignoring incoming vibration as process with uid = "
538 + uid + " is background");
539 return;
540 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000541 if (!verifyVibrationEffect(effect)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800542 return;
543 }
544
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000545 // If our current vibration is longer than the new vibration and is the same amplitude,
546 // then just let the current one finish.
547 synchronized (mLock) {
548 if (effect instanceof VibrationEffect.OneShot
549 && mCurrentVibration != null
550 && mCurrentVibration.effect instanceof VibrationEffect.OneShot) {
551 VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect;
552 VibrationEffect.OneShot currentOneShot =
553 (VibrationEffect.OneShot) mCurrentVibration.effect;
554 if (mCurrentVibration.hasTimeoutLongerThan(newOneShot.getDuration())
555 && newOneShot.getAmplitude() == currentOneShot.getAmplitude()) {
556 if (DEBUG) {
557 Slog.d(TAG,
558 "Ignoring incoming vibration in favor of current vibration");
559 }
560 return;
561 }
562 }
563
564 // If the current vibration is repeating and the incoming one is non-repeating,
565 // then ignore the non-repeating vibration. This is so that we don't cancel
566 // vibrations that are meant to grab the attention of the user, like ringtones and
567 // alarms, in favor of one-shot vibrations that are likely quite short.
568 if (!isRepeatingVibration(effect)
569 && mCurrentVibration != null
570 && isRepeatingVibration(mCurrentVibration.effect)) {
571 if (DEBUG) {
572 Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
573 }
574 return;
575 }
576
Alexey Kuzmine1f06b82018-06-20 17:48:43 +0100577 Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg, reason);
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000578 linkVibration(vib);
579 long ident = Binder.clearCallingIdentity();
580 try {
Alexey Kuzmine1f06b82018-06-20 17:48:43 +0100581
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000582 doCancelVibrateLocked();
583 startVibrationLocked(vib);
584 addToPreviousVibrationsLocked(vib);
585 } finally {
586 Binder.restoreCallingIdentity(ident);
587 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000589 } finally {
590 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800591 }
592 }
593
Michael Wright58c46312017-10-05 14:04:14 -0400594 private static boolean isRepeatingVibration(VibrationEffect effect) {
Michael Wright35a0c672018-01-24 00:32:53 +0000595 return effect.getDuration() == Long.MAX_VALUE;
Michael Wright58c46312017-10-05 14:04:14 -0400596 }
597
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700598 private void addToPreviousVibrationsLocked(Vibration vib) {
599 if (mPreviousVibrations.size() > mPreviousVibrationsLimit) {
600 mPreviousVibrations.removeFirst();
601 }
Michael Wright36d873f2018-01-08 15:54:05 +0000602 mPreviousVibrations.addLast(vib.toInfo());
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700603 }
604
Jeff Brown82379ba2014-07-25 19:03:28 -0700605 @Override // Binder call
Patrick Scott18dd5f02009-07-02 11:31:12 -0400606 public void cancelVibrate(IBinder token) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800607 mContext.enforceCallingOrSelfPermission(
608 android.Manifest.permission.VIBRATE,
609 "cancelVibrate");
610
Michael Wright71216972017-01-31 18:33:54 +0000611 synchronized (mLock) {
Michael Wright35a0c672018-01-24 00:32:53 +0000612 if (mCurrentVibration != null && mCurrentVibration.token == token) {
Michael Wright71216972017-01-31 18:33:54 +0000613 if (DEBUG) {
614 Slog.d(TAG, "Canceling vibration.");
615 }
616 long ident = Binder.clearCallingIdentity();
617 try {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400618 doCancelVibrateLocked();
Michael Wright71216972017-01-31 18:33:54 +0000619 } finally {
620 Binder.restoreCallingIdentity(ident);
Patrick Scott18dd5f02009-07-02 11:31:12 -0400621 }
622 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800623 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800624 }
Eric Olsenf42f15c2009-10-29 16:42:03 -0700625
Michael Wright71216972017-01-31 18:33:54 +0000626 private final Runnable mVibrationEndRunnable = new Runnable() {
Jeff Brown82379ba2014-07-25 19:03:28 -0700627 @Override
Patrick Scott18dd5f02009-07-02 11:31:12 -0400628 public void run() {
Michael Wright71216972017-01-31 18:33:54 +0000629 onVibrationFinished();
Patrick Scott18dd5f02009-07-02 11:31:12 -0400630 }
631 };
632
Alexey Kuzmin7f38fb12018-02-07 13:51:28 +0000633 @GuardedBy("mLock")
Patrick Scott18dd5f02009-07-02 11:31:12 -0400634 private void doCancelVibrateLocked() {
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000635 Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
636 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doCancelVibrateLocked");
637 try {
638 mH.removeCallbacks(mVibrationEndRunnable);
639 if (mThread != null) {
640 mThread.cancel();
641 mThread = null;
642 }
643 doVibratorOff();
644 reportFinishVibrationLocked();
645 } finally {
646 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
Patrick Scott18dd5f02009-07-02 11:31:12 -0400647 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400648 }
649
Michael Wright71216972017-01-31 18:33:54 +0000650 // Callback for whenever the current vibration has finished played out
651 public void onVibrationFinished() {
652 if (DEBUG) {
653 Slog.e(TAG, "Vibration finished, cleaning up");
Patrick Scott18dd5f02009-07-02 11:31:12 -0400654 }
Michael Wright71216972017-01-31 18:33:54 +0000655 synchronized (mLock) {
656 // Make sure the vibration is really done. This also reports that the vibration is
657 // finished.
658 doCancelVibrateLocked();
659 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400660 }
661
Alexey Kuzmin7f38fb12018-02-07 13:51:28 +0000662 @GuardedBy("mLock")
Patrick Scott18dd5f02009-07-02 11:31:12 -0400663 private void startVibrationLocked(final Vibration vib) {
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000664 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked");
665 try {
666 if (!isAllowedToVibrateLocked(vib)) {
667 return;
Michael Wright71216972017-01-31 18:33:54 +0000668 }
Michael Wright71216972017-01-31 18:33:54 +0000669
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000670 final int intensity = getCurrentIntensityLocked(vib);
671 if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
672 return;
Michael Wright71216972017-01-31 18:33:54 +0000673 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000674
675 if (vib.isRingtone() && !shouldVibrateForRingtone()) {
676 if (DEBUG) {
677 Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones");
678 }
679 return;
680 }
681
682 final int mode = getAppOpMode(vib);
683 if (mode != AppOpsManager.MODE_ALLOWED) {
684 if (mode == AppOpsManager.MODE_ERRORED) {
685 // We might be getting calls from within system_server, so we don't actually
686 // want to throw a SecurityException here.
687 Slog.w(TAG, "Would be an error: vibrate from uid " + vib.uid);
688 }
689 return;
690 }
691 applyVibrationIntensityScalingLocked(vib, intensity);
692 startVibrationInnerLocked(vib);
693 } finally {
694 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
Michael Wright71216972017-01-31 18:33:54 +0000695 }
Michael Wright71216972017-01-31 18:33:54 +0000696 }
697
Alexey Kuzmin7f38fb12018-02-07 13:51:28 +0000698 @GuardedBy("mLock")
Michael Wright71216972017-01-31 18:33:54 +0000699 private void startVibrationInnerLocked(Vibration vib) {
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000700 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationInnerLocked");
701 try {
702 mCurrentVibration = vib;
703 if (vib.effect instanceof VibrationEffect.OneShot) {
704 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
705 VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect;
706 doVibratorOn(oneShot.getDuration(), oneShot.getAmplitude(), vib.uid, vib.usageHint);
707 mH.postDelayed(mVibrationEndRunnable, oneShot.getDuration());
708 } else if (vib.effect instanceof VibrationEffect.Waveform) {
709 // mThread better be null here. doCancelVibrate should always be
710 // called before startNextVibrationLocked or startVibrationLocked.
711 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
712 VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect;
713 mThread = new VibrateThread(waveform, vib.uid, vib.usageHint);
714 mThread.start();
715 } else if (vib.effect instanceof VibrationEffect.Prebaked) {
716 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
717 long timeout = doVibratorPrebakedEffectLocked(vib);
718 if (timeout > 0) {
719 mH.postDelayed(mVibrationEndRunnable, timeout);
720 }
721 } else {
722 Slog.e(TAG, "Unknown vibration type, ignoring");
Michael Wright71216972017-01-31 18:33:54 +0000723 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000724 } finally {
725 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800726 }
727 }
728
Michael Wright35a0c672018-01-24 00:32:53 +0000729 private boolean isAllowedToVibrateLocked(Vibration vib) {
Tyler Freeman319a34a2017-05-04 17:23:35 -0700730 if (!mLowPowerMode) {
731 return true;
732 }
Michael Wright35a0c672018-01-24 00:32:53 +0000733
734 if (vib.usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
Tyler Freeman319a34a2017-05-04 17:23:35 -0700735 return true;
736 }
Tyler Freeman319a34a2017-05-04 17:23:35 -0700737
Michael Wright35a0c672018-01-24 00:32:53 +0000738 if (vib.usageHint == AudioAttributes.USAGE_ALARM ||
739 vib.usageHint == AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY ||
740 vib.usageHint == AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) {
Tyler Freeman319a34a2017-05-04 17:23:35 -0700741 return true;
742 }
743
744 return false;
745 }
746
Michael Wright35a0c672018-01-24 00:32:53 +0000747 private int getCurrentIntensityLocked(Vibration vib) {
748 if (vib.isNotification() || vib.isRingtone()){
749 return mNotificationIntensity;
750 } else if (vib.isHapticFeedback()) {
751 return mHapticFeedbackIntensity;
752 } else {
753 return Vibrator.VIBRATION_INTENSITY_MEDIUM;
754 }
755 }
756
757 /**
758 * Scale the vibration effect by the intensity as appropriate based its intent.
759 */
760 private void applyVibrationIntensityScalingLocked(Vibration vib, int intensity) {
761 if (vib.effect instanceof VibrationEffect.Prebaked) {
762 // Prebaked effects are always just a direct translation from intensity to
763 // EffectStrength.
764 VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked)vib.effect;
765 prebaked.setEffectStrength(intensityToEffectStrength(intensity));
766 return;
767 }
768
Michael Wright0dbb5162018-05-25 15:13:36 +0100769 final int defaultIntensity;
Michael Wright35a0c672018-01-24 00:32:53 +0000770 if (vib.isNotification() || vib.isRingtone()) {
Michael Wright0dbb5162018-05-25 15:13:36 +0100771 defaultIntensity = mVibrator.getDefaultNotificationVibrationIntensity();
772 } else if (vib.isHapticFeedback()) {
773 defaultIntensity = mVibrator.getDefaultHapticFeedbackIntensity();
Michael Wright35a0c672018-01-24 00:32:53 +0000774 } else {
Michael Wright0dbb5162018-05-25 15:13:36 +0100775 // If we don't know what kind of vibration we're playing then just skip scaling for
776 // now.
777 return;
778 }
779
780 final ScaleLevel scale = mScaleLevels.get(intensity - defaultIntensity);
781 if (scale == null) {
782 // We should have scaling levels for all cases, so not being able to scale because of a
783 // missing level is unexpected.
784 Slog.e(TAG, "No configured scaling level!"
785 + " (current=" + intensity + ", default= " + defaultIntensity + ")");
786 return;
Michael Wright35a0c672018-01-24 00:32:53 +0000787 }
788
789 VibrationEffect scaledEffect = null;
790 if (vib.effect instanceof VibrationEffect.OneShot) {
791 VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect;
Alexey Kuzmin59efe972018-04-24 12:58:13 +0100792 oneShot = oneShot.resolve(mDefaultVibrationAmplitude);
Michael Wright0dbb5162018-05-25 15:13:36 +0100793 scaledEffect = oneShot.scale(scale.gamma, scale.maxAmplitude);
Michael Wright35a0c672018-01-24 00:32:53 +0000794 } else if (vib.effect instanceof VibrationEffect.Waveform) {
795 VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect;
Alexey Kuzmin59efe972018-04-24 12:58:13 +0100796 waveform = waveform.resolve(mDefaultVibrationAmplitude);
Michael Wright0dbb5162018-05-25 15:13:36 +0100797 scaledEffect = waveform.scale(scale.gamma, scale.maxAmplitude);
Michael Wright35a0c672018-01-24 00:32:53 +0000798 } else {
799 Slog.w(TAG, "Unable to apply intensity scaling, unknown VibrationEffect type");
800 }
801
802 if (scaledEffect != null) {
803 vib.originalEffect = vib.effect;
804 vib.effect = scaledEffect;
805 }
806 }
807
Brad Ebinger2d1c3b32016-05-12 18:05:17 -0700808 private boolean shouldVibrateForRingtone() {
Michael Wright35a0c672018-01-24 00:32:53 +0000809 AudioManager audioManager = mContext.getSystemService(AudioManager.class);
Brad Ebingerdcbdc0d2016-06-23 17:42:30 -0700810 int ringerMode = audioManager.getRingerModeInternal();
Brad Ebinger2d1c3b32016-05-12 18:05:17 -0700811 // "Also vibrate for calls" Setting in Sound
812 if (Settings.System.getInt(
813 mContext.getContentResolver(), Settings.System.VIBRATE_WHEN_RINGING, 0) != 0) {
814 return ringerMode != AudioManager.RINGER_MODE_SILENT;
815 } else {
816 return ringerMode == AudioManager.RINGER_MODE_VIBRATE;
817 }
818 }
819
Michael Wright71216972017-01-31 18:33:54 +0000820 private int getAppOpMode(Vibration vib) {
Svet Ganovf7b47252018-02-26 11:11:27 -0800821 int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE,
822 vib.usageHint, vib.uid, vib.opPkg);
823 if (mode == AppOpsManager.MODE_ALLOWED) {
824 mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, vib.uid, vib.opPkg);
Michael Wright71216972017-01-31 18:33:54 +0000825 }
826 return mode;
827 }
828
Alexey Kuzmin7f38fb12018-02-07 13:51:28 +0000829 @GuardedBy("mLock")
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800830 private void reportFinishVibrationLocked() {
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000831 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
832 try {
833 if (mCurrentVibration != null) {
Svet Ganovf7b47252018-02-26 11:11:27 -0800834 mAppOps.finishOp(AppOpsManager.OP_VIBRATE, mCurrentVibration.uid,
835 mCurrentVibration.opPkg);
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000836 unlinkVibration(mCurrentVibration);
837 mCurrentVibration = null;
838 }
839 } finally {
840 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800841 }
842 }
843
Michael Wright36d873f2018-01-08 15:54:05 +0000844 private void linkVibration(Vibration vib) {
845 // Only link against waveforms since they potentially don't have a finish if
846 // they're repeating. Let other effects just play out until they're done.
Michael Wright35a0c672018-01-24 00:32:53 +0000847 if (vib.effect instanceof VibrationEffect.Waveform) {
Michael Wright36d873f2018-01-08 15:54:05 +0000848 try {
Michael Wright35a0c672018-01-24 00:32:53 +0000849 vib.token.linkToDeath(vib, 0);
Michael Wright36d873f2018-01-08 15:54:05 +0000850 } catch (RemoteException e) {
851 return;
852 }
853 }
854 }
855
Mathias Jeppssonb23949b2010-09-28 14:45:23 +0200856 private void unlinkVibration(Vibration vib) {
Michael Wright35a0c672018-01-24 00:32:53 +0000857 if (vib.effect instanceof VibrationEffect.Waveform) {
858 vib.token.unlinkToDeath(vib, 0);
Mathias Jeppssonb23949b2010-09-28 14:45:23 +0200859 }
860 }
861
Michael Wright71216972017-01-31 18:33:54 +0000862 private void updateVibrators() {
863 synchronized (mLock) {
864 boolean devicesUpdated = updateInputDeviceVibratorsLocked();
865 boolean lowPowerModeUpdated = updateLowPowerModeLocked();
Michael Wright35a0c672018-01-24 00:32:53 +0000866 updateVibrationIntensityLocked();
Jeff Brown7f6c2312012-04-13 20:38:38 -0700867
Michael Wright71216972017-01-31 18:33:54 +0000868 if (devicesUpdated || lowPowerModeUpdated) {
869 // If the state changes out from under us then just reset.
870 doCancelVibrateLocked();
871 }
872 }
873 }
Jeff Brown82065252012-04-16 13:19:05 -0700874
Michael Wright71216972017-01-31 18:33:54 +0000875 private boolean updateInputDeviceVibratorsLocked() {
876 boolean changed = false;
877 boolean vibrateInputDevices = false;
878 try {
879 vibrateInputDevices = Settings.System.getIntForUser(
880 mContext.getContentResolver(),
881 Settings.System.VIBRATE_INPUT_DEVICES, UserHandle.USER_CURRENT) > 0;
882 } catch (SettingNotFoundException snfe) {
883 }
884 if (vibrateInputDevices != mVibrateInputDevicesSetting) {
885 changed = true;
886 mVibrateInputDevicesSetting = vibrateInputDevices;
887 }
Ruchi Kandoi13b03af2014-05-07 20:10:32 -0700888
Michael Wright71216972017-01-31 18:33:54 +0000889 if (mVibrateInputDevicesSetting) {
890 if (!mInputDeviceListenerRegistered) {
891 mInputDeviceListenerRegistered = true;
892 mIm.registerInputDeviceListener(this, mH);
893 }
894 } else {
895 if (mInputDeviceListenerRegistered) {
896 mInputDeviceListenerRegistered = false;
897 mIm.unregisterInputDeviceListener(this);
898 }
899 }
Jeff Brown82065252012-04-16 13:19:05 -0700900
Michael Wright71216972017-01-31 18:33:54 +0000901 mInputDeviceVibrators.clear();
902 if (mVibrateInputDevicesSetting) {
903 int[] ids = mIm.getInputDeviceIds();
904 for (int i = 0; i < ids.length; i++) {
905 InputDevice device = mIm.getInputDevice(ids[i]);
906 Vibrator vibrator = device.getVibrator();
907 if (vibrator.hasVibrator()) {
908 mInputDeviceVibrators.add(vibrator);
Jeff Brown7f6c2312012-04-13 20:38:38 -0700909 }
910 }
Michael Wright71216972017-01-31 18:33:54 +0000911 return true;
Jeff Brown7f6c2312012-04-13 20:38:38 -0700912 }
Michael Wright71216972017-01-31 18:33:54 +0000913 return changed;
914 }
915
916 private boolean updateLowPowerModeLocked() {
917 boolean lowPowerMode = mPowerManagerInternal
918 .getLowPowerState(ServiceType.VIBRATION).batterySaverEnabled;
919 if (lowPowerMode != mLowPowerMode) {
920 mLowPowerMode = lowPowerMode;
921 return true;
922 }
923 return false;
Jeff Brown7f6c2312012-04-13 20:38:38 -0700924 }
925
Michael Wright35a0c672018-01-24 00:32:53 +0000926 private void updateVibrationIntensityLocked() {
927 mHapticFeedbackIntensity = Settings.System.getIntForUser(mContext.getContentResolver(),
928 Settings.System.HAPTIC_FEEDBACK_INTENSITY,
929 mVibrator.getDefaultHapticFeedbackIntensity(), UserHandle.USER_CURRENT);
930 mNotificationIntensity = Settings.System.getIntForUser(mContext.getContentResolver(),
931 Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
932 mVibrator.getDefaultNotificationVibrationIntensity(), UserHandle.USER_CURRENT);
933 }
934
Jeff Brown7f6c2312012-04-13 20:38:38 -0700935 @Override
936 public void onInputDeviceAdded(int deviceId) {
Michael Wright71216972017-01-31 18:33:54 +0000937 updateVibrators();
Jeff Brown7f6c2312012-04-13 20:38:38 -0700938 }
939
940 @Override
941 public void onInputDeviceChanged(int deviceId) {
Michael Wright71216972017-01-31 18:33:54 +0000942 updateVibrators();
Jeff Brown7f6c2312012-04-13 20:38:38 -0700943 }
944
945 @Override
946 public void onInputDeviceRemoved(int deviceId) {
Michael Wright71216972017-01-31 18:33:54 +0000947 updateVibrators();
Jeff Brown7f6c2312012-04-13 20:38:38 -0700948 }
949
950 private boolean doVibratorExists() {
Jeff Brown1064a502012-05-02 16:51:37 -0700951 // For now, we choose to ignore the presence of input devices that have vibrators
952 // when reporting whether the device has a vibrator. Applications often use this
953 // information to decide whether to enable certain features so they expect the
954 // result of hasVibrator() to be constant. For now, just report whether
955 // the device has a built-in vibrator.
956 //synchronized (mInputDeviceVibrators) {
957 // return !mInputDeviceVibrators.isEmpty() || vibratorExists();
958 //}
Dianne Hackbornc2293022013-02-06 23:14:49 -0800959 return vibratorExists();
Jeff Brown7f6c2312012-04-13 20:38:38 -0700960 }
961
Michael Wright71216972017-01-31 18:33:54 +0000962 private void doVibratorOn(long millis, int amplitude, int uid, int usageHint) {
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000963 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn");
964 try {
965 synchronized (mInputDeviceVibrators) {
966 if (amplitude == VibrationEffect.DEFAULT_AMPLITUDE) {
967 amplitude = mDefaultVibrationAmplitude;
Jeff Brown7f6c2312012-04-13 20:38:38 -0700968 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000969 if (DEBUG) {
970 Slog.d(TAG, "Turning vibrator on for " + millis + " ms" +
971 " with amplitude " + amplitude + ".");
972 }
973 noteVibratorOnLocked(uid, millis);
974 final int vibratorCount = mInputDeviceVibrators.size();
975 if (vibratorCount != 0) {
976 final AudioAttributes attributes =
977 new AudioAttributes.Builder().setUsage(usageHint).build();
978 for (int i = 0; i < vibratorCount; i++) {
979 mInputDeviceVibrators.get(i).vibrate(millis, attributes);
980 }
981 } else {
982 // Note: ordering is important here! Many haptic drivers will reset their
983 // amplitude when enabled, so we always have to enable frst, then set the
984 // amplitude.
985 vibratorOn(millis);
986 doVibratorSetAmplitude(amplitude);
987 }
Jeff Brown7f6c2312012-04-13 20:38:38 -0700988 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +0000989 } finally {
990 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
Jeff Brown7f6c2312012-04-13 20:38:38 -0700991 }
992 }
993
Michael Wright71216972017-01-31 18:33:54 +0000994 private void doVibratorSetAmplitude(int amplitude) {
995 if (mSupportsAmplitudeControl) {
996 vibratorSetAmplitude(amplitude);
997 }
998 }
999
Jeff Brown7f6c2312012-04-13 20:38:38 -07001000 private void doVibratorOff() {
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001001 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOff");
1002 try {
1003 synchronized (mInputDeviceVibrators) {
1004 if (DEBUG) {
1005 Slog.d(TAG, "Turning vibrator off.");
Jeff Brown7f6c2312012-04-13 20:38:38 -07001006 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001007 noteVibratorOffLocked();
1008 final int vibratorCount = mInputDeviceVibrators.size();
1009 if (vibratorCount != 0) {
1010 for (int i = 0; i < vibratorCount; i++) {
1011 mInputDeviceVibrators.get(i).cancel();
1012 }
1013 } else {
1014 vibratorOff();
1015 }
Jeff Brown7f6c2312012-04-13 20:38:38 -07001016 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001017 } finally {
1018 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
Jeff Brown7f6c2312012-04-13 20:38:38 -07001019 }
1020 }
1021
Alexey Kuzmin7f38fb12018-02-07 13:51:28 +00001022 @GuardedBy("mLock")
Michael Wright71216972017-01-31 18:33:54 +00001023 private long doVibratorPrebakedEffectLocked(Vibration vib) {
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001024 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorPrebakedEffectLocked");
1025 try {
1026 final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect;
1027 final boolean usingInputDeviceVibrators;
1028 synchronized (mInputDeviceVibrators) {
1029 usingInputDeviceVibrators = !mInputDeviceVibrators.isEmpty();
Michael Wright35a0c672018-01-24 00:32:53 +00001030 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001031 // Input devices don't support prebaked effect, so skip trying it with them.
1032 if (!usingInputDeviceVibrators) {
1033 long timeout = vibratorPerformEffect(prebaked.getId(),
1034 prebaked.getEffectStrength());
1035 if (timeout > 0) {
1036 noteVibratorOnLocked(vib.uid, timeout);
1037 return timeout;
1038 }
1039 }
1040 if (!prebaked.shouldFallback()) {
1041 return 0;
1042 }
1043 VibrationEffect effect = getFallbackEffect(prebaked.getId());
1044 if (effect == null) {
1045 Slog.w(TAG, "Failed to play prebaked effect, no fallback");
1046 return 0;
1047 }
Alexey Kuzmine1f06b82018-06-20 17:48:43 +01001048 Vibration fallbackVib = new Vibration(vib.token, effect, vib.usageHint, vib.uid,
1049 vib.opPkg, vib.reason + " (fallback)");
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001050 final int intensity = getCurrentIntensityLocked(fallbackVib);
1051 linkVibration(fallbackVib);
1052 applyVibrationIntensityScalingLocked(fallbackVib, intensity);
1053 startVibrationInnerLocked(fallbackVib);
Michael Wright35a0c672018-01-24 00:32:53 +00001054 return 0;
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001055 } finally {
1056 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
Michael Wright35a0c672018-01-24 00:32:53 +00001057 }
Michael Wright71216972017-01-31 18:33:54 +00001058 }
Eric Olsenf42f15c2009-10-29 16:42:03 -07001059
Michael Wright36d873f2018-01-08 15:54:05 +00001060 private VibrationEffect getFallbackEffect(int effectId) {
Alexey Kuzmin5a0a26f2018-03-20 18:25:51 +00001061 return mFallbackEffects.get(effectId);
Michael Wright36d873f2018-01-08 15:54:05 +00001062 }
1063
Michael Wright35a0c672018-01-24 00:32:53 +00001064 /**
1065 * Return the current desired effect strength.
1066 *
1067 * If the returned value is &lt; 0 then the vibration shouldn't be played at all.
1068 */
1069 private static int intensityToEffectStrength(int intensity) {
1070 switch (intensity) {
1071 case Vibrator.VIBRATION_INTENSITY_LOW:
1072 return EffectStrength.LIGHT;
1073 case Vibrator.VIBRATION_INTENSITY_MEDIUM:
1074 return EffectStrength.MEDIUM;
1075 case Vibrator.VIBRATION_INTENSITY_HIGH:
1076 return EffectStrength.STRONG;
1077 default:
1078 Slog.w(TAG, "Got unexpected vibration intensity: " + intensity);
1079 return EffectStrength.STRONG;
1080 }
1081 }
1082
Michael Wright71216972017-01-31 18:33:54 +00001083 private void noteVibratorOnLocked(int uid, long millis) {
1084 try {
1085 mBatteryStatsService.noteVibratorOn(uid, millis);
Bookatza7020bd2018-08-28 16:29:35 -07001086 StatsLog.write_non_chained(StatsLog.VIBRATOR_STATE_CHANGED, uid, null,
1087 StatsLog.VIBRATOR_STATE_CHANGED__STATE__ON, millis);
Michael Wright71216972017-01-31 18:33:54 +00001088 mCurVibUid = uid;
1089 } catch (RemoteException e) {
1090 }
1091 }
1092
1093 private void noteVibratorOffLocked() {
1094 if (mCurVibUid >= 0) {
1095 try {
1096 mBatteryStatsService.noteVibratorOff(mCurVibUid);
Bookatza7020bd2018-08-28 16:29:35 -07001097 StatsLog.write_non_chained(StatsLog.VIBRATOR_STATE_CHANGED, mCurVibUid, null,
1098 StatsLog.VIBRATOR_STATE_CHANGED__STATE__OFF, 0);
Michael Wright71216972017-01-31 18:33:54 +00001099 } catch (RemoteException e) { }
1100 mCurVibUid = -1;
1101 }
1102 }
1103
1104 private class VibrateThread extends Thread {
1105 private final VibrationEffect.Waveform mWaveform;
1106 private final int mUid;
1107 private final int mUsageHint;
1108
1109 private boolean mForceStop;
1110
1111 VibrateThread(VibrationEffect.Waveform waveform, int uid, int usageHint) {
1112 mWaveform = waveform;
1113 mUid = uid;
1114 mUsageHint = usageHint;
1115 mTmpWorkSource.set(uid);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -07001116 mWakeLock.setWorkSource(mTmpWorkSource);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001117 }
1118
Michael Wright71216972017-01-31 18:33:54 +00001119 private long delayLocked(long duration) {
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001120 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "delayLocked");
1121 try {
1122 long durationRemaining = duration;
1123 if (duration > 0) {
1124 final long bedtime = duration + SystemClock.uptimeMillis();
1125 do {
1126 try {
1127 this.wait(durationRemaining);
1128 }
1129 catch (InterruptedException e) { }
1130 if (mForceStop) {
1131 break;
1132 }
1133 durationRemaining = bedtime - SystemClock.uptimeMillis();
1134 } while (durationRemaining > 0);
1135 return duration - durationRemaining;
1136 }
1137 return 0;
1138 } finally {
1139 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001140 }
1141 }
1142
1143 public void run() {
1144 Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
Michael Wright71216972017-01-31 18:33:54 +00001145 mWakeLock.acquire();
1146 try {
1147 boolean finished = playWaveform();
1148 if (finished) {
1149 onVibrationFinished();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001150 }
Michael Wright71216972017-01-31 18:33:54 +00001151 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001152 mWakeLock.release();
1153 }
Michael Wright71216972017-01-31 18:33:54 +00001154 }
1155
1156 /**
1157 * Play the waveform.
1158 *
1159 * @return true if it finished naturally, false otherwise (e.g. it was canceled).
1160 */
1161 public boolean playWaveform() {
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001162 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playWaveform");
1163 try {
1164 synchronized (this) {
1165 final long[] timings = mWaveform.getTimings();
1166 final int[] amplitudes = mWaveform.getAmplitudes();
1167 final int len = timings.length;
1168 final int repeat = mWaveform.getRepeatIndex();
Michael Wright71216972017-01-31 18:33:54 +00001169
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001170 int index = 0;
1171 long onDuration = 0;
1172 while (!mForceStop) {
1173 if (index < len) {
1174 final int amplitude = amplitudes[index];
1175 final long duration = timings[index++];
1176 if (duration <= 0) {
1177 continue;
Michael Wright71216972017-01-31 18:33:54 +00001178 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001179 if (amplitude != 0) {
1180 if (onDuration <= 0) {
1181 // Telling the vibrator to start multiple times usually causes
1182 // effects to feel "choppy" because the motor resets at every on
1183 // command. Instead we figure out how long our next "on" period
1184 // is going to be, tell the motor to stay on for the full
1185 // duration, and then wake up to change the amplitude at the
1186 // appropriate intervals.
1187 onDuration = getTotalOnDuration(timings, amplitudes, index - 1,
1188 repeat);
1189 doVibratorOn(onDuration, amplitude, mUid, mUsageHint);
1190 } else {
1191 doVibratorSetAmplitude(amplitude);
1192 }
1193 }
Michael Wright71216972017-01-31 18:33:54 +00001194
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001195 long waitTime = delayLocked(duration);
1196 if (amplitude != 0) {
1197 onDuration -= waitTime;
1198 }
1199 } else if (repeat < 0) {
1200 break;
1201 } else {
1202 index = repeat;
Michael Wright71216972017-01-31 18:33:54 +00001203 }
Michael Wright71216972017-01-31 18:33:54 +00001204 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001205 return !mForceStop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001206 }
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001207 } finally {
1208 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
Michael Wright71216972017-01-31 18:33:54 +00001209 }
1210 }
1211
1212 public void cancel() {
1213 synchronized (this) {
1214 mThread.mForceStop = true;
1215 mThread.notify();
1216 }
1217 }
1218
1219 /**
1220 * Get the duration the vibrator will be on starting at startIndex until the next time it's
1221 * off.
1222 */
1223 private long getTotalOnDuration(
1224 long[] timings, int[] amplitudes, int startIndex, int repeatIndex) {
1225 int i = startIndex;
1226 long timing = 0;
1227 while(amplitudes[i] != 0) {
1228 timing += timings[i++];
1229 if (i >= timings.length) {
1230 if (repeatIndex >= 0) {
1231 i = repeatIndex;
Harpreet "Eli" Sanghac5526dd2018-10-30 16:14:33 +09001232 // prevent infinite loop
1233 repeatIndex = -1;
Michael Wright71216972017-01-31 18:33:54 +00001234 } else {
1235 break;
1236 }
1237 }
1238 if (i == startIndex) {
1239 return 1000;
Patrick Scott18dd5f02009-07-02 11:31:12 -04001240 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001241 }
Michael Wright71216972017-01-31 18:33:54 +00001242 return timing;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001243 }
Jeff Brown969579b2014-05-20 19:29:29 -07001244 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001245
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001246 BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
Jeff Brown969579b2014-05-20 19:29:29 -07001247 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001248 public void onReceive(Context context, Intent intent) {
1249 if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
Michael Wright71216972017-01-31 18:33:54 +00001250 synchronized (mLock) {
Jeff Brown969579b2014-05-20 19:29:29 -07001251 // When the system is entering a non-interactive state, we want
1252 // to cancel vibrations in case a misbehaving app has abandoned
1253 // them. However it may happen that the system is currently playing
1254 // haptic feedback as part of the transition. So we don't cancel
1255 // system vibrations.
1256 if (mCurrentVibration != null
Michael Wright35a0c672018-01-24 00:32:53 +00001257 && !(mCurrentVibration.isHapticFeedback()
1258 && mCurrentVibration.isFromSystem())) {
Jeff Brown969579b2014-05-20 19:29:29 -07001259 doCancelVibrateLocked();
Vairavan Srinivasan8a61f492011-05-13 10:47:20 -07001260 }
Patrick Scott18dd5f02009-07-02 11:31:12 -04001261 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001262 }
1263 }
1264 };
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -07001265
1266 @Override
1267 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -06001268 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -07001269
Michael Wright35a0c672018-01-24 00:32:53 +00001270 pw.println("Vibrator Service:");
Michael Wright71216972017-01-31 18:33:54 +00001271 synchronized (mLock) {
Michael Wright35a0c672018-01-24 00:32:53 +00001272 pw.print(" mCurrentVibration=");
1273 if (mCurrentVibration != null) {
1274 pw.println(mCurrentVibration.toInfo().toString());
1275 } else {
1276 pw.println("null");
1277 }
1278 pw.println(" mLowPowerMode=" + mLowPowerMode);
1279 pw.println(" mHapticFeedbackIntensity=" + mHapticFeedbackIntensity);
1280 pw.println(" mNotificationIntensity=" + mNotificationIntensity);
1281 pw.println("");
1282 pw.println(" Previous vibrations:");
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -07001283 for (VibrationInfo info : mPreviousVibrations) {
Michael Wright35a0c672018-01-24 00:32:53 +00001284 pw.print(" ");
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -07001285 pw.println(info.toString());
1286 }
1287 }
1288 }
Felipe Lemea5281002017-02-10 15:13:48 -08001289
1290 @Override
1291 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
1292 String[] args, ShellCallback callback, ResultReceiver resultReceiver)
1293 throws RemoteException {
1294 new VibratorShellCommand(this).exec(this, in, out, err, args, callback, resultReceiver);
1295 }
1296
1297 private final class VibratorShellCommand extends ShellCommand {
1298
Felipe Lemea5281002017-02-10 15:13:48 -08001299 private final IBinder mToken;
1300
1301 private VibratorShellCommand(IBinder token) {
1302 mToken = token;
1303 }
1304
1305 @Override
1306 public int onCommand(String cmd) {
1307 if ("vibrate".equals(cmd)) {
1308 return runVibrate();
Harpreet "Eli" Sanghac5526dd2018-10-30 16:14:33 +09001309 } else if ("waveform".equals(cmd)) {
1310 return runWaveform();
Harpreet "Eli" Sangha5ecc5362018-10-16 17:27:58 +09001311 } else if ("prebaked".equals(cmd)) {
1312 return runPrebaked();
Harpreet "Eli" Sanghac5526dd2018-10-30 16:14:33 +09001313 } else if ("cancel".equals(cmd)) {
1314 cancelVibrate(mToken);
1315 return 0;
Felipe Lemea5281002017-02-10 15:13:48 -08001316 }
1317 return handleDefaultCommands(cmd);
1318 }
1319
Harpreet "Eli" Sangha5ecc5362018-10-16 17:27:58 +09001320 private boolean checkDoNotDisturb() {
1321 try {
1322 final int zenMode = Settings.Global.getInt(mContext.getContentResolver(),
1323 Settings.Global.ZEN_MODE);
1324 if (zenMode != Settings.Global.ZEN_MODE_OFF) {
1325 try (PrintWriter pw = getOutPrintWriter();) {
1326 pw.print("Ignoring because device is on DND mode ");
1327 pw.println(DebugUtils.flagsToString(Settings.Global.class, "ZEN_MODE_",
1328 zenMode));
1329 return true;
1330 }
1331 }
1332 } catch (SettingNotFoundException e) {
1333 // ignore
1334 }
1335
1336 return false;
1337 }
1338
Felipe Lemea5281002017-02-10 15:13:48 -08001339 private int runVibrate() {
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001340 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runVibrate");
Felipe Leme5e2e6322017-07-14 17:25:59 -07001341 try {
Harpreet "Eli" Sangha5ecc5362018-10-16 17:27:58 +09001342 if (checkDoNotDisturb()) {
1343 return 0;
Felipe Leme5e2e6322017-07-14 17:25:59 -07001344 }
Felipe Leme5e2e6322017-07-14 17:25:59 -07001345
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001346 final long duration = Long.parseLong(getNextArgRequired());
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001347 String description = getNextArg();
1348 if (description == null) {
1349 description = "Shell command";
1350 }
Michael Wright71216972017-01-31 18:33:54 +00001351
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001352 VibrationEffect effect =
1353 VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE);
1354 vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
Alexey Kuzmine1f06b82018-06-20 17:48:43 +01001355 "Shell Command", mToken);
Alexey Kuzmine59145a2018-02-10 15:19:03 +00001356 return 0;
1357 } finally {
1358 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
1359 }
Felipe Lemea5281002017-02-10 15:13:48 -08001360 }
1361
Harpreet "Eli" Sanghac5526dd2018-10-30 16:14:33 +09001362 private int runWaveform() {
1363 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runWaveform");
1364 try {
1365 if (checkDoNotDisturb()) {
1366 return 0;
1367 }
1368
1369 String description = "Shell command";
1370 int repeat = -1;
1371 ArrayList<Integer> amplitudesList = null;
1372
1373 String opt;
1374 while ((opt = getNextOption()) != null) {
1375 switch (opt) {
1376 case "-d":
1377 description = getNextArgRequired();
1378 break;
1379 case "-r":
1380 repeat = Integer.parseInt(getNextArgRequired());
1381 break;
1382 case "-a":
1383 if (amplitudesList == null) {
1384 amplitudesList = new ArrayList<Integer>();
1385 }
1386 break;
1387 }
1388 }
1389
1390 ArrayList<Long> timingsList = new ArrayList<Long>();
1391
1392 String arg;
1393 while ((arg = getNextArg()) != null) {
1394 if (amplitudesList != null && amplitudesList.size() < timingsList.size()) {
1395 amplitudesList.add(Integer.parseInt(arg));
1396 } else {
1397 timingsList.add(Long.parseLong(arg));
1398 }
1399 }
1400
1401 VibrationEffect effect;
1402 long[] timings = timingsList.stream().mapToLong(Long::longValue).toArray();
1403 if (amplitudesList == null) {
1404 effect = VibrationEffect.createWaveform(timings, repeat);
1405 } else {
1406 int[] amplitudes =
1407 amplitudesList.stream().mapToInt(Integer::intValue).toArray();
1408 effect = VibrationEffect.createWaveform(timings, amplitudes, repeat);
1409 }
1410 vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
1411 "Shell Command", mToken);
1412 return 0;
1413 } finally {
1414 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
1415 }
1416 }
1417
Harpreet "Eli" Sangha5ecc5362018-10-16 17:27:58 +09001418 private int runPrebaked() {
1419 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runPrebaked");
1420 try {
1421 if (checkDoNotDisturb()) {
1422 return 0;
1423 }
1424
1425 final int id = Integer.parseInt(getNextArgRequired());
1426
1427 String description = getNextArg();
1428 if (description == null) {
1429 description = "Shell command";
1430 }
1431
1432 VibrationEffect effect =
1433 VibrationEffect.get(id, false);
1434 vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
1435 "Shell Command", mToken);
1436 return 0;
1437 } finally {
1438 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
1439 }
1440 }
1441
Felipe Lemea5281002017-02-10 15:13:48 -08001442 @Override
1443 public void onHelp() {
1444 try (PrintWriter pw = getOutPrintWriter();) {
1445 pw.println("Vibrator commands:");
1446 pw.println(" help");
1447 pw.println(" Prints this help text.");
1448 pw.println("");
1449 pw.println(" vibrate duration [description]");
Felipe Leme5e2e6322017-07-14 17:25:59 -07001450 pw.println(" Vibrates for duration milliseconds; ignored when device is on DND ");
1451 pw.println(" (Do Not Disturb) mode.");
Harpreet "Eli" Sanghac5526dd2018-10-30 16:14:33 +09001452 pw.println(" waveform [-d description] [-r index] [-a] duration [amplitude] ...");
1453 pw.println(" Vibrates for durations and amplitudes in list;");
1454 pw.println(" ignored when device is on DND (Do Not Disturb) mode.");
1455 pw.println(" If -r is provided, the waveform loops back to the specified");
1456 pw.println(" index (e.g. 0 loops from the beginning)");
1457 pw.println(" If -a is provided, the command accepts duration-amplitude pairs;");
1458 pw.println(" otherwise, it accepts durations only and alternates off/on");
1459 pw.println(" Duration is in milliseconds; amplitude is a scale of 1-255.");
Harpreet "Eli" Sangha5ecc5362018-10-16 17:27:58 +09001460 pw.println(" prebaked effect-id [description]");
1461 pw.println(" Vibrates with prebaked effect; ignored when device is on DND ");
1462 pw.println(" (Do Not Disturb) mode.");
Harpreet "Eli" Sanghac5526dd2018-10-30 16:14:33 +09001463 pw.println(" cancel");
1464 pw.println(" Cancels any active vibration");
Felipe Lemea5281002017-02-10 15:13:48 -08001465 pw.println("");
1466 }
1467 }
1468 }
1469
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001470}