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