• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.server;
18 
19 import android.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.content.pm.PackageManager;
24 import android.database.ContentObserver;
25 import android.hardware.input.InputManager;
26 import android.os.Handler;
27 import android.os.IVibratorService;
28 import android.os.PowerManager;
29 import android.os.Process;
30 import android.os.RemoteException;
31 import android.os.IBinder;
32 import android.os.Binder;
33 import android.os.SystemClock;
34 import android.os.Vibrator;
35 import android.os.WorkSource;
36 import android.provider.Settings;
37 import android.provider.Settings.SettingNotFoundException;
38 import android.util.Slog;
39 import android.view.InputDevice;
40 
41 import java.util.ArrayList;
42 import java.util.LinkedList;
43 import java.util.ListIterator;
44 
45 public class VibratorService extends IVibratorService.Stub
46         implements InputManager.InputDeviceListener {
47     private static final String TAG = "VibratorService";
48 
49     private final LinkedList<Vibration> mVibrations;
50     private Vibration mCurrentVibration;
51     private final WorkSource mTmpWorkSource = new WorkSource();
52     private final Handler mH = new Handler();
53 
54     private final Context mContext;
55     private final PowerManager.WakeLock mWakeLock;
56     private InputManager mIm;
57 
58     volatile VibrateThread mThread;
59 
60     // mInputDeviceVibrators lock should be acquired after mVibrations lock, if both are
61     // to be acquired
62     private final ArrayList<Vibrator> mInputDeviceVibrators = new ArrayList<Vibrator>();
63     private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators
64     private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators
65 
vibratorExists()66     native static boolean vibratorExists();
vibratorOn(long milliseconds)67     native static void vibratorOn(long milliseconds);
vibratorOff()68     native static void vibratorOff();
69 
70     private class Vibration implements IBinder.DeathRecipient {
71         private final IBinder mToken;
72         private final long    mTimeout;
73         private final long    mStartTime;
74         private final long[]  mPattern;
75         private final int     mRepeat;
76         private final int     mUid;
77 
Vibration(IBinder token, long millis, int uid)78         Vibration(IBinder token, long millis, int uid) {
79             this(token, millis, null, 0, uid);
80         }
81 
Vibration(IBinder token, long[] pattern, int repeat, int uid)82         Vibration(IBinder token, long[] pattern, int repeat, int uid) {
83             this(token, 0, pattern, repeat, uid);
84         }
85 
Vibration(IBinder token, long millis, long[] pattern, int repeat, int uid)86         private Vibration(IBinder token, long millis, long[] pattern,
87                 int repeat, int uid) {
88             mToken = token;
89             mTimeout = millis;
90             mStartTime = SystemClock.uptimeMillis();
91             mPattern = pattern;
92             mRepeat = repeat;
93             mUid = uid;
94         }
95 
binderDied()96         public void binderDied() {
97             synchronized (mVibrations) {
98                 mVibrations.remove(this);
99                 if (this == mCurrentVibration) {
100                     doCancelVibrateLocked();
101                     startNextVibrationLocked();
102                 }
103             }
104         }
105 
hasLongerTimeout(long millis)106         public boolean hasLongerTimeout(long millis) {
107             if (mTimeout == 0) {
108                 // This is a pattern, return false to play the simple
109                 // vibration.
110                 return false;
111             }
112             if ((mStartTime + mTimeout)
113                     < (SystemClock.uptimeMillis() + millis)) {
114                 // If this vibration will end before the time passed in, let
115                 // the new vibration play.
116                 return false;
117             }
118             return true;
119         }
120     }
121 
VibratorService(Context context)122     VibratorService(Context context) {
123         // Reset the hardware to a default state, in case this is a runtime
124         // restart instead of a fresh boot.
125         vibratorOff();
126 
127         mContext = context;
128         PowerManager pm = (PowerManager)context.getSystemService(
129                 Context.POWER_SERVICE);
130         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
131         mWakeLock.setReferenceCounted(true);
132 
133         mVibrations = new LinkedList<Vibration>();
134 
135         IntentFilter filter = new IntentFilter();
136         filter.addAction(Intent.ACTION_SCREEN_OFF);
137         context.registerReceiver(mIntentReceiver, filter);
138     }
139 
systemReady()140     public void systemReady() {
141         mIm = (InputManager)mContext.getSystemService(Context.INPUT_SERVICE);
142         mContext.getContentResolver().registerContentObserver(
143                 Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES), true,
144                 new ContentObserver(mH) {
145                     @Override
146                     public void onChange(boolean selfChange) {
147                         updateInputDeviceVibrators();
148                     }
149                 });
150         updateInputDeviceVibrators();
151     }
152 
hasVibrator()153     public boolean hasVibrator() {
154         return doVibratorExists();
155     }
156 
vibrate(long milliseconds, IBinder token)157     public void vibrate(long milliseconds, IBinder token) {
158         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
159                 != PackageManager.PERMISSION_GRANTED) {
160             throw new SecurityException("Requires VIBRATE permission");
161         }
162         int uid = Binder.getCallingUid();
163         // We're running in the system server so we cannot crash. Check for a
164         // timeout of 0 or negative. This will ensure that a vibration has
165         // either a timeout of > 0 or a non-null pattern.
166         if (milliseconds <= 0 || (mCurrentVibration != null
167                 && mCurrentVibration.hasLongerTimeout(milliseconds))) {
168             // Ignore this vibration since the current vibration will play for
169             // longer than milliseconds.
170             return;
171         }
172 
173         Vibration vib = new Vibration(token, milliseconds, uid);
174         synchronized (mVibrations) {
175             removeVibrationLocked(token);
176             doCancelVibrateLocked();
177             mCurrentVibration = vib;
178             startVibrationLocked(vib);
179         }
180     }
181 
isAll0(long[] pattern)182     private boolean isAll0(long[] pattern) {
183         int N = pattern.length;
184         for (int i = 0; i < N; i++) {
185             if (pattern[i] != 0) {
186                 return false;
187             }
188         }
189         return true;
190     }
191 
vibratePattern(long[] pattern, int repeat, IBinder token)192     public void vibratePattern(long[] pattern, int repeat, IBinder token) {
193         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
194                 != PackageManager.PERMISSION_GRANTED) {
195             throw new SecurityException("Requires VIBRATE permission");
196         }
197         int uid = Binder.getCallingUid();
198         // so wakelock calls will succeed
199         long identity = Binder.clearCallingIdentity();
200         try {
201             if (false) {
202                 String s = "";
203                 int N = pattern.length;
204                 for (int i=0; i<N; i++) {
205                     s += " " + pattern[i];
206                 }
207                 Slog.i(TAG, "vibrating with pattern: " + s);
208             }
209 
210             // we're running in the server so we can't fail
211             if (pattern == null || pattern.length == 0
212                     || isAll0(pattern)
213                     || repeat >= pattern.length || token == null) {
214                 return;
215             }
216 
217             Vibration vib = new Vibration(token, pattern, repeat, uid);
218             try {
219                 token.linkToDeath(vib, 0);
220             } catch (RemoteException e) {
221                 return;
222             }
223 
224             synchronized (mVibrations) {
225                 removeVibrationLocked(token);
226                 doCancelVibrateLocked();
227                 if (repeat >= 0) {
228                     mVibrations.addFirst(vib);
229                     startNextVibrationLocked();
230                 } else {
231                     // A negative repeat means that this pattern is not meant
232                     // to repeat. Treat it like a simple vibration.
233                     mCurrentVibration = vib;
234                     startVibrationLocked(vib);
235                 }
236             }
237         }
238         finally {
239             Binder.restoreCallingIdentity(identity);
240         }
241     }
242 
cancelVibrate(IBinder token)243     public void cancelVibrate(IBinder token) {
244         mContext.enforceCallingOrSelfPermission(
245                 android.Manifest.permission.VIBRATE,
246                 "cancelVibrate");
247 
248         // so wakelock calls will succeed
249         long identity = Binder.clearCallingIdentity();
250         try {
251             synchronized (mVibrations) {
252                 final Vibration vib = removeVibrationLocked(token);
253                 if (vib == mCurrentVibration) {
254                     doCancelVibrateLocked();
255                     startNextVibrationLocked();
256                 }
257             }
258         }
259         finally {
260             Binder.restoreCallingIdentity(identity);
261         }
262     }
263 
264     private final Runnable mVibrationRunnable = new Runnable() {
265         public void run() {
266             synchronized (mVibrations) {
267                 doCancelVibrateLocked();
268                 startNextVibrationLocked();
269             }
270         }
271     };
272 
273     // Lock held on mVibrations
doCancelVibrateLocked()274     private void doCancelVibrateLocked() {
275         if (mThread != null) {
276             synchronized (mThread) {
277                 mThread.mDone = true;
278                 mThread.notify();
279             }
280             mThread = null;
281         }
282         doVibratorOff();
283         mH.removeCallbacks(mVibrationRunnable);
284     }
285 
286     // Lock held on mVibrations
startNextVibrationLocked()287     private void startNextVibrationLocked() {
288         if (mVibrations.size() <= 0) {
289             mCurrentVibration = null;
290             return;
291         }
292         mCurrentVibration = mVibrations.getFirst();
293         startVibrationLocked(mCurrentVibration);
294     }
295 
296     // Lock held on mVibrations
startVibrationLocked(final Vibration vib)297     private void startVibrationLocked(final Vibration vib) {
298         if (vib.mTimeout != 0) {
299             doVibratorOn(vib.mTimeout);
300             mH.postDelayed(mVibrationRunnable, vib.mTimeout);
301         } else {
302             // mThread better be null here. doCancelVibrate should always be
303             // called before startNextVibrationLocked or startVibrationLocked.
304             mThread = new VibrateThread(vib);
305             mThread.start();
306         }
307     }
308 
309     // Lock held on mVibrations
removeVibrationLocked(IBinder token)310     private Vibration removeVibrationLocked(IBinder token) {
311         ListIterator<Vibration> iter = mVibrations.listIterator(0);
312         while (iter.hasNext()) {
313             Vibration vib = iter.next();
314             if (vib.mToken == token) {
315                 iter.remove();
316                 unlinkVibration(vib);
317                 return vib;
318             }
319         }
320         // We might be looking for a simple vibration which is only stored in
321         // mCurrentVibration.
322         if (mCurrentVibration != null && mCurrentVibration.mToken == token) {
323             unlinkVibration(mCurrentVibration);
324             return mCurrentVibration;
325         }
326         return null;
327     }
328 
unlinkVibration(Vibration vib)329     private void unlinkVibration(Vibration vib) {
330         if (vib.mPattern != null) {
331             // If Vibration object has a pattern,
332             // the Vibration object has also been linkedToDeath.
333             vib.mToken.unlinkToDeath(vib, 0);
334         }
335     }
336 
updateInputDeviceVibrators()337     private void updateInputDeviceVibrators() {
338         synchronized (mVibrations) {
339             doCancelVibrateLocked();
340 
341             synchronized (mInputDeviceVibrators) {
342                 mVibrateInputDevicesSetting = false;
343                 try {
344                     mVibrateInputDevicesSetting = Settings.System.getInt(mContext.getContentResolver(),
345                             Settings.System.VIBRATE_INPUT_DEVICES) > 0;
346                 } catch (SettingNotFoundException snfe) {
347                 }
348 
349                 if (mVibrateInputDevicesSetting) {
350                     if (!mInputDeviceListenerRegistered) {
351                         mInputDeviceListenerRegistered = true;
352                         mIm.registerInputDeviceListener(this, mH);
353                     }
354                 } else {
355                     if (mInputDeviceListenerRegistered) {
356                         mInputDeviceListenerRegistered = false;
357                         mIm.unregisterInputDeviceListener(this);
358                     }
359                 }
360 
361                 mInputDeviceVibrators.clear();
362                 if (mVibrateInputDevicesSetting) {
363                     int[] ids = mIm.getInputDeviceIds();
364                     for (int i = 0; i < ids.length; i++) {
365                         InputDevice device = mIm.getInputDevice(ids[i]);
366                         Vibrator vibrator = device.getVibrator();
367                         if (vibrator.hasVibrator()) {
368                             mInputDeviceVibrators.add(vibrator);
369                         }
370                     }
371                 }
372             }
373 
374             startNextVibrationLocked();
375         }
376     }
377 
378     @Override
onInputDeviceAdded(int deviceId)379     public void onInputDeviceAdded(int deviceId) {
380         updateInputDeviceVibrators();
381     }
382 
383     @Override
onInputDeviceChanged(int deviceId)384     public void onInputDeviceChanged(int deviceId) {
385         updateInputDeviceVibrators();
386     }
387 
388     @Override
onInputDeviceRemoved(int deviceId)389     public void onInputDeviceRemoved(int deviceId) {
390         updateInputDeviceVibrators();
391     }
392 
doVibratorExists()393     private boolean doVibratorExists() {
394         // For now, we choose to ignore the presence of input devices that have vibrators
395         // when reporting whether the device has a vibrator.  Applications often use this
396         // information to decide whether to enable certain features so they expect the
397         // result of hasVibrator() to be constant.  For now, just report whether
398         // the device has a built-in vibrator.
399         //synchronized (mInputDeviceVibrators) {
400         //    return !mInputDeviceVibrators.isEmpty() || vibratorExists();
401         //}
402         return vibratorExists();
403     }
404 
doVibratorOn(long millis)405     private void doVibratorOn(long millis) {
406         synchronized (mInputDeviceVibrators) {
407             final int vibratorCount = mInputDeviceVibrators.size();
408             if (vibratorCount != 0) {
409                 for (int i = 0; i < vibratorCount; i++) {
410                     mInputDeviceVibrators.get(i).vibrate(millis);
411                 }
412             } else {
413                 vibratorOn(millis);
414             }
415         }
416     }
417 
doVibratorOff()418     private void doVibratorOff() {
419         synchronized (mInputDeviceVibrators) {
420             final int vibratorCount = mInputDeviceVibrators.size();
421             if (vibratorCount != 0) {
422                 for (int i = 0; i < vibratorCount; i++) {
423                     mInputDeviceVibrators.get(i).cancel();
424                 }
425             } else {
426                 vibratorOff();
427             }
428         }
429     }
430 
431     private class VibrateThread extends Thread {
432         final Vibration mVibration;
433         boolean mDone;
434 
VibrateThread(Vibration vib)435         VibrateThread(Vibration vib) {
436             mVibration = vib;
437             mTmpWorkSource.set(vib.mUid);
438             mWakeLock.setWorkSource(mTmpWorkSource);
439             mWakeLock.acquire();
440         }
441 
delay(long duration)442         private void delay(long duration) {
443             if (duration > 0) {
444                 long bedtime = SystemClock.uptimeMillis();
445                 do {
446                     try {
447                         this.wait(duration);
448                     }
449                     catch (InterruptedException e) {
450                     }
451                     if (mDone) {
452                         break;
453                     }
454                     duration = duration
455                             - SystemClock.uptimeMillis() - bedtime;
456                 } while (duration > 0);
457             }
458         }
459 
run()460         public void run() {
461             Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
462             synchronized (this) {
463                 int index = 0;
464                 long[] pattern = mVibration.mPattern;
465                 int len = pattern.length;
466                 int repeat = mVibration.mRepeat;
467                 long duration = 0;
468 
469                 while (!mDone) {
470                     // add off-time duration to any accumulated on-time duration
471                     if (index < len) {
472                         duration += pattern[index++];
473                     }
474 
475                     // sleep until it is time to start the vibrator
476                     delay(duration);
477                     if (mDone) {
478                         break;
479                     }
480 
481                     if (index < len) {
482                         // read on-time duration and start the vibrator
483                         // duration is saved for delay() at top of loop
484                         duration = pattern[index++];
485                         if (duration > 0) {
486                             VibratorService.this.doVibratorOn(duration);
487                         }
488                     } else {
489                         if (repeat < 0) {
490                             break;
491                         } else {
492                             index = repeat;
493                             duration = 0;
494                         }
495                     }
496                 }
497                 mWakeLock.release();
498             }
499             synchronized (mVibrations) {
500                 if (mThread == this) {
501                     mThread = null;
502                 }
503                 if (!mDone) {
504                     // If this vibration finished naturally, start the next
505                     // vibration.
506                     mVibrations.remove(mVibration);
507                     unlinkVibration(mVibration);
508                     startNextVibrationLocked();
509                 }
510             }
511         }
512     };
513 
514     BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
515         public void onReceive(Context context, Intent intent) {
516             if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
517                 synchronized (mVibrations) {
518                     doCancelVibrateLocked();
519 
520                     int size = mVibrations.size();
521                     for(int i = 0; i < size; i++) {
522                         unlinkVibration(mVibrations.get(i));
523                     }
524 
525                     mVibrations.clear();
526                 }
527             }
528         }
529     };
530 }
531