blob: 7ba3d7d94392929297bed45b411c88def1b151b7 [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
Dianne Hackborna06de0f2012-12-11 16:34:47 -080019import android.app.AppOpsManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020import android.content.BroadcastReceiver;
21import android.content.Context;
22import android.content.Intent;
23import android.content.IntentFilter;
24import android.content.pm.PackageManager;
Michael Wright71216972017-01-31 18:33:54 +000025import android.content.res.Resources;
Jeff Brown7f6c2312012-04-13 20:38:38 -070026import android.database.ContentObserver;
27import android.hardware.input.InputManager;
Michael Wright71216972017-01-31 18:33:54 +000028import android.hardware.vibrator.V1_0.Constants.EffectStrength;
Brad Ebinger2d1c3b32016-05-12 18:05:17 -070029import android.media.AudioManager;
jackqdyulei455e90a2017-02-09 15:29:16 -080030import android.os.PowerSaveState;
Dianne Hackborn91268cf2013-06-13 19:06:50 -070031import android.os.BatteryStats;
Joe Onorato95e4f702009-03-24 19:29:09 -070032import android.os.Handler;
Mike Lockwood3a322132009-11-24 00:30:52 -050033import android.os.IVibratorService;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.os.PowerManager;
Dianne Hackborneb94fa72014-06-03 17:48:12 -070035import android.os.PowerManagerInternal;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.os.Process;
37import android.os.RemoteException;
Felipe Lemea5281002017-02-10 15:13:48 -080038import android.os.ResultReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.os.IBinder;
40import android.os.Binder;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080041import android.os.ServiceManager;
Felipe Lemea5281002017-02-10 15:13:48 -080042import android.os.ShellCallback;
43import android.os.ShellCommand;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044import android.os.SystemClock;
Jeff Brownd4935962012-09-25 13:27:20 -070045import android.os.UserHandle;
Jeff Brown7f6c2312012-04-13 20:38:38 -070046import android.os.Vibrator;
Michael Wright71216972017-01-31 18:33:54 +000047import android.os.VibrationEffect;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070048import android.os.WorkSource;
Jeff Brown7f6c2312012-04-13 20:38:38 -070049import android.provider.Settings;
50import android.provider.Settings.SettingNotFoundException;
Joe Onorato8a9b2202010-02-26 18:56:32 -080051import android.util.Slog;
Jeff Brown7f6c2312012-04-13 20:38:38 -070052import android.view.InputDevice;
John Spurlock7b414672014-07-18 13:02:39 -040053import android.media.AudioAttributes;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054
Dianne Hackborna06de0f2012-12-11 16:34:47 -080055import com.android.internal.app.IAppOpsService;
56import com.android.internal.app.IBatteryStats;
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060057import com.android.internal.util.DumpUtils;
jackqdyulei455e90a2017-02-09 15:29:16 -080058import com.android.server.power.BatterySaverPolicy.ServiceType;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080059
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -070060import java.io.FileDescriptor;
61import java.io.PrintWriter;
Jeff Brown7f6c2312012-04-13 20:38:38 -070062import java.util.ArrayList;
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -070063import java.util.Arrays;
Jeff Brown969579b2014-05-20 19:29:29 -070064import java.util.Iterator;
Patrick Scott18dd5f02009-07-02 11:31:12 -040065import java.util.LinkedList;
66import java.util.ListIterator;
67
Jeff Brown7f6c2312012-04-13 20:38:38 -070068public class VibratorService extends IVibratorService.Stub
69 implements InputManager.InputDeviceListener {
Mike Lockwood3a322132009-11-24 00:30:52 -050070 private static final String TAG = "VibratorService";
Jeff Brown82379ba2014-07-25 19:03:28 -070071 private static final boolean DEBUG = false;
Jorim Jaggi18f18ae2015-09-10 15:48:21 -070072 private static final String SYSTEM_UI_PACKAGE = "com.android.systemui";
Mike Lockwoodcc9a63d2009-11-10 07:50:28 -050073
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -070074 private final LinkedList<VibrationInfo> mPreviousVibrations;
75 private final int mPreviousVibrationsLimit;
Michael Wright71216972017-01-31 18:33:54 +000076 private final boolean mSupportsAmplitudeControl;
77 private final int mDefaultVibrationAmplitude;
78 private final VibrationEffect[] mFallbackEffects;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070079 private final WorkSource mTmpWorkSource = new WorkSource();
Jeff Brown7f6c2312012-04-13 20:38:38 -070080 private final Handler mH = new Handler();
Michael Wright71216972017-01-31 18:33:54 +000081 private final Object mLock = new Object();
Jeff Brown7f6c2312012-04-13 20:38:38 -070082
83 private final Context mContext;
84 private final PowerManager.WakeLock mWakeLock;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080085 private final IAppOpsService mAppOpsService;
86 private final IBatteryStats mBatteryStatsService;
Dianne Hackborneb94fa72014-06-03 17:48:12 -070087 private PowerManagerInternal mPowerManagerInternal;
Jeff Brown7f6c2312012-04-13 20:38:38 -070088 private InputManager mIm;
89
Michael Wright71216972017-01-31 18:33:54 +000090 private volatile VibrateThread mThread;
Jeff Brown7f6c2312012-04-13 20:38:38 -070091
Michael Wright71216972017-01-31 18:33:54 +000092 // mInputDeviceVibrators lock should be acquired after mLock, if both are
Jeff Brown7f6c2312012-04-13 20:38:38 -070093 // to be acquired
94 private final ArrayList<Vibrator> mInputDeviceVibrators = new ArrayList<Vibrator>();
95 private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators
96 private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators
97
Michael Wright71216972017-01-31 18:33:54 +000098 private Vibration mCurrentVibration;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080099 private int mCurVibUid = -1;
Ruchi Kandoi13b03af2014-05-07 20:10:32 -0700100 private boolean mLowPowerMode;
101 private SettingsObserver mSettingObserver;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800102
Jeff Brown7f6c2312012-04-13 20:38:38 -0700103 native static boolean vibratorExists();
Vincent Beckere6904fb2012-08-10 14:17:33 +0200104 native static void vibratorInit();
Jeff Brown7f6c2312012-04-13 20:38:38 -0700105 native static void vibratorOn(long milliseconds);
106 native static void vibratorOff();
Michael Wright71216972017-01-31 18:33:54 +0000107 native static boolean vibratorSupportsAmplitudeControl();
108 native static void vibratorSetAmplitude(int amplitude);
109 native static long vibratorPerformEffect(long effect, long strength);
Patrick Scott18dd5f02009-07-02 11:31:12 -0400110
Patrick Scott18dd5f02009-07-02 11:31:12 -0400111 private class Vibration implements IBinder.DeathRecipient {
112 private final IBinder mToken;
Michael Wright71216972017-01-31 18:33:54 +0000113 private final VibrationEffect mEffect;
114 private final long mStartTime;
115 private final int mUsageHint;
116 private final int mUid;
117 private final String mOpPkg;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400118
Michael Wright71216972017-01-31 18:33:54 +0000119 private Vibration(IBinder token, VibrationEffect effect,
120 int usageHint, int uid, String opPkg) {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400121 mToken = token;
Michael Wright71216972017-01-31 18:33:54 +0000122 mEffect = effect;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400123 mStartTime = SystemClock.uptimeMillis();
John Spurlock7b414672014-07-18 13:02:39 -0400124 mUsageHint = usageHint;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700125 mUid = uid;
Christoph Studer8fd7f1e2014-04-11 17:35:05 -0400126 mOpPkg = opPkg;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400127 }
128
129 public void binderDied() {
Michael Wright71216972017-01-31 18:33:54 +0000130 synchronized (mLock) {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400131 if (this == mCurrentVibration) {
132 doCancelVibrateLocked();
Patrick Scott18dd5f02009-07-02 11:31:12 -0400133 }
134 }
135 }
136
137 public boolean hasLongerTimeout(long millis) {
Michael Wright71216972017-01-31 18:33:54 +0000138 // If the current effect is a one shot vibration that will end after the given timeout
139 // for the new one shot vibration, then just let the current vibration finish. All
140 // other effect types will get pre-empted.
141 if (mEffect instanceof VibrationEffect.OneShot) {
142 VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) mEffect;
143 return mStartTime + oneShot.getTiming() > SystemClock.uptimeMillis() + millis;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400144 }
Michael Wright71216972017-01-31 18:33:54 +0000145 return false;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400146 }
Jeff Brown969579b2014-05-20 19:29:29 -0700147
148 public boolean isSystemHapticFeedback() {
Michael Wright71216972017-01-31 18:33:54 +0000149 boolean repeating = false;
150 if (mEffect instanceof VibrationEffect.Waveform) {
151 VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) mEffect;
152 repeating = (waveform.getRepeatIndex() < 0);
153 }
Jorim Jaggi18f18ae2015-09-10 15:48:21 -0700154 return (mUid == Process.SYSTEM_UID || mUid == 0 || SYSTEM_UI_PACKAGE.equals(mOpPkg))
Michael Wright71216972017-01-31 18:33:54 +0000155 && !repeating;
Jeff Brown969579b2014-05-20 19:29:29 -0700156 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400157 }
158
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700159 private static class VibrationInfo {
Michael Wright71216972017-01-31 18:33:54 +0000160 private final long mStartTime;
161 private final VibrationEffect mEffect;
162 private final int mUsageHint;
163 private final int mUid;
164 private final String mOpPkg;
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700165
Michael Wright71216972017-01-31 18:33:54 +0000166 public VibrationInfo(long startTime, VibrationEffect effect,
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700167 int usageHint, int uid, String opPkg) {
Michael Wright71216972017-01-31 18:33:54 +0000168 mStartTime = startTime;
169 mEffect = effect;
170 mUsageHint = usageHint;
171 mUid = uid;
172 mOpPkg = opPkg;
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700173 }
174
175 @Override
176 public String toString() {
177 return new StringBuilder()
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700178 .append(", startTime: ")
Michael Wright71216972017-01-31 18:33:54 +0000179 .append(mStartTime)
180 .append(", effect: ")
181 .append(mEffect)
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700182 .append(", usageHint: ")
Michael Wright71216972017-01-31 18:33:54 +0000183 .append(mUsageHint)
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700184 .append(", uid: ")
Michael Wright71216972017-01-31 18:33:54 +0000185 .append(mUid)
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700186 .append(", opPkg: ")
Michael Wright71216972017-01-31 18:33:54 +0000187 .append(mOpPkg)
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700188 .toString();
189 }
190 }
191
Mike Lockwood3a322132009-11-24 00:30:52 -0500192 VibratorService(Context context) {
Vincent Beckere6904fb2012-08-10 14:17:33 +0200193 vibratorInit();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 // Reset the hardware to a default state, in case this is a runtime
195 // restart instead of a fresh boot.
196 vibratorOff();
197
Michael Wright71216972017-01-31 18:33:54 +0000198 mSupportsAmplitudeControl = vibratorSupportsAmplitudeControl();
199
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800200 mContext = context;
Michael Wright71216972017-01-31 18:33:54 +0000201 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700202 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 mWakeLock.setReferenceCounted(true);
204
Michael Wright71216972017-01-31 18:33:54 +0000205 mAppOpsService =
206 IAppOpsService.Stub.asInterface(ServiceManager.getService(Context.APP_OPS_SERVICE));
Dianne Hackborn91268cf2013-06-13 19:06:50 -0700207 mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
208 BatteryStats.SERVICE_NAME));
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800209
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700210 mPreviousVibrationsLimit = mContext.getResources().getInteger(
211 com.android.internal.R.integer.config_previousVibrationsDumpLimit);
212
Michael Wright71216972017-01-31 18:33:54 +0000213 mDefaultVibrationAmplitude = mContext.getResources().getInteger(
214 com.android.internal.R.integer.config_defaultVibrationAmplitude);
215
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700216 mPreviousVibrations = new LinkedList<>();
Patrick Scott18dd5f02009-07-02 11:31:12 -0400217
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 IntentFilter filter = new IntentFilter();
219 filter.addAction(Intent.ACTION_SCREEN_OFF);
220 context.registerReceiver(mIntentReceiver, filter);
Michael Wright71216972017-01-31 18:33:54 +0000221
222 long[] clickEffectTimings = getLongIntArray(context.getResources(),
223 com.android.internal.R.array.config_virtualKeyVibePattern);
Michael Wright57d94d92017-05-31 14:44:45 +0100224 VibrationEffect clickEffect = createEffect(clickEffectTimings);
Michael Wright71216972017-01-31 18:33:54 +0000225 VibrationEffect doubleClickEffect = VibrationEffect.createWaveform(
226 new long[] {0, 30, 100, 30} /*timings*/, -1);
Michael Wright57d94d92017-05-31 14:44:45 +0100227 long[] tickEffectTimings = getLongIntArray(context.getResources(),
228 com.android.internal.R.array.config_clockTickVibePattern);
229 VibrationEffect tickEffect = createEffect(tickEffectTimings);
Michael Wright71216972017-01-31 18:33:54 +0000230
Michael Wright57d94d92017-05-31 14:44:45 +0100231 mFallbackEffects = new VibrationEffect[] { clickEffect, doubleClickEffect, tickEffect };
232 }
233
234 private static VibrationEffect createEffect(long[] timings) {
235 if (timings == null || timings.length == 0) {
236 return null;
237 } else if (timings.length == 1) {
238 return VibrationEffect.createOneShot(timings[0], VibrationEffect.DEFAULT_AMPLITUDE);
239 } else {
240 return VibrationEffect.createWaveform(timings, -1);
241 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 }
243
Jeff Brown7f6c2312012-04-13 20:38:38 -0700244 public void systemReady() {
Yohei Yukawa8ce2a532015-11-25 20:35:04 -0800245 mIm = mContext.getSystemService(InputManager.class);
Ruchi Kandoi13b03af2014-05-07 20:10:32 -0700246 mSettingObserver = new SettingsObserver(mH);
Jeff Brownd4935962012-09-25 13:27:20 -0700247
Dianne Hackborneb94fa72014-06-03 17:48:12 -0700248 mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
249 mPowerManagerInternal.registerLowPowerModeObserver(
250 new PowerManagerInternal.LowPowerModeListener() {
jackqdyulei455e90a2017-02-09 15:29:16 -0800251 @Override
252 public int getServiceType() {
253 return ServiceType.VIBRATION;
254 }
255
256 @Override
257 public void onLowPowerModeChanged(PowerSaveState result) {
Michael Wright71216972017-01-31 18:33:54 +0000258 updateVibrators();
jackqdyulei455e90a2017-02-09 15:29:16 -0800259 }
Dianne Hackborneb94fa72014-06-03 17:48:12 -0700260 });
261
Jeff Brown7f6c2312012-04-13 20:38:38 -0700262 mContext.getContentResolver().registerContentObserver(
Ruchi Kandoi13b03af2014-05-07 20:10:32 -0700263 Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES),
264 true, mSettingObserver, UserHandle.USER_ALL);
265
Jeff Brownd4935962012-09-25 13:27:20 -0700266 mContext.registerReceiver(new BroadcastReceiver() {
267 @Override
268 public void onReceive(Context context, Intent intent) {
Michael Wright71216972017-01-31 18:33:54 +0000269 updateVibrators();
Jeff Brownd4935962012-09-25 13:27:20 -0700270 }
271 }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mH);
272
Michael Wright71216972017-01-31 18:33:54 +0000273 updateVibrators();
Dianne Hackbornea9020e2010-11-04 11:39:12 -0700274 }
Jeff Brown7f6c2312012-04-13 20:38:38 -0700275
Ruchi Kandoi13b03af2014-05-07 20:10:32 -0700276 private final class SettingsObserver extends ContentObserver {
277 public SettingsObserver(Handler handler) {
278 super(handler);
279 }
280
281 @Override
282 public void onChange(boolean SelfChange) {
Michael Wright71216972017-01-31 18:33:54 +0000283 updateVibrators();
Ruchi Kandoi13b03af2014-05-07 20:10:32 -0700284 }
285 }
286
Jeff Brown82379ba2014-07-25 19:03:28 -0700287 @Override // Binder call
Jeff Brown7f6c2312012-04-13 20:38:38 -0700288 public boolean hasVibrator() {
289 return doVibratorExists();
290 }
291
Michael Wright71216972017-01-31 18:33:54 +0000292 @Override // Binder call
293 public boolean hasAmplitudeControl() {
294 synchronized (mInputDeviceVibrators) {
295 // Input device vibrators don't support amplitude controls yet, but are still used over
296 // the system vibrator when connected.
297 return mSupportsAmplitudeControl && mInputDeviceVibrators.isEmpty();
298 }
299 }
300
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800301 private void verifyIncomingUid(int uid) {
302 if (uid == Binder.getCallingUid()) {
303 return;
304 }
305 if (Binder.getCallingPid() == Process.myPid()) {
306 return;
307 }
308 mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
309 Binder.getCallingPid(), Binder.getCallingUid(), null);
310 }
311
Michael Wright71216972017-01-31 18:33:54 +0000312 /**
313 * Validate the incoming VibrationEffect.
314 *
315 * We can't throw exceptions here since we might be called from some system_server component,
316 * which would bring the whole system down.
317 *
318 * @return whether the VibrationEffect is valid
319 */
320 private static boolean verifyVibrationEffect(VibrationEffect effect) {
321 if (effect == null) {
322 // Effect must not be null.
323 Slog.wtf(TAG, "effect must not be null");
324 return false;
325 }
326 try {
327 effect.validate();
328 } catch (Exception e) {
329 Slog.wtf(TAG, "Encountered issue when verifying VibrationEffect.", e);
330 return false;
331 }
332 return true;
333 }
334
335 private static long[] getLongIntArray(Resources r, int resid) {
336 int[] ar = r.getIntArray(resid);
337 if (ar == null) {
338 return null;
339 }
340 long[] out = new long[ar.length];
341 for (int i = 0; i < ar.length; i++) {
342 out[i] = ar[i];
343 }
344 return out;
345 }
346
Jeff Brown82379ba2014-07-25 19:03:28 -0700347 @Override // Binder call
Michael Wright71216972017-01-31 18:33:54 +0000348 public void vibrate(int uid, String opPkg, VibrationEffect effect, int usageHint,
John Spurlock1af30c72014-03-10 08:33:35 -0400349 IBinder token) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700350 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
351 != PackageManager.PERMISSION_GRANTED) {
352 throw new SecurityException("Requires VIBRATE permission");
353 }
Michael Wright71216972017-01-31 18:33:54 +0000354 if (token == null) {
355 Slog.e(TAG, "token must not be null");
356 return;
357 }
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800358 verifyIncomingUid(uid);
Michael Wright71216972017-01-31 18:33:54 +0000359 if (!verifyVibrationEffect(effect)) {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400360 return;
361 }
Jeff Brown7f6c2312012-04-13 20:38:38 -0700362
Michael Wright71216972017-01-31 18:33:54 +0000363 // If our current vibration is longer than the new vibration and is the same amplitude,
364 // then just let the current one finish.
365 if (effect instanceof VibrationEffect.OneShot
366 && mCurrentVibration != null
367 && mCurrentVibration.mEffect instanceof VibrationEffect.OneShot) {
368 VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect;
369 VibrationEffect.OneShot currentOneShot =
370 (VibrationEffect.OneShot) mCurrentVibration.mEffect;
371 if (mCurrentVibration.hasLongerTimeout(newOneShot.getTiming())
372 && newOneShot.getAmplitude() == currentOneShot.getAmplitude()) {
373 if (DEBUG) {
374 Slog.e(TAG, "Ignoring incoming vibration in favor of current vibration");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800376 return;
377 }
Michael Wright71216972017-01-31 18:33:54 +0000378 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379
Michael Wright71216972017-01-31 18:33:54 +0000380 Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg);
381
382 // Only link against waveforms since they potentially don't have a finish if
383 // they're repeating. Let other effects just play out until they're done.
384 if (effect instanceof VibrationEffect.Waveform) {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400385 try {
386 token.linkToDeath(vib, 0);
387 } catch (RemoteException e) {
388 return;
389 }
Michael Wright71216972017-01-31 18:33:54 +0000390 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400391
Michael Wright71216972017-01-31 18:33:54 +0000392
393 long ident = Binder.clearCallingIdentity();
394 try {
395 synchronized (mLock) {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400396 doCancelVibrateLocked();
Michael Wright71216972017-01-31 18:33:54 +0000397 startVibrationLocked(vib);
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700398 addToPreviousVibrationsLocked(vib);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 }
Michael Wright71216972017-01-31 18:33:54 +0000400 } finally {
401 Binder.restoreCallingIdentity(ident);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 }
403 }
404
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700405 private void addToPreviousVibrationsLocked(Vibration vib) {
406 if (mPreviousVibrations.size() > mPreviousVibrationsLimit) {
407 mPreviousVibrations.removeFirst();
408 }
Michael Wright71216972017-01-31 18:33:54 +0000409 mPreviousVibrations.addLast(new VibrationInfo(
410 vib.mStartTime, vib.mEffect, vib.mUsageHint, vib.mUid, vib.mOpPkg));
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700411 }
412
Jeff Brown82379ba2014-07-25 19:03:28 -0700413 @Override // Binder call
Patrick Scott18dd5f02009-07-02 11:31:12 -0400414 public void cancelVibrate(IBinder token) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415 mContext.enforceCallingOrSelfPermission(
416 android.Manifest.permission.VIBRATE,
417 "cancelVibrate");
418
Michael Wright71216972017-01-31 18:33:54 +0000419 synchronized (mLock) {
420 if (mCurrentVibration != null && mCurrentVibration.mToken == token) {
421 if (DEBUG) {
422 Slog.d(TAG, "Canceling vibration.");
423 }
424 long ident = Binder.clearCallingIdentity();
425 try {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400426 doCancelVibrateLocked();
Michael Wright71216972017-01-31 18:33:54 +0000427 } finally {
428 Binder.restoreCallingIdentity(ident);
Patrick Scott18dd5f02009-07-02 11:31:12 -0400429 }
430 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800431 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 }
Eric Olsenf42f15c2009-10-29 16:42:03 -0700433
Michael Wright71216972017-01-31 18:33:54 +0000434 private final Runnable mVibrationEndRunnable = new Runnable() {
Jeff Brown82379ba2014-07-25 19:03:28 -0700435 @Override
Patrick Scott18dd5f02009-07-02 11:31:12 -0400436 public void run() {
Michael Wright71216972017-01-31 18:33:54 +0000437 onVibrationFinished();
Patrick Scott18dd5f02009-07-02 11:31:12 -0400438 }
439 };
440
Patrick Scott18dd5f02009-07-02 11:31:12 -0400441 private void doCancelVibrateLocked() {
Michael Wright71216972017-01-31 18:33:54 +0000442 mH.removeCallbacks(mVibrationEndRunnable);
Patrick Scott18dd5f02009-07-02 11:31:12 -0400443 if (mThread != null) {
Michael Wright71216972017-01-31 18:33:54 +0000444 mThread.cancel();
Patrick Scott18dd5f02009-07-02 11:31:12 -0400445 mThread = null;
446 }
Jeff Brown7f6c2312012-04-13 20:38:38 -0700447 doVibratorOff();
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800448 reportFinishVibrationLocked();
Patrick Scott18dd5f02009-07-02 11:31:12 -0400449 }
450
Michael Wright71216972017-01-31 18:33:54 +0000451 // Callback for whenever the current vibration has finished played out
452 public void onVibrationFinished() {
453 if (DEBUG) {
454 Slog.e(TAG, "Vibration finished, cleaning up");
Patrick Scott18dd5f02009-07-02 11:31:12 -0400455 }
Michael Wright71216972017-01-31 18:33:54 +0000456 synchronized (mLock) {
457 // Make sure the vibration is really done. This also reports that the vibration is
458 // finished.
459 doCancelVibrateLocked();
460 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400461 }
462
Patrick Scott18dd5f02009-07-02 11:31:12 -0400463 private void startVibrationLocked(final Vibration vib) {
Michael Wright71216972017-01-31 18:33:54 +0000464 if (mLowPowerMode && vib.mUsageHint != AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
465 if (DEBUG) {
466 Slog.e(TAG, "Vibrate ignored, low power mode");
Ruchi Kandoi664703d2014-05-09 16:01:31 -0700467 }
Michael Wright71216972017-01-31 18:33:54 +0000468 return;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800469 }
Michael Wright71216972017-01-31 18:33:54 +0000470
471 if (vib.mUsageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE &&
472 !shouldVibrateForRingtone()) {
473 if (DEBUG) {
474 Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones");
475 }
476 return;
477 }
478
479 final int mode = getAppOpMode(vib);
480 if (mode != AppOpsManager.MODE_ALLOWED) {
481 if (mode == AppOpsManager.MODE_ERRORED) {
482 // We might be getting calls from within system_server, so we don't actually want
483 // to throw a SecurityException here.
484 Slog.w(TAG, "Would be an error: vibrate from uid " + vib.mUid);
485 }
486 return;
487 }
488 startVibrationInnerLocked(vib);
489 }
490
491 private void startVibrationInnerLocked(Vibration vib) {
492 mCurrentVibration = vib;
493 if (vib.mEffect instanceof VibrationEffect.OneShot) {
494 VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.mEffect;
495 doVibratorOn(oneShot.getTiming(), oneShot.getAmplitude(), vib.mUid, vib.mUsageHint);
496 mH.postDelayed(mVibrationEndRunnable, oneShot.getTiming());
497 } else if (vib.mEffect instanceof VibrationEffect.Waveform) {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400498 // mThread better be null here. doCancelVibrate should always be
499 // called before startNextVibrationLocked or startVibrationLocked.
Michael Wright71216972017-01-31 18:33:54 +0000500 VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.mEffect;
501 mThread = new VibrateThread(waveform, vib.mUid, vib.mUsageHint);
Patrick Scott18dd5f02009-07-02 11:31:12 -0400502 mThread.start();
Michael Wright71216972017-01-31 18:33:54 +0000503 } else if (vib.mEffect instanceof VibrationEffect.Prebaked) {
504 long timeout = doVibratorPrebakedEffectLocked(vib);
505 if (timeout > 0) {
506 mH.postDelayed(mVibrationEndRunnable, timeout);
507 }
508 } else {
509 Slog.e(TAG, "Unknown vibration type, ignoring");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800510 }
511 }
512
Brad Ebinger2d1c3b32016-05-12 18:05:17 -0700513 private boolean shouldVibrateForRingtone() {
514 AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
Brad Ebingerdcbdc0d2016-06-23 17:42:30 -0700515 int ringerMode = audioManager.getRingerModeInternal();
Brad Ebinger2d1c3b32016-05-12 18:05:17 -0700516 // "Also vibrate for calls" Setting in Sound
517 if (Settings.System.getInt(
518 mContext.getContentResolver(), Settings.System.VIBRATE_WHEN_RINGING, 0) != 0) {
519 return ringerMode != AudioManager.RINGER_MODE_SILENT;
520 } else {
521 return ringerMode == AudioManager.RINGER_MODE_VIBRATE;
522 }
523 }
524
Michael Wright71216972017-01-31 18:33:54 +0000525 private int getAppOpMode(Vibration vib) {
526 int mode;
527 try {
528 mode = mAppOpsService.checkAudioOperation(AppOpsManager.OP_VIBRATE,
529 vib.mUsageHint, vib.mUid, vib.mOpPkg);
530 if (mode == AppOpsManager.MODE_ALLOWED) {
531 mode = mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
532 AppOpsManager.OP_VIBRATE, vib.mUid, vib.mOpPkg);
533 }
534 } catch (RemoteException e) {
535 Slog.e(TAG, "Failed to get appop mode for vibration!", e);
536 mode = AppOpsManager.MODE_IGNORED;
537 }
538 return mode;
539 }
540
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800541 private void reportFinishVibrationLocked() {
542 if (mCurrentVibration != null) {
543 try {
Dianne Hackborne98f5db2013-07-17 17:23:25 -0700544 mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
545 AppOpsManager.OP_VIBRATE, mCurrentVibration.mUid,
Christoph Studer8fd7f1e2014-04-11 17:35:05 -0400546 mCurrentVibration.mOpPkg);
Michael Wright71216972017-01-31 18:33:54 +0000547 } catch (RemoteException e) { }
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800548 mCurrentVibration = null;
549 }
550 }
551
Mathias Jeppssonb23949b2010-09-28 14:45:23 +0200552 private void unlinkVibration(Vibration vib) {
Michael Wright71216972017-01-31 18:33:54 +0000553 if (vib.mEffect instanceof VibrationEffect.Waveform) {
Mathias Jeppssonb23949b2010-09-28 14:45:23 +0200554 vib.mToken.unlinkToDeath(vib, 0);
555 }
556 }
557
Michael Wright71216972017-01-31 18:33:54 +0000558 private void updateVibrators() {
559 synchronized (mLock) {
560 boolean devicesUpdated = updateInputDeviceVibratorsLocked();
561 boolean lowPowerModeUpdated = updateLowPowerModeLocked();
Jeff Brown7f6c2312012-04-13 20:38:38 -0700562
Michael Wright71216972017-01-31 18:33:54 +0000563 if (devicesUpdated || lowPowerModeUpdated) {
564 // If the state changes out from under us then just reset.
565 doCancelVibrateLocked();
566 }
567 }
568 }
Jeff Brown82065252012-04-16 13:19:05 -0700569
Michael Wright71216972017-01-31 18:33:54 +0000570 private boolean updateInputDeviceVibratorsLocked() {
571 boolean changed = false;
572 boolean vibrateInputDevices = false;
573 try {
574 vibrateInputDevices = Settings.System.getIntForUser(
575 mContext.getContentResolver(),
576 Settings.System.VIBRATE_INPUT_DEVICES, UserHandle.USER_CURRENT) > 0;
577 } catch (SettingNotFoundException snfe) {
578 }
579 if (vibrateInputDevices != mVibrateInputDevicesSetting) {
580 changed = true;
581 mVibrateInputDevicesSetting = vibrateInputDevices;
582 }
Ruchi Kandoi13b03af2014-05-07 20:10:32 -0700583
Michael Wright71216972017-01-31 18:33:54 +0000584 if (mVibrateInputDevicesSetting) {
585 if (!mInputDeviceListenerRegistered) {
586 mInputDeviceListenerRegistered = true;
587 mIm.registerInputDeviceListener(this, mH);
588 }
589 } else {
590 if (mInputDeviceListenerRegistered) {
591 mInputDeviceListenerRegistered = false;
592 mIm.unregisterInputDeviceListener(this);
593 }
594 }
Jeff Brown82065252012-04-16 13:19:05 -0700595
Michael Wright71216972017-01-31 18:33:54 +0000596 mInputDeviceVibrators.clear();
597 if (mVibrateInputDevicesSetting) {
598 int[] ids = mIm.getInputDeviceIds();
599 for (int i = 0; i < ids.length; i++) {
600 InputDevice device = mIm.getInputDevice(ids[i]);
601 Vibrator vibrator = device.getVibrator();
602 if (vibrator.hasVibrator()) {
603 mInputDeviceVibrators.add(vibrator);
Jeff Brown7f6c2312012-04-13 20:38:38 -0700604 }
605 }
Michael Wright71216972017-01-31 18:33:54 +0000606 return true;
Jeff Brown7f6c2312012-04-13 20:38:38 -0700607 }
Michael Wright71216972017-01-31 18:33:54 +0000608 return changed;
609 }
610
611 private boolean updateLowPowerModeLocked() {
612 boolean lowPowerMode = mPowerManagerInternal
613 .getLowPowerState(ServiceType.VIBRATION).batterySaverEnabled;
614 if (lowPowerMode != mLowPowerMode) {
615 mLowPowerMode = lowPowerMode;
616 return true;
617 }
618 return false;
Jeff Brown7f6c2312012-04-13 20:38:38 -0700619 }
620
621 @Override
622 public void onInputDeviceAdded(int deviceId) {
Michael Wright71216972017-01-31 18:33:54 +0000623 updateVibrators();
Jeff Brown7f6c2312012-04-13 20:38:38 -0700624 }
625
626 @Override
627 public void onInputDeviceChanged(int deviceId) {
Michael Wright71216972017-01-31 18:33:54 +0000628 updateVibrators();
Jeff Brown7f6c2312012-04-13 20:38:38 -0700629 }
630
631 @Override
632 public void onInputDeviceRemoved(int deviceId) {
Michael Wright71216972017-01-31 18:33:54 +0000633 updateVibrators();
Jeff Brown7f6c2312012-04-13 20:38:38 -0700634 }
635
636 private boolean doVibratorExists() {
Jeff Brown1064a502012-05-02 16:51:37 -0700637 // For now, we choose to ignore the presence of input devices that have vibrators
638 // when reporting whether the device has a vibrator. Applications often use this
639 // information to decide whether to enable certain features so they expect the
640 // result of hasVibrator() to be constant. For now, just report whether
641 // the device has a built-in vibrator.
642 //synchronized (mInputDeviceVibrators) {
643 // return !mInputDeviceVibrators.isEmpty() || vibratorExists();
644 //}
Dianne Hackbornc2293022013-02-06 23:14:49 -0800645 return vibratorExists();
Jeff Brown7f6c2312012-04-13 20:38:38 -0700646 }
647
Michael Wright71216972017-01-31 18:33:54 +0000648 private void doVibratorOn(long millis, int amplitude, int uid, int usageHint) {
Jeff Brown7f6c2312012-04-13 20:38:38 -0700649 synchronized (mInputDeviceVibrators) {
Michael Wright71216972017-01-31 18:33:54 +0000650 if (amplitude == VibrationEffect.DEFAULT_AMPLITUDE) {
651 amplitude = mDefaultVibrationAmplitude;
652 }
Jeff Brown82379ba2014-07-25 19:03:28 -0700653 if (DEBUG) {
Michael Wright71216972017-01-31 18:33:54 +0000654 Slog.d(TAG, "Turning vibrator on for " + millis + " ms" +
655 " with amplitude " + amplitude + ".");
Jeff Brown82379ba2014-07-25 19:03:28 -0700656 }
Michael Wright71216972017-01-31 18:33:54 +0000657 noteVibratorOnLocked(uid, millis);
Jeff Brown7f6c2312012-04-13 20:38:38 -0700658 final int vibratorCount = mInputDeviceVibrators.size();
659 if (vibratorCount != 0) {
Michael Wright71216972017-01-31 18:33:54 +0000660 final AudioAttributes attributes =
661 new AudioAttributes.Builder().setUsage(usageHint).build();
Jeff Brown7f6c2312012-04-13 20:38:38 -0700662 for (int i = 0; i < vibratorCount; i++) {
John Spurlock7b414672014-07-18 13:02:39 -0400663 mInputDeviceVibrators.get(i).vibrate(millis, attributes);
Jeff Brown7f6c2312012-04-13 20:38:38 -0700664 }
665 } else {
Michael Wright71216972017-01-31 18:33:54 +0000666 // Note: ordering is important here! Many haptic drivers will reset their amplitude
667 // when enabled, so we always have to enable frst, then set the amplitude.
Jeff Brown7f6c2312012-04-13 20:38:38 -0700668 vibratorOn(millis);
Michael Wright71216972017-01-31 18:33:54 +0000669 doVibratorSetAmplitude(amplitude);
Jeff Brown7f6c2312012-04-13 20:38:38 -0700670 }
671 }
672 }
673
Michael Wright71216972017-01-31 18:33:54 +0000674 private void doVibratorSetAmplitude(int amplitude) {
675 if (mSupportsAmplitudeControl) {
676 vibratorSetAmplitude(amplitude);
677 }
678 }
679
Jeff Brown7f6c2312012-04-13 20:38:38 -0700680 private void doVibratorOff() {
681 synchronized (mInputDeviceVibrators) {
Jeff Brown82379ba2014-07-25 19:03:28 -0700682 if (DEBUG) {
683 Slog.d(TAG, "Turning vibrator off.");
684 }
Michael Wright71216972017-01-31 18:33:54 +0000685 noteVibratorOffLocked();
Jeff Brown7f6c2312012-04-13 20:38:38 -0700686 final int vibratorCount = mInputDeviceVibrators.size();
687 if (vibratorCount != 0) {
688 for (int i = 0; i < vibratorCount; i++) {
689 mInputDeviceVibrators.get(i).cancel();
690 }
691 } else {
692 vibratorOff();
693 }
694 }
695 }
696
Michael Wright71216972017-01-31 18:33:54 +0000697 private long doVibratorPrebakedEffectLocked(Vibration vib) {
698 synchronized (mInputDeviceVibrators) {
699 VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.mEffect;
700 // Input devices don't support prebaked effect, so skip trying it with them.
701 final int vibratorCount = mInputDeviceVibrators.size();
702 if (vibratorCount == 0) {
703 long timeout = vibratorPerformEffect(prebaked.getId(), EffectStrength.MEDIUM);
704 if (timeout > 0) {
705 noteVibratorOnLocked(vib.mUid, timeout);
706 return timeout;
707 }
708 }
709 final int id = prebaked.getId();
Erik Wolsheimer017939e2017-05-24 11:18:25 -0700710 if (id < 0 || id >= mFallbackEffects.length || mFallbackEffects[id] == null) {
Michael Wright71216972017-01-31 18:33:54 +0000711 Slog.w(TAG, "Failed to play prebaked effect, no fallback");
712 return 0;
713 }
714 VibrationEffect effect = mFallbackEffects[id];
715 Vibration fallbackVib =
716 new Vibration(vib.mToken, effect, vib.mUsageHint, vib.mUid, vib.mOpPkg);
717 startVibrationInnerLocked(fallbackVib);
718 }
719 return 0;
720 }
Eric Olsenf42f15c2009-10-29 16:42:03 -0700721
Michael Wright71216972017-01-31 18:33:54 +0000722 private void noteVibratorOnLocked(int uid, long millis) {
723 try {
724 mBatteryStatsService.noteVibratorOn(uid, millis);
725 mCurVibUid = uid;
726 } catch (RemoteException e) {
727 }
728 }
729
730 private void noteVibratorOffLocked() {
731 if (mCurVibUid >= 0) {
732 try {
733 mBatteryStatsService.noteVibratorOff(mCurVibUid);
734 } catch (RemoteException e) { }
735 mCurVibUid = -1;
736 }
737 }
738
739 private class VibrateThread extends Thread {
740 private final VibrationEffect.Waveform mWaveform;
741 private final int mUid;
742 private final int mUsageHint;
743
744 private boolean mForceStop;
745
746 VibrateThread(VibrationEffect.Waveform waveform, int uid, int usageHint) {
747 mWaveform = waveform;
748 mUid = uid;
749 mUsageHint = usageHint;
750 mTmpWorkSource.set(uid);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700751 mWakeLock.setWorkSource(mTmpWorkSource);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800752 }
753
Michael Wright71216972017-01-31 18:33:54 +0000754 private long delayLocked(long duration) {
755 long durationRemaining = duration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800756 if (duration > 0) {
Michael Wright71216972017-01-31 18:33:54 +0000757 final long bedtime = duration + SystemClock.uptimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800758 do {
759 try {
Michael Wright71216972017-01-31 18:33:54 +0000760 this.wait(durationRemaining);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800761 }
Michael Wright71216972017-01-31 18:33:54 +0000762 catch (InterruptedException e) { }
763 if (mForceStop) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800764 break;
765 }
Michael Wright71216972017-01-31 18:33:54 +0000766 durationRemaining = bedtime - SystemClock.uptimeMillis();
767 } while (durationRemaining > 0);
768 return duration - durationRemaining;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800769 }
Michael Wright71216972017-01-31 18:33:54 +0000770 return 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800771 }
772
773 public void run() {
774 Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
Michael Wright71216972017-01-31 18:33:54 +0000775 mWakeLock.acquire();
776 try {
777 boolean finished = playWaveform();
778 if (finished) {
779 onVibrationFinished();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800780 }
Michael Wright71216972017-01-31 18:33:54 +0000781 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800782 mWakeLock.release();
783 }
Michael Wright71216972017-01-31 18:33:54 +0000784 }
785
786 /**
787 * Play the waveform.
788 *
789 * @return true if it finished naturally, false otherwise (e.g. it was canceled).
790 */
791 public boolean playWaveform() {
792 synchronized (this) {
793 final long[] timings = mWaveform.getTimings();
794 final int[] amplitudes = mWaveform.getAmplitudes();
795 final int len = timings.length;
796 final int repeat = mWaveform.getRepeatIndex();
797
798 int index = 0;
799 long onDuration = 0;
800 while (!mForceStop) {
801 if (index < len) {
802 final int amplitude = amplitudes[index];
803 final long duration = timings[index++];
804 if (duration <= 0) {
805 continue;
806 }
807 if (amplitude != 0) {
808 if (onDuration <= 0) {
809 // Telling the vibrator to start multiple times usually causes
810 // effects to feel "choppy" because the motor resets at every on
811 // command. Instead we figure out how long our next "on" period is
812 // going to be, tell the motor to stay on for the full duration,
813 // and then wake up to change the amplitude at the appropriate
814 // intervals.
815 onDuration =
816 getTotalOnDuration(timings, amplitudes, index - 1, repeat);
817 doVibratorOn(onDuration, amplitude, mUid, mUsageHint);
818 } else {
819 doVibratorSetAmplitude(amplitude);
820 }
821 }
822
823 long waitTime = delayLocked(duration);
824 if (amplitude != 0) {
825 onDuration -= waitTime;
826 }
827 } else if (repeat < 0) {
828 break;
829 } else {
830 index = repeat;
831 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800832 }
Michael Wright71216972017-01-31 18:33:54 +0000833 return !mForceStop;
834 }
835 }
836
837 public void cancel() {
838 synchronized (this) {
839 mThread.mForceStop = true;
840 mThread.notify();
841 }
842 }
843
844 /**
845 * Get the duration the vibrator will be on starting at startIndex until the next time it's
846 * off.
847 */
848 private long getTotalOnDuration(
849 long[] timings, int[] amplitudes, int startIndex, int repeatIndex) {
850 int i = startIndex;
851 long timing = 0;
852 while(amplitudes[i] != 0) {
853 timing += timings[i++];
854 if (i >= timings.length) {
855 if (repeatIndex >= 0) {
856 i = repeatIndex;
857 } else {
858 break;
859 }
860 }
861 if (i == startIndex) {
862 return 1000;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400863 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800864 }
Michael Wright71216972017-01-31 18:33:54 +0000865 return timing;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800866 }
Jeff Brown969579b2014-05-20 19:29:29 -0700867 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800868
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800869 BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
Jeff Brown969579b2014-05-20 19:29:29 -0700870 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800871 public void onReceive(Context context, Intent intent) {
872 if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
Michael Wright71216972017-01-31 18:33:54 +0000873 synchronized (mLock) {
Jeff Brown969579b2014-05-20 19:29:29 -0700874 // When the system is entering a non-interactive state, we want
875 // to cancel vibrations in case a misbehaving app has abandoned
876 // them. However it may happen that the system is currently playing
877 // haptic feedback as part of the transition. So we don't cancel
878 // system vibrations.
879 if (mCurrentVibration != null
880 && !mCurrentVibration.isSystemHapticFeedback()) {
881 doCancelVibrateLocked();
Vairavan Srinivasan8a61f492011-05-13 10:47:20 -0700882 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400883 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800884 }
885 }
886 };
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700887
888 @Override
889 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -0600890 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700891
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700892 pw.println("Previous vibrations:");
Michael Wright71216972017-01-31 18:33:54 +0000893 synchronized (mLock) {
Filip Gruszczynski3a8eb0f2015-06-25 18:35:00 -0700894 for (VibrationInfo info : mPreviousVibrations) {
895 pw.print(" ");
896 pw.println(info.toString());
897 }
898 }
899 }
Felipe Lemea5281002017-02-10 15:13:48 -0800900
901 @Override
902 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
903 String[] args, ShellCallback callback, ResultReceiver resultReceiver)
904 throws RemoteException {
905 new VibratorShellCommand(this).exec(this, in, out, err, args, callback, resultReceiver);
906 }
907
908 private final class VibratorShellCommand extends ShellCommand {
909
910 private static final long MAX_VIBRATION_MS = 200;
911
912 private final IBinder mToken;
913
914 private VibratorShellCommand(IBinder token) {
915 mToken = token;
916 }
917
918 @Override
919 public int onCommand(String cmd) {
920 if ("vibrate".equals(cmd)) {
921 return runVibrate();
922 }
923 return handleDefaultCommands(cmd);
924 }
925
926 private int runVibrate() {
927 final long duration = Long.parseLong(getNextArgRequired());
928 if (duration > MAX_VIBRATION_MS) {
929 throw new IllegalArgumentException("maximum duration is " + MAX_VIBRATION_MS);
930 }
931 String description = getNextArg();
932 if (description == null) {
933 description = "Shell command";
934 }
Michael Wright71216972017-01-31 18:33:54 +0000935
936 VibrationEffect effect =
937 VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE);
938 vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
Felipe Lemea5281002017-02-10 15:13:48 -0800939 mToken);
940 return 0;
941 }
942
943 @Override
944 public void onHelp() {
945 try (PrintWriter pw = getOutPrintWriter();) {
946 pw.println("Vibrator commands:");
947 pw.println(" help");
948 pw.println(" Prints this help text.");
949 pw.println("");
950 pw.println(" vibrate duration [description]");
951 pw.println(" Vibrates for duration milliseconds.");
952 pw.println("");
953 }
954 }
955 }
956
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800957}