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.app.AppOpsManager;
20import android.content.BroadcastReceiver;
21import android.content.Context;
22import android.content.Intent;
23import android.content.IntentFilter;
24import android.content.pm.PackageManager;
25import android.database.ContentObserver;
26import android.hardware.input.InputManager;
27import android.os.BatteryStats;
28import android.os.Handler;
29import android.os.IVibratorService;
30import android.os.PowerManager;
31import android.os.PowerManagerInternal;
32import android.os.Process;
33import android.os.RemoteException;
34import android.os.IBinder;
35import android.os.Binder;
36import android.os.ServiceManager;
37import android.os.SystemClock;
38import android.os.UserHandle;
39import android.os.Vibrator;
40import android.os.WorkSource;
41import android.provider.Settings;
42import android.provider.Settings.SettingNotFoundException;
43import android.util.Slog;
44import android.view.InputDevice;
45import android.media.AudioAttributes;
46
47import com.android.internal.app.IAppOpsService;
48import com.android.internal.app.IBatteryStats;
49
50import java.util.ArrayList;
51import java.util.Iterator;
52import java.util.LinkedList;
53import java.util.ListIterator;
54
55public class VibratorService extends IVibratorService.Stub
56        implements InputManager.InputDeviceListener {
57    private static final String TAG = "VibratorService";
58    private static final boolean DEBUG = false;
59
60    private final LinkedList<Vibration> mVibrations;
61    private Vibration mCurrentVibration;
62    private final WorkSource mTmpWorkSource = new WorkSource();
63    private final Handler mH = new Handler();
64
65    private final Context mContext;
66    private final PowerManager.WakeLock mWakeLock;
67    private final IAppOpsService mAppOpsService;
68    private final IBatteryStats mBatteryStatsService;
69    private PowerManagerInternal mPowerManagerInternal;
70    private InputManager mIm;
71
72    volatile VibrateThread mThread;
73
74    // mInputDeviceVibrators lock should be acquired after mVibrations lock, if both are
75    // to be acquired
76    private final ArrayList<Vibrator> mInputDeviceVibrators = new ArrayList<Vibrator>();
77    private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators
78    private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators
79
80    private int mCurVibUid = -1;
81    private boolean mLowPowerMode;
82    private SettingsObserver mSettingObserver;
83
84    native static boolean vibratorExists();
85    native static void vibratorOn(long milliseconds);
86    native static void vibratorOff();
87
88    private class Vibration implements IBinder.DeathRecipient {
89        private final IBinder mToken;
90        private final long    mTimeout;
91        private final long    mStartTime;
92        private final long[]  mPattern;
93        private final int     mRepeat;
94        private final int     mUsageHint;
95        private final int     mUid;
96        private final String  mOpPkg;
97
98        Vibration(IBinder token, long millis, int usageHint, int uid, String opPkg) {
99            this(token, millis, null, 0, usageHint, uid, opPkg);
100        }
101
102        Vibration(IBinder token, long[] pattern, int repeat, int usageHint, int uid,
103                String opPkg) {
104            this(token, 0, pattern, repeat, usageHint, uid, opPkg);
105        }
106
107        private Vibration(IBinder token, long millis, long[] pattern,
108                int repeat, int usageHint, int uid, String opPkg) {
109            mToken = token;
110            mTimeout = millis;
111            mStartTime = SystemClock.uptimeMillis();
112            mPattern = pattern;
113            mRepeat = repeat;
114            mUsageHint = usageHint;
115            mUid = uid;
116            mOpPkg = opPkg;
117        }
118
119        public void binderDied() {
120            synchronized (mVibrations) {
121                mVibrations.remove(this);
122                if (this == mCurrentVibration) {
123                    doCancelVibrateLocked();
124                    startNextVibrationLocked();
125                }
126            }
127        }
128
129        public boolean hasLongerTimeout(long millis) {
130            if (mTimeout == 0) {
131                // This is a pattern, return false to play the simple
132                // vibration.
133                return false;
134            }
135            if ((mStartTime + mTimeout)
136                    < (SystemClock.uptimeMillis() + millis)) {
137                // If this vibration will end before the time passed in, let
138                // the new vibration play.
139                return false;
140            }
141            return true;
142        }
143
144        public boolean isSystemHapticFeedback() {
145            return (mUid == Process.SYSTEM_UID || mUid == 0) && mRepeat < 0;
146        }
147    }
148
149    VibratorService(Context context) {
150        // Reset the hardware to a default state, in case this is a runtime
151        // restart instead of a fresh boot.
152        vibratorOff();
153
154        mContext = context;
155        PowerManager pm = (PowerManager)context.getSystemService(
156                Context.POWER_SERVICE);
157        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
158        mWakeLock.setReferenceCounted(true);
159
160        mAppOpsService = IAppOpsService.Stub.asInterface(ServiceManager.getService(Context.APP_OPS_SERVICE));
161        mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
162                BatteryStats.SERVICE_NAME));
163
164        mVibrations = new LinkedList<Vibration>();
165
166        IntentFilter filter = new IntentFilter();
167        filter.addAction(Intent.ACTION_SCREEN_OFF);
168        context.registerReceiver(mIntentReceiver, filter);
169    }
170
171    public void systemReady() {
172        mIm = (InputManager)mContext.getSystemService(Context.INPUT_SERVICE);
173        mSettingObserver = new SettingsObserver(mH);
174
175        mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
176        mPowerManagerInternal.registerLowPowerModeObserver(
177                new PowerManagerInternal.LowPowerModeListener() {
178            @Override
179            public void onLowPowerModeChanged(boolean enabled) {
180                updateInputDeviceVibrators();
181            }
182        });
183
184        mContext.getContentResolver().registerContentObserver(
185                Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES),
186                true, mSettingObserver, UserHandle.USER_ALL);
187
188        mContext.registerReceiver(new BroadcastReceiver() {
189            @Override
190            public void onReceive(Context context, Intent intent) {
191                updateInputDeviceVibrators();
192            }
193        }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mH);
194
195        updateInputDeviceVibrators();
196    }
197
198    private final class SettingsObserver extends ContentObserver {
199        public SettingsObserver(Handler handler) {
200            super(handler);
201        }
202
203        @Override
204        public void onChange(boolean SelfChange) {
205            updateInputDeviceVibrators();
206        }
207    }
208
209    @Override // Binder call
210    public boolean hasVibrator() {
211        return doVibratorExists();
212    }
213
214    private void verifyIncomingUid(int uid) {
215        if (uid == Binder.getCallingUid()) {
216            return;
217        }
218        if (Binder.getCallingPid() == Process.myPid()) {
219            return;
220        }
221        mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
222                Binder.getCallingPid(), Binder.getCallingUid(), null);
223    }
224
225    @Override // Binder call
226    public void vibrate(int uid, String opPkg, long milliseconds, int usageHint,
227            IBinder token) {
228        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
229                != PackageManager.PERMISSION_GRANTED) {
230            throw new SecurityException("Requires VIBRATE permission");
231        }
232        verifyIncomingUid(uid);
233        // We're running in the system server so we cannot crash. Check for a
234        // timeout of 0 or negative. This will ensure that a vibration has
235        // either a timeout of > 0 or a non-null pattern.
236        if (milliseconds <= 0 || (mCurrentVibration != null
237                && mCurrentVibration.hasLongerTimeout(milliseconds))) {
238            // Ignore this vibration since the current vibration will play for
239            // longer than milliseconds.
240            return;
241        }
242
243        if (DEBUG) {
244            Slog.d(TAG, "Vibrating for " + milliseconds + " ms.");
245        }
246
247        Vibration vib = new Vibration(token, milliseconds, usageHint, uid, opPkg);
248
249        final long ident = Binder.clearCallingIdentity();
250        try {
251            synchronized (mVibrations) {
252                removeVibrationLocked(token);
253                doCancelVibrateLocked();
254                mCurrentVibration = vib;
255                startVibrationLocked(vib);
256            }
257        } finally {
258            Binder.restoreCallingIdentity(ident);
259        }
260    }
261
262    private boolean isAll0(long[] pattern) {
263        int N = pattern.length;
264        for (int i = 0; i < N; i++) {
265            if (pattern[i] != 0) {
266                return false;
267            }
268        }
269        return true;
270    }
271
272    @Override // Binder call
273    public void vibratePattern(int uid, String packageName, long[] pattern, int repeat,
274            int usageHint, IBinder token) {
275        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
276                != PackageManager.PERMISSION_GRANTED) {
277            throw new SecurityException("Requires VIBRATE permission");
278        }
279        verifyIncomingUid(uid);
280        // so wakelock calls will succeed
281        long identity = Binder.clearCallingIdentity();
282        try {
283            if (DEBUG) {
284                String s = "";
285                int N = pattern.length;
286                for (int i=0; i<N; i++) {
287                    s += " " + pattern[i];
288                }
289                Slog.d(TAG, "Vibrating with pattern:" + s);
290            }
291
292            // we're running in the server so we can't fail
293            if (pattern == null || pattern.length == 0
294                    || isAll0(pattern)
295                    || repeat >= pattern.length || token == null) {
296                return;
297            }
298
299            Vibration vib = new Vibration(token, pattern, repeat, usageHint, uid, packageName);
300            try {
301                token.linkToDeath(vib, 0);
302            } catch (RemoteException e) {
303                return;
304            }
305
306            synchronized (mVibrations) {
307                removeVibrationLocked(token);
308                doCancelVibrateLocked();
309                if (repeat >= 0) {
310                    mVibrations.addFirst(vib);
311                    startNextVibrationLocked();
312                } else {
313                    // A negative repeat means that this pattern is not meant
314                    // to repeat. Treat it like a simple vibration.
315                    mCurrentVibration = vib;
316                    startVibrationLocked(vib);
317                }
318            }
319        }
320        finally {
321            Binder.restoreCallingIdentity(identity);
322        }
323    }
324
325    @Override // Binder call
326    public void cancelVibrate(IBinder token) {
327        mContext.enforceCallingOrSelfPermission(
328                android.Manifest.permission.VIBRATE,
329                "cancelVibrate");
330
331        // so wakelock calls will succeed
332        long identity = Binder.clearCallingIdentity();
333        try {
334            synchronized (mVibrations) {
335                final Vibration vib = removeVibrationLocked(token);
336                if (vib == mCurrentVibration) {
337                    if (DEBUG) {
338                        Slog.d(TAG, "Canceling vibration.");
339                    }
340                    doCancelVibrateLocked();
341                    startNextVibrationLocked();
342                }
343            }
344        }
345        finally {
346            Binder.restoreCallingIdentity(identity);
347        }
348    }
349
350    private final Runnable mVibrationRunnable = new Runnable() {
351        @Override
352        public void run() {
353            synchronized (mVibrations) {
354                doCancelVibrateLocked();
355                startNextVibrationLocked();
356            }
357        }
358    };
359
360    // Lock held on mVibrations
361    private void doCancelVibrateLocked() {
362        if (mThread != null) {
363            synchronized (mThread) {
364                mThread.mDone = true;
365                mThread.notify();
366            }
367            mThread = null;
368        }
369        doVibratorOff();
370        mH.removeCallbacks(mVibrationRunnable);
371        reportFinishVibrationLocked();
372    }
373
374    // Lock held on mVibrations
375    private void startNextVibrationLocked() {
376        if (mVibrations.size() <= 0) {
377            reportFinishVibrationLocked();
378            mCurrentVibration = null;
379            return;
380        }
381        mCurrentVibration = mVibrations.getFirst();
382        startVibrationLocked(mCurrentVibration);
383    }
384
385    // Lock held on mVibrations
386    private void startVibrationLocked(final Vibration vib) {
387        try {
388            if (mLowPowerMode
389                    && vib.mUsageHint != AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
390                return;
391            }
392
393            int mode = mAppOpsService.checkAudioOperation(AppOpsManager.OP_VIBRATE,
394                    vib.mUsageHint, vib.mUid, vib.mOpPkg);
395            if (mode == AppOpsManager.MODE_ALLOWED) {
396                mode = mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
397                    AppOpsManager.OP_VIBRATE, vib.mUid, vib.mOpPkg);
398            }
399            if (mode != AppOpsManager.MODE_ALLOWED) {
400                if (mode == AppOpsManager.MODE_ERRORED) {
401                    Slog.w(TAG, "Would be an error: vibrate from uid " + vib.mUid);
402                }
403                mH.post(mVibrationRunnable);
404                return;
405            }
406        } catch (RemoteException e) {
407        }
408        if (vib.mTimeout != 0) {
409            doVibratorOn(vib.mTimeout, vib.mUid, vib.mUsageHint);
410            mH.postDelayed(mVibrationRunnable, vib.mTimeout);
411        } else {
412            // mThread better be null here. doCancelVibrate should always be
413            // called before startNextVibrationLocked or startVibrationLocked.
414            mThread = new VibrateThread(vib);
415            mThread.start();
416        }
417    }
418
419    private void reportFinishVibrationLocked() {
420        if (mCurrentVibration != null) {
421            try {
422                mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
423                        AppOpsManager.OP_VIBRATE, mCurrentVibration.mUid,
424                        mCurrentVibration.mOpPkg);
425            } catch (RemoteException e) {
426            }
427            mCurrentVibration = null;
428        }
429    }
430
431    // Lock held on mVibrations
432    private Vibration removeVibrationLocked(IBinder token) {
433        ListIterator<Vibration> iter = mVibrations.listIterator(0);
434        while (iter.hasNext()) {
435            Vibration vib = iter.next();
436            if (vib.mToken == token) {
437                iter.remove();
438                unlinkVibration(vib);
439                return vib;
440            }
441        }
442        // We might be looking for a simple vibration which is only stored in
443        // mCurrentVibration.
444        if (mCurrentVibration != null && mCurrentVibration.mToken == token) {
445            unlinkVibration(mCurrentVibration);
446            return mCurrentVibration;
447        }
448        return null;
449    }
450
451    private void unlinkVibration(Vibration vib) {
452        if (vib.mPattern != null) {
453            // If Vibration object has a pattern,
454            // the Vibration object has also been linkedToDeath.
455            vib.mToken.unlinkToDeath(vib, 0);
456        }
457    }
458
459    private void updateInputDeviceVibrators() {
460        synchronized (mVibrations) {
461            doCancelVibrateLocked();
462
463            synchronized (mInputDeviceVibrators) {
464                mVibrateInputDevicesSetting = false;
465                try {
466                    mVibrateInputDevicesSetting = Settings.System.getIntForUser(
467                            mContext.getContentResolver(),
468                            Settings.System.VIBRATE_INPUT_DEVICES, UserHandle.USER_CURRENT) > 0;
469                } catch (SettingNotFoundException snfe) {
470                }
471
472                mLowPowerMode = mPowerManagerInternal.getLowPowerModeEnabled();
473
474                if (mVibrateInputDevicesSetting) {
475                    if (!mInputDeviceListenerRegistered) {
476                        mInputDeviceListenerRegistered = true;
477                        mIm.registerInputDeviceListener(this, mH);
478                    }
479                } else {
480                    if (mInputDeviceListenerRegistered) {
481                        mInputDeviceListenerRegistered = false;
482                        mIm.unregisterInputDeviceListener(this);
483                    }
484                }
485
486                mInputDeviceVibrators.clear();
487                if (mVibrateInputDevicesSetting) {
488                    int[] ids = mIm.getInputDeviceIds();
489                    for (int i = 0; i < ids.length; i++) {
490                        InputDevice device = mIm.getInputDevice(ids[i]);
491                        Vibrator vibrator = device.getVibrator();
492                        if (vibrator.hasVibrator()) {
493                            mInputDeviceVibrators.add(vibrator);
494                        }
495                    }
496                }
497            }
498
499            startNextVibrationLocked();
500        }
501    }
502
503    @Override
504    public void onInputDeviceAdded(int deviceId) {
505        updateInputDeviceVibrators();
506    }
507
508    @Override
509    public void onInputDeviceChanged(int deviceId) {
510        updateInputDeviceVibrators();
511    }
512
513    @Override
514    public void onInputDeviceRemoved(int deviceId) {
515        updateInputDeviceVibrators();
516    }
517
518    private boolean doVibratorExists() {
519        // For now, we choose to ignore the presence of input devices that have vibrators
520        // when reporting whether the device has a vibrator.  Applications often use this
521        // information to decide whether to enable certain features so they expect the
522        // result of hasVibrator() to be constant.  For now, just report whether
523        // the device has a built-in vibrator.
524        //synchronized (mInputDeviceVibrators) {
525        //    return !mInputDeviceVibrators.isEmpty() || vibratorExists();
526        //}
527        return vibratorExists();
528    }
529
530    private void doVibratorOn(long millis, int uid, int usageHint) {
531        synchronized (mInputDeviceVibrators) {
532            if (DEBUG) {
533                Slog.d(TAG, "Turning vibrator on for " + millis + " ms.");
534            }
535            try {
536                mBatteryStatsService.noteVibratorOn(uid, millis);
537                mCurVibUid = uid;
538            } catch (RemoteException e) {
539            }
540            final int vibratorCount = mInputDeviceVibrators.size();
541            if (vibratorCount != 0) {
542                final AudioAttributes attributes = new AudioAttributes.Builder().setUsage(usageHint)
543                        .build();
544                for (int i = 0; i < vibratorCount; i++) {
545                    mInputDeviceVibrators.get(i).vibrate(millis, attributes);
546                }
547            } else {
548                vibratorOn(millis);
549            }
550        }
551    }
552
553    private void doVibratorOff() {
554        synchronized (mInputDeviceVibrators) {
555            if (DEBUG) {
556                Slog.d(TAG, "Turning vibrator off.");
557            }
558            if (mCurVibUid >= 0) {
559                try {
560                    mBatteryStatsService.noteVibratorOff(mCurVibUid);
561                } catch (RemoteException e) {
562                }
563                mCurVibUid = -1;
564            }
565            final int vibratorCount = mInputDeviceVibrators.size();
566            if (vibratorCount != 0) {
567                for (int i = 0; i < vibratorCount; i++) {
568                    mInputDeviceVibrators.get(i).cancel();
569                }
570            } else {
571                vibratorOff();
572            }
573        }
574    }
575
576    private class VibrateThread extends Thread {
577        final Vibration mVibration;
578        boolean mDone;
579
580        VibrateThread(Vibration vib) {
581            mVibration = vib;
582            mTmpWorkSource.set(vib.mUid);
583            mWakeLock.setWorkSource(mTmpWorkSource);
584            mWakeLock.acquire();
585        }
586
587        private void delay(long duration) {
588            if (duration > 0) {
589                long bedtime = duration + SystemClock.uptimeMillis();
590                do {
591                    try {
592                        this.wait(duration);
593                    }
594                    catch (InterruptedException e) {
595                    }
596                    if (mDone) {
597                        break;
598                    }
599                    duration = bedtime - SystemClock.uptimeMillis();
600                } while (duration > 0);
601            }
602        }
603
604        public void run() {
605            Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
606            synchronized (this) {
607                final long[] pattern = mVibration.mPattern;
608                final int len = pattern.length;
609                final int repeat = mVibration.mRepeat;
610                final int uid = mVibration.mUid;
611                final int usageHint = mVibration.mUsageHint;
612                int index = 0;
613                long duration = 0;
614
615                while (!mDone) {
616                    // add off-time duration to any accumulated on-time duration
617                    if (index < len) {
618                        duration += pattern[index++];
619                    }
620
621                    // sleep until it is time to start the vibrator
622                    delay(duration);
623                    if (mDone) {
624                        break;
625                    }
626
627                    if (index < len) {
628                        // read on-time duration and start the vibrator
629                        // duration is saved for delay() at top of loop
630                        duration = pattern[index++];
631                        if (duration > 0) {
632                            VibratorService.this.doVibratorOn(duration, uid, usageHint);
633                        }
634                    } else {
635                        if (repeat < 0) {
636                            break;
637                        } else {
638                            index = repeat;
639                            duration = 0;
640                        }
641                    }
642                }
643                mWakeLock.release();
644            }
645            synchronized (mVibrations) {
646                if (mThread == this) {
647                    mThread = null;
648                }
649                if (!mDone) {
650                    // If this vibration finished naturally, start the next
651                    // vibration.
652                    mVibrations.remove(mVibration);
653                    unlinkVibration(mVibration);
654                    startNextVibrationLocked();
655                }
656            }
657        }
658    }
659
660    BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
661        @Override
662        public void onReceive(Context context, Intent intent) {
663            if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
664                synchronized (mVibrations) {
665                    // When the system is entering a non-interactive state, we want
666                    // to cancel vibrations in case a misbehaving app has abandoned
667                    // them.  However it may happen that the system is currently playing
668                    // haptic feedback as part of the transition.  So we don't cancel
669                    // system vibrations.
670                    if (mCurrentVibration != null
671                            && !mCurrentVibration.isSystemHapticFeedback()) {
672                        doCancelVibrateLocked();
673                    }
674
675                    // Clear all remaining vibrations.
676                    Iterator<Vibration> it = mVibrations.iterator();
677                    while (it.hasNext()) {
678                        Vibration vibration = it.next();
679                        if (vibration != mCurrentVibration) {
680                            unlinkVibration(vibration);
681                            it.remove();
682                        }
683                    }
684                }
685            }
686        }
687    };
688}
689