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.os.Handler;
25import android.os.IVibratorService;
26import android.os.PowerManager;
27import android.os.Process;
28import android.os.RemoteException;
29import android.os.IBinder;
30import android.os.Binder;
31import android.os.SystemClock;
32import android.os.WorkSource;
33import android.util.Slog;
34
35import java.util.LinkedList;
36import java.util.ListIterator;
37
38public class VibratorService extends IVibratorService.Stub {
39    private static final String TAG = "VibratorService";
40
41    private final LinkedList<Vibration> mVibrations;
42    private Vibration mCurrentVibration;
43    private final WorkSource mTmpWorkSource = new WorkSource();
44
45    private class Vibration implements IBinder.DeathRecipient {
46        private final IBinder mToken;
47        private final long    mTimeout;
48        private final long    mStartTime;
49        private final long[]  mPattern;
50        private final int     mRepeat;
51        private final int     mUid;
52
53        Vibration(IBinder token, long millis, int uid) {
54            this(token, millis, null, 0, uid);
55        }
56
57        Vibration(IBinder token, long[] pattern, int repeat, int uid) {
58            this(token, 0, pattern, repeat, uid);
59        }
60
61        private Vibration(IBinder token, long millis, long[] pattern,
62                int repeat, int uid) {
63            mToken = token;
64            mTimeout = millis;
65            mStartTime = SystemClock.uptimeMillis();
66            mPattern = pattern;
67            mRepeat = repeat;
68            mUid = uid;
69        }
70
71        public void binderDied() {
72            synchronized (mVibrations) {
73                mVibrations.remove(this);
74                if (this == mCurrentVibration) {
75                    doCancelVibrateLocked();
76                    startNextVibrationLocked();
77                }
78            }
79        }
80
81        public boolean hasLongerTimeout(long millis) {
82            if (mTimeout == 0) {
83                // This is a pattern, return false to play the simple
84                // vibration.
85                return false;
86            }
87            if ((mStartTime + mTimeout)
88                    < (SystemClock.uptimeMillis() + millis)) {
89                // If this vibration will end before the time passed in, let
90                // the new vibration play.
91                return false;
92            }
93            return true;
94        }
95    }
96
97    VibratorService(Context context) {
98        // Reset the hardware to a default state, in case this is a runtime
99        // restart instead of a fresh boot.
100        vibratorOff();
101
102        mContext = context;
103        PowerManager pm = (PowerManager)context.getSystemService(
104                Context.POWER_SERVICE);
105        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
106        mWakeLock.setReferenceCounted(true);
107
108        mVibrations = new LinkedList<Vibration>();
109
110        IntentFilter filter = new IntentFilter();
111        filter.addAction(Intent.ACTION_SCREEN_OFF);
112        context.registerReceiver(mIntentReceiver, filter);
113    }
114
115    public void vibrate(long milliseconds, IBinder token) {
116        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
117                != PackageManager.PERMISSION_GRANTED) {
118            throw new SecurityException("Requires VIBRATE permission");
119        }
120        int uid = Binder.getCallingUid();
121        // We're running in the system server so we cannot crash. Check for a
122        // timeout of 0 or negative. This will ensure that a vibration has
123        // either a timeout of > 0 or a non-null pattern.
124        if (milliseconds <= 0 || (mCurrentVibration != null
125                && mCurrentVibration.hasLongerTimeout(milliseconds))) {
126            // Ignore this vibration since the current vibration will play for
127            // longer than milliseconds.
128            return;
129        }
130        Vibration vib = new Vibration(token, milliseconds, uid);
131        synchronized (mVibrations) {
132            removeVibrationLocked(token);
133            doCancelVibrateLocked();
134            mCurrentVibration = vib;
135            startVibrationLocked(vib);
136        }
137    }
138
139    private boolean isAll0(long[] pattern) {
140        int N = pattern.length;
141        for (int i = 0; i < N; i++) {
142            if (pattern[i] != 0) {
143                return false;
144            }
145        }
146        return true;
147    }
148
149    public void vibratePattern(long[] pattern, int repeat, IBinder token) {
150        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
151                != PackageManager.PERMISSION_GRANTED) {
152            throw new SecurityException("Requires VIBRATE permission");
153        }
154        int uid = Binder.getCallingUid();
155        // so wakelock calls will succeed
156        long identity = Binder.clearCallingIdentity();
157        try {
158            if (false) {
159                String s = "";
160                int N = pattern.length;
161                for (int i=0; i<N; i++) {
162                    s += " " + pattern[i];
163                }
164                Slog.i(TAG, "vibrating with pattern: " + s);
165            }
166
167            // we're running in the server so we can't fail
168            if (pattern == null || pattern.length == 0
169                    || isAll0(pattern)
170                    || repeat >= pattern.length || token == null) {
171                return;
172            }
173
174            Vibration vib = new Vibration(token, pattern, repeat, uid);
175            try {
176                token.linkToDeath(vib, 0);
177            } catch (RemoteException e) {
178                return;
179            }
180
181            synchronized (mVibrations) {
182                removeVibrationLocked(token);
183                doCancelVibrateLocked();
184                if (repeat >= 0) {
185                    mVibrations.addFirst(vib);
186                    startNextVibrationLocked();
187                } else {
188                    // A negative repeat means that this pattern is not meant
189                    // to repeat. Treat it like a simple vibration.
190                    mCurrentVibration = vib;
191                    startVibrationLocked(vib);
192                }
193            }
194        }
195        finally {
196            Binder.restoreCallingIdentity(identity);
197        }
198    }
199
200    public void cancelVibrate(IBinder token) {
201        mContext.enforceCallingOrSelfPermission(
202                android.Manifest.permission.VIBRATE,
203                "cancelVibrate");
204
205        // so wakelock calls will succeed
206        long identity = Binder.clearCallingIdentity();
207        try {
208            synchronized (mVibrations) {
209                final Vibration vib = removeVibrationLocked(token);
210                if (vib == mCurrentVibration) {
211                    doCancelVibrateLocked();
212                    startNextVibrationLocked();
213                }
214            }
215        }
216        finally {
217            Binder.restoreCallingIdentity(identity);
218        }
219    }
220
221    private final Runnable mVibrationRunnable = new Runnable() {
222        public void run() {
223            synchronized (mVibrations) {
224                doCancelVibrateLocked();
225                startNextVibrationLocked();
226            }
227        }
228    };
229
230    // Lock held on mVibrations
231    private void doCancelVibrateLocked() {
232        if (mThread != null) {
233            synchronized (mThread) {
234                mThread.mDone = true;
235                mThread.notify();
236            }
237            mThread = null;
238        }
239        vibratorOff();
240        mH.removeCallbacks(mVibrationRunnable);
241    }
242
243    // Lock held on mVibrations
244    private void startNextVibrationLocked() {
245        if (mVibrations.size() <= 0) {
246            return;
247        }
248        mCurrentVibration = mVibrations.getFirst();
249        startVibrationLocked(mCurrentVibration);
250    }
251
252    // Lock held on mVibrations
253    private void startVibrationLocked(final Vibration vib) {
254        if (vib.mTimeout != 0) {
255            vibratorOn(vib.mTimeout);
256            mH.postDelayed(mVibrationRunnable, vib.mTimeout);
257        } else {
258            // mThread better be null here. doCancelVibrate should always be
259            // called before startNextVibrationLocked or startVibrationLocked.
260            mThread = new VibrateThread(vib);
261            mThread.start();
262        }
263    }
264
265    // Lock held on mVibrations
266    private Vibration removeVibrationLocked(IBinder token) {
267        ListIterator<Vibration> iter = mVibrations.listIterator(0);
268        while (iter.hasNext()) {
269            Vibration vib = iter.next();
270            if (vib.mToken == token) {
271                iter.remove();
272                return vib;
273            }
274        }
275        // We might be looking for a simple vibration which is only stored in
276        // mCurrentVibration.
277        if (mCurrentVibration != null && mCurrentVibration.mToken == token) {
278            return mCurrentVibration;
279        }
280        return null;
281    }
282
283    private class VibrateThread extends Thread {
284        final Vibration mVibration;
285        boolean mDone;
286
287        VibrateThread(Vibration vib) {
288            mVibration = vib;
289            mTmpWorkSource.set(vib.mUid);
290            mWakeLock.setWorkSource(mTmpWorkSource);
291            mWakeLock.acquire();
292        }
293
294        private void delay(long duration) {
295            if (duration > 0) {
296                long bedtime = SystemClock.uptimeMillis();
297                do {
298                    try {
299                        this.wait(duration);
300                    }
301                    catch (InterruptedException e) {
302                    }
303                    if (mDone) {
304                        break;
305                    }
306                    duration = duration
307                            - SystemClock.uptimeMillis() - bedtime;
308                } while (duration > 0);
309            }
310        }
311
312        public void run() {
313            Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
314            synchronized (this) {
315                int index = 0;
316                long[] pattern = mVibration.mPattern;
317                int len = pattern.length;
318                int repeat = mVibration.mRepeat;
319                long duration = 0;
320
321                while (!mDone) {
322                    // add off-time duration to any accumulated on-time duration
323                    if (index < len) {
324                        duration += pattern[index++];
325                    }
326
327                    // sleep until it is time to start the vibrator
328                    delay(duration);
329                    if (mDone) {
330                        break;
331                    }
332
333                    if (index < len) {
334                        // read on-time duration and start the vibrator
335                        // duration is saved for delay() at top of loop
336                        duration = pattern[index++];
337                        if (duration > 0) {
338                            VibratorService.this.vibratorOn(duration);
339                        }
340                    } else {
341                        if (repeat < 0) {
342                            break;
343                        } else {
344                            index = repeat;
345                            duration = 0;
346                        }
347                    }
348                }
349                mWakeLock.release();
350            }
351            synchronized (mVibrations) {
352                if (mThread == this) {
353                    mThread = null;
354                }
355                if (!mDone) {
356                    // If this vibration finished naturally, start the next
357                    // vibration.
358                    mVibrations.remove(mVibration);
359                    startNextVibrationLocked();
360                }
361            }
362        }
363    };
364
365    BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
366        public void onReceive(Context context, Intent intent) {
367            if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
368                synchronized (mVibrations) {
369                    doCancelVibrateLocked();
370                    mVibrations.clear();
371                }
372            }
373        }
374    };
375
376    private Handler mH = new Handler();
377
378    private final Context mContext;
379    private final PowerManager.WakeLock mWakeLock;
380
381    volatile VibrateThread mThread;
382
383    native static void vibratorOn(long milliseconds);
384    native static void vibratorOff();
385}
386