blob: 09828fbef4953cfc6976637e9209e620577bbb82 [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;
Jeff Brown7f6c2312012-04-13 20:38:38 -070025import android.database.ContentObserver;
26import android.hardware.input.InputManager;
Dianne Hackborn91268cf2013-06-13 19:06:50 -070027import android.os.BatteryStats;
Joe Onorato95e4f702009-03-24 19:29:09 -070028import android.os.Handler;
Mike Lockwood3a322132009-11-24 00:30:52 -050029import android.os.IVibratorService;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.os.PowerManager;
31import android.os.Process;
32import android.os.RemoteException;
33import android.os.IBinder;
34import android.os.Binder;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080035import android.os.ServiceManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.os.SystemClock;
Jeff Brownd4935962012-09-25 13:27:20 -070037import android.os.UserHandle;
Jeff Brown7f6c2312012-04-13 20:38:38 -070038import android.os.Vibrator;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070039import android.os.WorkSource;
Jeff Brown7f6c2312012-04-13 20:38:38 -070040import android.provider.Settings;
41import android.provider.Settings.SettingNotFoundException;
Joe Onorato8a9b2202010-02-26 18:56:32 -080042import android.util.Slog;
Jeff Brown7f6c2312012-04-13 20:38:38 -070043import android.view.InputDevice;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044
Dianne Hackborna06de0f2012-12-11 16:34:47 -080045import com.android.internal.app.IAppOpsService;
46import com.android.internal.app.IBatteryStats;
47
Jeff Brown7f6c2312012-04-13 20:38:38 -070048import java.util.ArrayList;
Jeff Brown969579b2014-05-20 19:29:29 -070049import java.util.Iterator;
Patrick Scott18dd5f02009-07-02 11:31:12 -040050import java.util.LinkedList;
51import java.util.ListIterator;
52
Jeff Brown7f6c2312012-04-13 20:38:38 -070053public class VibratorService extends IVibratorService.Stub
54 implements InputManager.InputDeviceListener {
Mike Lockwood3a322132009-11-24 00:30:52 -050055 private static final String TAG = "VibratorService";
Mike Lockwoodcc9a63d2009-11-10 07:50:28 -050056
Patrick Scott18dd5f02009-07-02 11:31:12 -040057 private final LinkedList<Vibration> mVibrations;
58 private Vibration mCurrentVibration;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070059 private final WorkSource mTmpWorkSource = new WorkSource();
Jeff Brown7f6c2312012-04-13 20:38:38 -070060 private final Handler mH = new Handler();
61
62 private final Context mContext;
63 private final PowerManager.WakeLock mWakeLock;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080064 private final IAppOpsService mAppOpsService;
65 private final IBatteryStats mBatteryStatsService;
Jeff Brown7f6c2312012-04-13 20:38:38 -070066 private InputManager mIm;
67
68 volatile VibrateThread mThread;
69
70 // mInputDeviceVibrators lock should be acquired after mVibrations lock, if both are
71 // to be acquired
72 private final ArrayList<Vibrator> mInputDeviceVibrators = new ArrayList<Vibrator>();
73 private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators
74 private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators
75
Dianne Hackborna06de0f2012-12-11 16:34:47 -080076 private int mCurVibUid = -1;
77
Jeff Brown7f6c2312012-04-13 20:38:38 -070078 native static boolean vibratorExists();
79 native static void vibratorOn(long milliseconds);
80 native static void vibratorOff();
Patrick Scott18dd5f02009-07-02 11:31:12 -040081
Patrick Scott18dd5f02009-07-02 11:31:12 -040082 private class Vibration implements IBinder.DeathRecipient {
83 private final IBinder mToken;
84 private final long mTimeout;
85 private final long mStartTime;
86 private final long[] mPattern;
87 private final int mRepeat;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070088 private final int mUid;
Dianne Hackborna06de0f2012-12-11 16:34:47 -080089 private final String mPackageName;
Patrick Scott18dd5f02009-07-02 11:31:12 -040090
Dianne Hackborna06de0f2012-12-11 16:34:47 -080091 Vibration(IBinder token, long millis, int uid, String packageName) {
92 this(token, millis, null, 0, uid, packageName);
Patrick Scott18dd5f02009-07-02 11:31:12 -040093 }
94
Dianne Hackborna06de0f2012-12-11 16:34:47 -080095 Vibration(IBinder token, long[] pattern, int repeat, int uid, String packageName) {
96 this(token, 0, pattern, repeat, uid, packageName);
Patrick Scott18dd5f02009-07-02 11:31:12 -040097 }
98
99 private Vibration(IBinder token, long millis, long[] pattern,
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800100 int repeat, int uid, String packageName) {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400101 mToken = token;
102 mTimeout = millis;
103 mStartTime = SystemClock.uptimeMillis();
104 mPattern = pattern;
105 mRepeat = repeat;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700106 mUid = uid;
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800107 mPackageName = packageName;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400108 }
109
110 public void binderDied() {
111 synchronized (mVibrations) {
112 mVibrations.remove(this);
113 if (this == mCurrentVibration) {
114 doCancelVibrateLocked();
115 startNextVibrationLocked();
116 }
117 }
118 }
119
120 public boolean hasLongerTimeout(long millis) {
121 if (mTimeout == 0) {
122 // This is a pattern, return false to play the simple
123 // vibration.
124 return false;
125 }
126 if ((mStartTime + mTimeout)
127 < (SystemClock.uptimeMillis() + millis)) {
128 // If this vibration will end before the time passed in, let
129 // the new vibration play.
130 return false;
131 }
132 return true;
133 }
Jeff Brown969579b2014-05-20 19:29:29 -0700134
135 public boolean isSystemHapticFeedback() {
136 return (mUid == Process.SYSTEM_UID || mUid == 0) && mRepeat < 0;
137 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400138 }
139
Mike Lockwood3a322132009-11-24 00:30:52 -0500140 VibratorService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 // Reset the hardware to a default state, in case this is a runtime
142 // restart instead of a fresh boot.
143 vibratorOff();
144
145 mContext = context;
146 PowerManager pm = (PowerManager)context.getSystemService(
147 Context.POWER_SERVICE);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700148 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149 mWakeLock.setReferenceCounted(true);
150
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800151 mAppOpsService = IAppOpsService.Stub.asInterface(ServiceManager.getService(Context.APP_OPS_SERVICE));
Dianne Hackborn91268cf2013-06-13 19:06:50 -0700152 mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
153 BatteryStats.SERVICE_NAME));
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800154
Patrick Scott18dd5f02009-07-02 11:31:12 -0400155 mVibrations = new LinkedList<Vibration>();
156
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800157 IntentFilter filter = new IntentFilter();
158 filter.addAction(Intent.ACTION_SCREEN_OFF);
159 context.registerReceiver(mIntentReceiver, filter);
160 }
161
Jeff Brown7f6c2312012-04-13 20:38:38 -0700162 public void systemReady() {
163 mIm = (InputManager)mContext.getSystemService(Context.INPUT_SERVICE);
Jeff Brownd4935962012-09-25 13:27:20 -0700164
Jeff Brown7f6c2312012-04-13 20:38:38 -0700165 mContext.getContentResolver().registerContentObserver(
166 Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES), true,
167 new ContentObserver(mH) {
168 @Override
169 public void onChange(boolean selfChange) {
Jeff Brown82065252012-04-16 13:19:05 -0700170 updateInputDeviceVibrators();
Jeff Brown7f6c2312012-04-13 20:38:38 -0700171 }
Jeff Brownd4935962012-09-25 13:27:20 -0700172 }, UserHandle.USER_ALL);
173
174 mContext.registerReceiver(new BroadcastReceiver() {
175 @Override
176 public void onReceive(Context context, Intent intent) {
177 updateInputDeviceVibrators();
178 }
179 }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mH);
180
Jeff Brown82065252012-04-16 13:19:05 -0700181 updateInputDeviceVibrators();
Dianne Hackbornea9020e2010-11-04 11:39:12 -0700182 }
Jeff Brown7f6c2312012-04-13 20:38:38 -0700183
184 public boolean hasVibrator() {
185 return doVibratorExists();
186 }
187
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800188 private void verifyIncomingUid(int uid) {
189 if (uid == Binder.getCallingUid()) {
190 return;
191 }
192 if (Binder.getCallingPid() == Process.myPid()) {
193 return;
194 }
195 mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
196 Binder.getCallingPid(), Binder.getCallingUid(), null);
197 }
198
199 public void vibrate(int uid, String packageName, long milliseconds, IBinder token) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700200 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
201 != PackageManager.PERMISSION_GRANTED) {
202 throw new SecurityException("Requires VIBRATE permission");
203 }
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800204 verifyIncomingUid(uid);
Patrick Scott24f10762009-08-19 09:03:56 -0400205 // We're running in the system server so we cannot crash. Check for a
206 // timeout of 0 or negative. This will ensure that a vibration has
207 // either a timeout of > 0 or a non-null pattern.
208 if (milliseconds <= 0 || (mCurrentVibration != null
209 && mCurrentVibration.hasLongerTimeout(milliseconds))) {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400210 // Ignore this vibration since the current vibration will play for
211 // longer than milliseconds.
212 return;
213 }
Jeff Brown7f6c2312012-04-13 20:38:38 -0700214
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800215 Vibration vib = new Vibration(token, milliseconds, uid, packageName);
216
217 final long ident = Binder.clearCallingIdentity();
218 try {
219 synchronized (mVibrations) {
220 removeVibrationLocked(token);
221 doCancelVibrateLocked();
222 mCurrentVibration = vib;
223 startVibrationLocked(vib);
224 }
225 } finally {
226 Binder.restoreCallingIdentity(ident);
Patrick Scott18dd5f02009-07-02 11:31:12 -0400227 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228 }
229
230 private boolean isAll0(long[] pattern) {
231 int N = pattern.length;
232 for (int i = 0; i < N; i++) {
233 if (pattern[i] != 0) {
234 return false;
235 }
236 }
237 return true;
238 }
239
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800240 public void vibratePattern(int uid, String packageName, long[] pattern, int repeat,
241 IBinder token) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
243 != PackageManager.PERMISSION_GRANTED) {
244 throw new SecurityException("Requires VIBRATE permission");
245 }
Dianne Hackbornf265ea92013-01-31 15:00:51 -0800246 verifyIncomingUid(uid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800247 // so wakelock calls will succeed
248 long identity = Binder.clearCallingIdentity();
249 try {
250 if (false) {
251 String s = "";
252 int N = pattern.length;
253 for (int i=0; i<N; i++) {
254 s += " " + pattern[i];
255 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800256 Slog.i(TAG, "vibrating with pattern: " + s);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800257 }
258
259 // we're running in the server so we can't fail
260 if (pattern == null || pattern.length == 0
261 || isAll0(pattern)
262 || repeat >= pattern.length || token == null) {
263 return;
264 }
265
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800266 Vibration vib = new Vibration(token, pattern, repeat, uid, packageName);
Patrick Scott18dd5f02009-07-02 11:31:12 -0400267 try {
268 token.linkToDeath(vib, 0);
269 } catch (RemoteException e) {
270 return;
271 }
272
273 synchronized (mVibrations) {
274 removeVibrationLocked(token);
275 doCancelVibrateLocked();
276 if (repeat >= 0) {
277 mVibrations.addFirst(vib);
278 startNextVibrationLocked();
279 } else {
280 // A negative repeat means that this pattern is not meant
281 // to repeat. Treat it like a simple vibration.
282 mCurrentVibration = vib;
283 startVibrationLocked(vib);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800285 }
286 }
287 finally {
288 Binder.restoreCallingIdentity(identity);
289 }
290 }
291
Patrick Scott18dd5f02009-07-02 11:31:12 -0400292 public void cancelVibrate(IBinder token) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 mContext.enforceCallingOrSelfPermission(
294 android.Manifest.permission.VIBRATE,
295 "cancelVibrate");
296
297 // so wakelock calls will succeed
298 long identity = Binder.clearCallingIdentity();
299 try {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400300 synchronized (mVibrations) {
301 final Vibration vib = removeVibrationLocked(token);
302 if (vib == mCurrentVibration) {
303 doCancelVibrateLocked();
304 startNextVibrationLocked();
305 }
306 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307 }
308 finally {
309 Binder.restoreCallingIdentity(identity);
310 }
311 }
Eric Olsenf42f15c2009-10-29 16:42:03 -0700312
Patrick Scott18dd5f02009-07-02 11:31:12 -0400313 private final Runnable mVibrationRunnable = new Runnable() {
314 public void run() {
315 synchronized (mVibrations) {
316 doCancelVibrateLocked();
317 startNextVibrationLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800318 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400319 }
320 };
321
322 // Lock held on mVibrations
323 private void doCancelVibrateLocked() {
324 if (mThread != null) {
325 synchronized (mThread) {
326 mThread.mDone = true;
327 mThread.notify();
328 }
329 mThread = null;
330 }
Jeff Brown7f6c2312012-04-13 20:38:38 -0700331 doVibratorOff();
Patrick Scott18dd5f02009-07-02 11:31:12 -0400332 mH.removeCallbacks(mVibrationRunnable);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800333 reportFinishVibrationLocked();
Patrick Scott18dd5f02009-07-02 11:31:12 -0400334 }
335
336 // Lock held on mVibrations
337 private void startNextVibrationLocked() {
338 if (mVibrations.size() <= 0) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800339 reportFinishVibrationLocked();
Mathias Jeppssonb23949b2010-09-28 14:45:23 +0200340 mCurrentVibration = null;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400341 return;
342 }
343 mCurrentVibration = mVibrations.getFirst();
344 startVibrationLocked(mCurrentVibration);
345 }
346
347 // Lock held on mVibrations
348 private void startVibrationLocked(final Vibration vib) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800349 try {
Dianne Hackborne98f5db2013-07-17 17:23:25 -0700350 int mode = mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
351 AppOpsManager.OP_VIBRATE, vib.mUid, vib.mPackageName);
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800352 if (mode != AppOpsManager.MODE_ALLOWED) {
353 if (mode == AppOpsManager.MODE_ERRORED) {
354 Slog.w(TAG, "Would be an error: vibrate from uid " + vib.mUid);
355 }
356 mH.post(mVibrationRunnable);
357 return;
358 }
359 } catch (RemoteException e) {
360 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400361 if (vib.mTimeout != 0) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800362 doVibratorOn(vib.mTimeout, vib.mUid);
Patrick Scott18dd5f02009-07-02 11:31:12 -0400363 mH.postDelayed(mVibrationRunnable, vib.mTimeout);
364 } else {
365 // mThread better be null here. doCancelVibrate should always be
366 // called before startNextVibrationLocked or startVibrationLocked.
367 mThread = new VibrateThread(vib);
368 mThread.start();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369 }
370 }
371
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800372 private void reportFinishVibrationLocked() {
373 if (mCurrentVibration != null) {
374 try {
Dianne Hackborne98f5db2013-07-17 17:23:25 -0700375 mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
376 AppOpsManager.OP_VIBRATE, mCurrentVibration.mUid,
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800377 mCurrentVibration.mPackageName);
378 } catch (RemoteException e) {
379 }
380 mCurrentVibration = null;
381 }
382 }
383
Patrick Scott18dd5f02009-07-02 11:31:12 -0400384 // Lock held on mVibrations
385 private Vibration removeVibrationLocked(IBinder token) {
386 ListIterator<Vibration> iter = mVibrations.listIterator(0);
387 while (iter.hasNext()) {
388 Vibration vib = iter.next();
389 if (vib.mToken == token) {
390 iter.remove();
Mathias Jeppssonb23949b2010-09-28 14:45:23 +0200391 unlinkVibration(vib);
Patrick Scott18dd5f02009-07-02 11:31:12 -0400392 return vib;
393 }
394 }
395 // We might be looking for a simple vibration which is only stored in
396 // mCurrentVibration.
397 if (mCurrentVibration != null && mCurrentVibration.mToken == token) {
Mathias Jeppssonb23949b2010-09-28 14:45:23 +0200398 unlinkVibration(mCurrentVibration);
Patrick Scott18dd5f02009-07-02 11:31:12 -0400399 return mCurrentVibration;
400 }
401 return null;
402 }
403
Mathias Jeppssonb23949b2010-09-28 14:45:23 +0200404 private void unlinkVibration(Vibration vib) {
405 if (vib.mPattern != null) {
406 // If Vibration object has a pattern,
407 // the Vibration object has also been linkedToDeath.
408 vib.mToken.unlinkToDeath(vib, 0);
409 }
410 }
411
Jeff Brown7f6c2312012-04-13 20:38:38 -0700412 private void updateInputDeviceVibrators() {
413 synchronized (mVibrations) {
414 doCancelVibrateLocked();
415
416 synchronized (mInputDeviceVibrators) {
Jeff Brown82065252012-04-16 13:19:05 -0700417 mVibrateInputDevicesSetting = false;
418 try {
Jeff Brownd4935962012-09-25 13:27:20 -0700419 mVibrateInputDevicesSetting = Settings.System.getIntForUser(
420 mContext.getContentResolver(),
421 Settings.System.VIBRATE_INPUT_DEVICES, UserHandle.USER_CURRENT) > 0;
Jeff Brown82065252012-04-16 13:19:05 -0700422 } catch (SettingNotFoundException snfe) {
423 }
424
425 if (mVibrateInputDevicesSetting) {
426 if (!mInputDeviceListenerRegistered) {
427 mInputDeviceListenerRegistered = true;
428 mIm.registerInputDeviceListener(this, mH);
429 }
430 } else {
431 if (mInputDeviceListenerRegistered) {
432 mInputDeviceListenerRegistered = false;
433 mIm.unregisterInputDeviceListener(this);
434 }
435 }
436
Jeff Brown7f6c2312012-04-13 20:38:38 -0700437 mInputDeviceVibrators.clear();
438 if (mVibrateInputDevicesSetting) {
439 int[] ids = mIm.getInputDeviceIds();
440 for (int i = 0; i < ids.length; i++) {
441 InputDevice device = mIm.getInputDevice(ids[i]);
442 Vibrator vibrator = device.getVibrator();
443 if (vibrator.hasVibrator()) {
444 mInputDeviceVibrators.add(vibrator);
445 }
446 }
447 }
448 }
449
450 startNextVibrationLocked();
451 }
452 }
453
454 @Override
455 public void onInputDeviceAdded(int deviceId) {
456 updateInputDeviceVibrators();
457 }
458
459 @Override
460 public void onInputDeviceChanged(int deviceId) {
461 updateInputDeviceVibrators();
462 }
463
464 @Override
465 public void onInputDeviceRemoved(int deviceId) {
466 updateInputDeviceVibrators();
467 }
468
469 private boolean doVibratorExists() {
Jeff Brown1064a502012-05-02 16:51:37 -0700470 // For now, we choose to ignore the presence of input devices that have vibrators
471 // when reporting whether the device has a vibrator. Applications often use this
472 // information to decide whether to enable certain features so they expect the
473 // result of hasVibrator() to be constant. For now, just report whether
474 // the device has a built-in vibrator.
475 //synchronized (mInputDeviceVibrators) {
476 // return !mInputDeviceVibrators.isEmpty() || vibratorExists();
477 //}
Dianne Hackbornc2293022013-02-06 23:14:49 -0800478 return vibratorExists();
Jeff Brown7f6c2312012-04-13 20:38:38 -0700479 }
480
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800481 private void doVibratorOn(long millis, int uid) {
Jeff Brown7f6c2312012-04-13 20:38:38 -0700482 synchronized (mInputDeviceVibrators) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800483 try {
484 mBatteryStatsService.noteVibratorOn(uid, millis);
485 mCurVibUid = uid;
486 } catch (RemoteException e) {
487 }
Jeff Brown7f6c2312012-04-13 20:38:38 -0700488 final int vibratorCount = mInputDeviceVibrators.size();
489 if (vibratorCount != 0) {
490 for (int i = 0; i < vibratorCount; i++) {
491 mInputDeviceVibrators.get(i).vibrate(millis);
492 }
493 } else {
494 vibratorOn(millis);
495 }
496 }
497 }
498
499 private void doVibratorOff() {
500 synchronized (mInputDeviceVibrators) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800501 if (mCurVibUid >= 0) {
502 try {
503 mBatteryStatsService.noteVibratorOff(mCurVibUid);
504 } catch (RemoteException e) {
505 }
506 mCurVibUid = -1;
507 }
Jeff Brown7f6c2312012-04-13 20:38:38 -0700508 final int vibratorCount = mInputDeviceVibrators.size();
509 if (vibratorCount != 0) {
510 for (int i = 0; i < vibratorCount; i++) {
511 mInputDeviceVibrators.get(i).cancel();
512 }
513 } else {
514 vibratorOff();
515 }
516 }
517 }
518
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800519 private class VibrateThread extends Thread {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400520 final Vibration mVibration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800521 boolean mDone;
Eric Olsenf42f15c2009-10-29 16:42:03 -0700522
Patrick Scott18dd5f02009-07-02 11:31:12 -0400523 VibrateThread(Vibration vib) {
524 mVibration = vib;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700525 mTmpWorkSource.set(vib.mUid);
526 mWakeLock.setWorkSource(mTmpWorkSource);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800527 mWakeLock.acquire();
528 }
529
530 private void delay(long duration) {
531 if (duration > 0) {
Vairavan Srinivasane4c56d92011-03-31 13:32:54 -0700532 long bedtime = duration + SystemClock.uptimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800533 do {
534 try {
535 this.wait(duration);
536 }
537 catch (InterruptedException e) {
538 }
539 if (mDone) {
540 break;
541 }
Vairavan Srinivasane4c56d92011-03-31 13:32:54 -0700542 duration = bedtime - SystemClock.uptimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800543 } while (duration > 0);
544 }
545 }
546
547 public void run() {
548 Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
549 synchronized (this) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800550 final long[] pattern = mVibration.mPattern;
551 final int len = pattern.length;
552 final int repeat = mVibration.mRepeat;
553 final int uid = mVibration.mUid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800554 int index = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800555 long duration = 0;
556
557 while (!mDone) {
Eric Olsenf42f15c2009-10-29 16:42:03 -0700558 // add off-time duration to any accumulated on-time duration
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800559 if (index < len) {
560 duration += pattern[index++];
561 }
562
563 // sleep until it is time to start the vibrator
564 delay(duration);
565 if (mDone) {
566 break;
567 }
568
569 if (index < len) {
570 // read on-time duration and start the vibrator
571 // duration is saved for delay() at top of loop
572 duration = pattern[index++];
573 if (duration > 0) {
Dianne Hackborna06de0f2012-12-11 16:34:47 -0800574 VibratorService.this.doVibratorOn(duration, uid);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800575 }
576 } else {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400577 if (repeat < 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800578 break;
579 } else {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400580 index = repeat;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581 duration = 0;
582 }
583 }
584 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800585 mWakeLock.release();
586 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400587 synchronized (mVibrations) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588 if (mThread == this) {
589 mThread = null;
590 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400591 if (!mDone) {
592 // If this vibration finished naturally, start the next
593 // vibration.
594 mVibrations.remove(mVibration);
Mathias Jeppssonb23949b2010-09-28 14:45:23 +0200595 unlinkVibration(mVibration);
Patrick Scott18dd5f02009-07-02 11:31:12 -0400596 startNextVibrationLocked();
597 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800598 }
599 }
Jeff Brown969579b2014-05-20 19:29:29 -0700600 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602 BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
Jeff Brown969579b2014-05-20 19:29:29 -0700603 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800604 public void onReceive(Context context, Intent intent) {
605 if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400606 synchronized (mVibrations) {
Jeff Brown969579b2014-05-20 19:29:29 -0700607 // When the system is entering a non-interactive state, we want
608 // to cancel vibrations in case a misbehaving app has abandoned
609 // them. However it may happen that the system is currently playing
610 // haptic feedback as part of the transition. So we don't cancel
611 // system vibrations.
612 if (mCurrentVibration != null
613 && !mCurrentVibration.isSystemHapticFeedback()) {
614 doCancelVibrateLocked();
Vairavan Srinivasan8a61f492011-05-13 10:47:20 -0700615 }
616
Jeff Brown969579b2014-05-20 19:29:29 -0700617 // Clear all remaining vibrations.
618 Iterator<Vibration> it = mVibrations.iterator();
619 while (it.hasNext()) {
620 Vibration vibration = it.next();
621 if (vibration != mCurrentVibration) {
622 unlinkVibration(vibration);
623 it.remove();
624 }
625 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400626 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800627 }
628 }
629 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800630}