VibratorService.java revision 57d94d9fb1e55345307e579977138aaf6177e388
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.content.res.Resources;
26import android.database.ContentObserver;
27import android.hardware.input.InputManager;
28import android.hardware.vibrator.V1_0.Constants.EffectStrength;
29import android.media.AudioManager;
30import android.os.PowerSaveState;
31import android.os.BatteryStats;
32import android.os.Handler;
33import android.os.IVibratorService;
34import android.os.PowerManager;
35import android.os.PowerManagerInternal;
36import android.os.Process;
37import android.os.RemoteException;
38import android.os.ResultReceiver;
39import android.os.IBinder;
40import android.os.Binder;
41import android.os.ServiceManager;
42import android.os.ShellCallback;
43import android.os.ShellCommand;
44import android.os.SystemClock;
45import android.os.UserHandle;
46import android.os.Vibrator;
47import android.os.VibrationEffect;
48import android.os.WorkSource;
49import android.provider.Settings;
50import android.provider.Settings.SettingNotFoundException;
51import android.util.Slog;
52import android.view.InputDevice;
53import android.media.AudioAttributes;
54
55import com.android.internal.app.IAppOpsService;
56import com.android.internal.app.IBatteryStats;
57import com.android.internal.util.DumpUtils;
58import com.android.server.power.BatterySaverPolicy.ServiceType;
59
60import java.io.FileDescriptor;
61import java.io.PrintWriter;
62import java.util.ArrayList;
63import java.util.Arrays;
64import java.util.Iterator;
65import java.util.LinkedList;
66import java.util.ListIterator;
67
68public class VibratorService extends IVibratorService.Stub
69        implements InputManager.InputDeviceListener {
70    private static final String TAG = "VibratorService";
71    private static final boolean DEBUG = false;
72    private static final String SYSTEM_UI_PACKAGE = "com.android.systemui";
73
74    private final LinkedList<VibrationInfo> mPreviousVibrations;
75    private final int mPreviousVibrationsLimit;
76    private final boolean mSupportsAmplitudeControl;
77    private final int mDefaultVibrationAmplitude;
78    private final VibrationEffect[] mFallbackEffects;
79    private final WorkSource mTmpWorkSource = new WorkSource();
80    private final Handler mH = new Handler();
81    private final Object mLock = new Object();
82
83    private final Context mContext;
84    private final PowerManager.WakeLock mWakeLock;
85    private final IAppOpsService mAppOpsService;
86    private final IBatteryStats mBatteryStatsService;
87    private PowerManagerInternal mPowerManagerInternal;
88    private InputManager mIm;
89
90    private volatile VibrateThread mThread;
91
92    // mInputDeviceVibrators lock should be acquired after mLock, if both are
93    // to be acquired
94    private final ArrayList<Vibrator> mInputDeviceVibrators = new ArrayList<Vibrator>();
95    private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators
96    private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators
97
98    private Vibration mCurrentVibration;
99    private int mCurVibUid = -1;
100    private boolean mLowPowerMode;
101    private SettingsObserver mSettingObserver;
102
103    native static boolean vibratorExists();
104    native static void vibratorInit();
105    native static void vibratorOn(long milliseconds);
106    native static void vibratorOff();
107    native static boolean vibratorSupportsAmplitudeControl();
108    native static void vibratorSetAmplitude(int amplitude);
109    native static long vibratorPerformEffect(long effect, long strength);
110
111    private class Vibration implements IBinder.DeathRecipient {
112        private final IBinder mToken;
113        private final VibrationEffect mEffect;
114        private final long mStartTime;
115        private final int mUsageHint;
116        private final int mUid;
117        private final String mOpPkg;
118
119        private Vibration(IBinder token, VibrationEffect effect,
120                int usageHint, int uid, String opPkg) {
121            mToken = token;
122            mEffect = effect;
123            mStartTime = SystemClock.uptimeMillis();
124            mUsageHint = usageHint;
125            mUid = uid;
126            mOpPkg = opPkg;
127        }
128
129        public void binderDied() {
130            synchronized (mLock) {
131                if (this == mCurrentVibration) {
132                    doCancelVibrateLocked();
133                }
134            }
135        }
136
137        public boolean hasLongerTimeout(long millis) {
138            // If the current effect is a one shot vibration that will end after the given timeout
139            // for the new one shot vibration, then just let the current vibration finish. All
140            // other effect types will get pre-empted.
141            if (mEffect instanceof VibrationEffect.OneShot) {
142                VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) mEffect;
143                return mStartTime + oneShot.getTiming() > SystemClock.uptimeMillis() + millis;
144            }
145            return false;
146        }
147
148        public boolean isSystemHapticFeedback() {
149            boolean repeating = false;
150            if (mEffect instanceof VibrationEffect.Waveform) {
151                VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) mEffect;
152                repeating = (waveform.getRepeatIndex() < 0);
153            }
154            return (mUid == Process.SYSTEM_UID || mUid == 0 || SYSTEM_UI_PACKAGE.equals(mOpPkg))
155                    && !repeating;
156        }
157    }
158
159    private static class VibrationInfo {
160        private final long mStartTime;
161        private final VibrationEffect mEffect;
162        private final int mUsageHint;
163        private final int mUid;
164        private final String mOpPkg;
165
166        public VibrationInfo(long startTime, VibrationEffect effect,
167                int usageHint, int uid, String opPkg) {
168            mStartTime = startTime;
169            mEffect = effect;
170            mUsageHint = usageHint;
171            mUid = uid;
172            mOpPkg = opPkg;
173        }
174
175        @Override
176        public String toString() {
177            return new StringBuilder()
178                    .append(", startTime: ")
179                    .append(mStartTime)
180                    .append(", effect: ")
181                    .append(mEffect)
182                    .append(", usageHint: ")
183                    .append(mUsageHint)
184                    .append(", uid: ")
185                    .append(mUid)
186                    .append(", opPkg: ")
187                    .append(mOpPkg)
188                    .toString();
189        }
190    }
191
192    VibratorService(Context context) {
193        vibratorInit();
194        // Reset the hardware to a default state, in case this is a runtime
195        // restart instead of a fresh boot.
196        vibratorOff();
197
198        mSupportsAmplitudeControl = vibratorSupportsAmplitudeControl();
199
200        mContext = context;
201        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
202        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
203        mWakeLock.setReferenceCounted(true);
204
205        mAppOpsService =
206            IAppOpsService.Stub.asInterface(ServiceManager.getService(Context.APP_OPS_SERVICE));
207        mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
208                BatteryStats.SERVICE_NAME));
209
210        mPreviousVibrationsLimit = mContext.getResources().getInteger(
211                com.android.internal.R.integer.config_previousVibrationsDumpLimit);
212
213        mDefaultVibrationAmplitude = mContext.getResources().getInteger(
214                com.android.internal.R.integer.config_defaultVibrationAmplitude);
215
216        mPreviousVibrations = new LinkedList<>();
217
218        IntentFilter filter = new IntentFilter();
219        filter.addAction(Intent.ACTION_SCREEN_OFF);
220        context.registerReceiver(mIntentReceiver, filter);
221
222        long[] clickEffectTimings = getLongIntArray(context.getResources(),
223                com.android.internal.R.array.config_virtualKeyVibePattern);
224        VibrationEffect clickEffect = createEffect(clickEffectTimings);
225        VibrationEffect doubleClickEffect = VibrationEffect.createWaveform(
226                new long[] {0, 30, 100, 30} /*timings*/, -1);
227        long[] tickEffectTimings = getLongIntArray(context.getResources(),
228                com.android.internal.R.array.config_clockTickVibePattern);
229        VibrationEffect tickEffect = createEffect(tickEffectTimings);
230
231        mFallbackEffects = new VibrationEffect[] { clickEffect, doubleClickEffect, tickEffect };
232    }
233
234    private static VibrationEffect createEffect(long[] timings) {
235        if (timings == null || timings.length == 0) {
236            return null;
237        } else if (timings.length == 1) {
238            return VibrationEffect.createOneShot(timings[0], VibrationEffect.DEFAULT_AMPLITUDE);
239        } else {
240            return VibrationEffect.createWaveform(timings, -1);
241        }
242    }
243
244    public void systemReady() {
245        mIm = mContext.getSystemService(InputManager.class);
246        mSettingObserver = new SettingsObserver(mH);
247
248        mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
249        mPowerManagerInternal.registerLowPowerModeObserver(
250                new PowerManagerInternal.LowPowerModeListener() {
251                    @Override
252                    public int getServiceType() {
253                        return ServiceType.VIBRATION;
254                    }
255
256                    @Override
257                    public void onLowPowerModeChanged(PowerSaveState result) {
258                        updateVibrators();
259                    }
260        });
261
262        mContext.getContentResolver().registerContentObserver(
263                Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES),
264                true, mSettingObserver, UserHandle.USER_ALL);
265
266        mContext.registerReceiver(new BroadcastReceiver() {
267            @Override
268            public void onReceive(Context context, Intent intent) {
269                updateVibrators();
270            }
271        }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mH);
272
273        updateVibrators();
274    }
275
276    private final class SettingsObserver extends ContentObserver {
277        public SettingsObserver(Handler handler) {
278            super(handler);
279        }
280
281        @Override
282        public void onChange(boolean SelfChange) {
283            updateVibrators();
284        }
285    }
286
287    @Override // Binder call
288    public boolean hasVibrator() {
289        return doVibratorExists();
290    }
291
292    @Override // Binder call
293    public boolean hasAmplitudeControl() {
294        synchronized (mInputDeviceVibrators) {
295            // Input device vibrators don't support amplitude controls yet, but are still used over
296            // the system vibrator when connected.
297            return mSupportsAmplitudeControl && mInputDeviceVibrators.isEmpty();
298        }
299    }
300
301    private void verifyIncomingUid(int uid) {
302        if (uid == Binder.getCallingUid()) {
303            return;
304        }
305        if (Binder.getCallingPid() == Process.myPid()) {
306            return;
307        }
308        mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
309                Binder.getCallingPid(), Binder.getCallingUid(), null);
310    }
311
312    /**
313     * Validate the incoming VibrationEffect.
314     *
315     * We can't throw exceptions here since we might be called from some system_server component,
316     * which would bring the whole system down.
317     *
318     * @return whether the VibrationEffect is valid
319     */
320    private static boolean verifyVibrationEffect(VibrationEffect effect) {
321        if (effect == null) {
322            // Effect must not be null.
323            Slog.wtf(TAG, "effect must not be null");
324            return false;
325        }
326        try {
327            effect.validate();
328        } catch (Exception e) {
329            Slog.wtf(TAG, "Encountered issue when verifying VibrationEffect.", e);
330            return false;
331        }
332        return true;
333    }
334
335    private static long[] getLongIntArray(Resources r, int resid) {
336        int[] ar = r.getIntArray(resid);
337        if (ar == null) {
338            return null;
339        }
340        long[] out = new long[ar.length];
341        for (int i = 0; i < ar.length; i++) {
342            out[i] = ar[i];
343        }
344        return out;
345    }
346
347    @Override // Binder call
348    public void vibrate(int uid, String opPkg, VibrationEffect effect, int usageHint,
349            IBinder token) {
350        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
351                != PackageManager.PERMISSION_GRANTED) {
352            throw new SecurityException("Requires VIBRATE permission");
353        }
354        if (token == null) {
355            Slog.e(TAG, "token must not be null");
356            return;
357        }
358        verifyIncomingUid(uid);
359        if (!verifyVibrationEffect(effect)) {
360            return;
361        }
362
363        // If our current vibration is longer than the new vibration and is the same amplitude,
364        // then just let the current one finish.
365        if (effect instanceof VibrationEffect.OneShot
366                && mCurrentVibration != null
367                && mCurrentVibration.mEffect instanceof VibrationEffect.OneShot) {
368            VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect;
369            VibrationEffect.OneShot currentOneShot =
370                    (VibrationEffect.OneShot) mCurrentVibration.mEffect;
371            if (mCurrentVibration.hasLongerTimeout(newOneShot.getTiming())
372                    && newOneShot.getAmplitude() == currentOneShot.getAmplitude()) {
373                if (DEBUG) {
374                    Slog.e(TAG, "Ignoring incoming vibration in favor of current vibration");
375                }
376                return;
377            }
378        }
379
380        Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg);
381
382        // Only link against waveforms since they potentially don't have a finish if
383        // they're repeating. Let other effects just play out until they're done.
384        if (effect instanceof VibrationEffect.Waveform) {
385            try {
386                token.linkToDeath(vib, 0);
387            } catch (RemoteException e) {
388                return;
389            }
390        }
391
392
393        long ident = Binder.clearCallingIdentity();
394        try {
395            synchronized (mLock) {
396                doCancelVibrateLocked();
397                startVibrationLocked(vib);
398                addToPreviousVibrationsLocked(vib);
399            }
400        } finally {
401            Binder.restoreCallingIdentity(ident);
402        }
403    }
404
405    private void addToPreviousVibrationsLocked(Vibration vib) {
406        if (mPreviousVibrations.size() > mPreviousVibrationsLimit) {
407            mPreviousVibrations.removeFirst();
408        }
409        mPreviousVibrations.addLast(new VibrationInfo(
410                    vib.mStartTime, vib.mEffect, vib.mUsageHint, vib.mUid, vib.mOpPkg));
411    }
412
413    @Override // Binder call
414    public void cancelVibrate(IBinder token) {
415        mContext.enforceCallingOrSelfPermission(
416                android.Manifest.permission.VIBRATE,
417                "cancelVibrate");
418
419        synchronized (mLock) {
420            if (mCurrentVibration != null && mCurrentVibration.mToken == token) {
421                if (DEBUG) {
422                    Slog.d(TAG, "Canceling vibration.");
423                }
424                long ident = Binder.clearCallingIdentity();
425                try {
426                    doCancelVibrateLocked();
427                } finally {
428                    Binder.restoreCallingIdentity(ident);
429                }
430            }
431        }
432    }
433
434    private final Runnable mVibrationEndRunnable = new Runnable() {
435        @Override
436        public void run() {
437            onVibrationFinished();
438        }
439    };
440
441    private void doCancelVibrateLocked() {
442        mH.removeCallbacks(mVibrationEndRunnable);
443        if (mThread != null) {
444            mThread.cancel();
445            mThread = null;
446        }
447        doVibratorOff();
448        reportFinishVibrationLocked();
449    }
450
451    // Callback for whenever the current vibration has finished played out
452    public void onVibrationFinished() {
453        if (DEBUG) {
454            Slog.e(TAG, "Vibration finished, cleaning up");
455        }
456        synchronized (mLock) {
457            // Make sure the vibration is really done. This also reports that the vibration is
458            // finished.
459            doCancelVibrateLocked();
460        }
461    }
462
463    private void startVibrationLocked(final Vibration vib) {
464        if (mLowPowerMode && vib.mUsageHint != AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
465            if (DEBUG) {
466                Slog.e(TAG, "Vibrate ignored, low power mode");
467            }
468            return;
469        }
470
471        if (vib.mUsageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE &&
472                !shouldVibrateForRingtone()) {
473            if (DEBUG) {
474                Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones");
475            }
476            return;
477        }
478
479        final int mode = getAppOpMode(vib);
480        if (mode != AppOpsManager.MODE_ALLOWED) {
481            if (mode == AppOpsManager.MODE_ERRORED) {
482                // We might be getting calls from within system_server, so we don't actually want
483                // to throw a SecurityException here.
484                Slog.w(TAG, "Would be an error: vibrate from uid " + vib.mUid);
485            }
486            return;
487        }
488        startVibrationInnerLocked(vib);
489    }
490
491    private void startVibrationInnerLocked(Vibration vib) {
492        mCurrentVibration = vib;
493        if (vib.mEffect instanceof VibrationEffect.OneShot) {
494            VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.mEffect;
495            doVibratorOn(oneShot.getTiming(), oneShot.getAmplitude(), vib.mUid, vib.mUsageHint);
496            mH.postDelayed(mVibrationEndRunnable, oneShot.getTiming());
497        } else if (vib.mEffect instanceof VibrationEffect.Waveform) {
498            // mThread better be null here. doCancelVibrate should always be
499            // called before startNextVibrationLocked or startVibrationLocked.
500            VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.mEffect;
501            mThread = new VibrateThread(waveform, vib.mUid, vib.mUsageHint);
502            mThread.start();
503        } else if (vib.mEffect instanceof VibrationEffect.Prebaked) {
504            long timeout = doVibratorPrebakedEffectLocked(vib);
505            if (timeout > 0) {
506                mH.postDelayed(mVibrationEndRunnable, timeout);
507            }
508        } else {
509            Slog.e(TAG, "Unknown vibration type, ignoring");
510        }
511    }
512
513    private boolean shouldVibrateForRingtone() {
514        AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
515        int ringerMode = audioManager.getRingerModeInternal();
516        // "Also vibrate for calls" Setting in Sound
517        if (Settings.System.getInt(
518                mContext.getContentResolver(), Settings.System.VIBRATE_WHEN_RINGING, 0) != 0) {
519            return ringerMode != AudioManager.RINGER_MODE_SILENT;
520        } else {
521            return ringerMode == AudioManager.RINGER_MODE_VIBRATE;
522        }
523    }
524
525    private int getAppOpMode(Vibration vib) {
526        int mode;
527        try {
528            mode = mAppOpsService.checkAudioOperation(AppOpsManager.OP_VIBRATE,
529                    vib.mUsageHint, vib.mUid, vib.mOpPkg);
530            if (mode == AppOpsManager.MODE_ALLOWED) {
531                mode = mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
532                    AppOpsManager.OP_VIBRATE, vib.mUid, vib.mOpPkg);
533            }
534        } catch (RemoteException e) {
535            Slog.e(TAG, "Failed to get appop mode for vibration!", e);
536            mode = AppOpsManager.MODE_IGNORED;
537        }
538        return mode;
539    }
540
541    private void reportFinishVibrationLocked() {
542        if (mCurrentVibration != null) {
543            try {
544                mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
545                        AppOpsManager.OP_VIBRATE, mCurrentVibration.mUid,
546                        mCurrentVibration.mOpPkg);
547            } catch (RemoteException e) { }
548            mCurrentVibration = null;
549        }
550    }
551
552    private void unlinkVibration(Vibration vib) {
553        if (vib.mEffect instanceof VibrationEffect.Waveform) {
554            vib.mToken.unlinkToDeath(vib, 0);
555        }
556    }
557
558    private void updateVibrators() {
559        synchronized (mLock) {
560            boolean devicesUpdated = updateInputDeviceVibratorsLocked();
561            boolean lowPowerModeUpdated = updateLowPowerModeLocked();
562
563            if (devicesUpdated || lowPowerModeUpdated) {
564                // If the state changes out from under us then just reset.
565                doCancelVibrateLocked();
566            }
567        }
568    }
569
570    private boolean updateInputDeviceVibratorsLocked() {
571        boolean changed = false;
572        boolean vibrateInputDevices = false;
573        try {
574            vibrateInputDevices = Settings.System.getIntForUser(
575                    mContext.getContentResolver(),
576                    Settings.System.VIBRATE_INPUT_DEVICES, UserHandle.USER_CURRENT) > 0;
577        } catch (SettingNotFoundException snfe) {
578        }
579        if (vibrateInputDevices != mVibrateInputDevicesSetting) {
580            changed = true;
581            mVibrateInputDevicesSetting = vibrateInputDevices;
582        }
583
584        if (mVibrateInputDevicesSetting) {
585            if (!mInputDeviceListenerRegistered) {
586                mInputDeviceListenerRegistered = true;
587                mIm.registerInputDeviceListener(this, mH);
588            }
589        } else {
590            if (mInputDeviceListenerRegistered) {
591                mInputDeviceListenerRegistered = false;
592                mIm.unregisterInputDeviceListener(this);
593            }
594        }
595
596        mInputDeviceVibrators.clear();
597        if (mVibrateInputDevicesSetting) {
598            int[] ids = mIm.getInputDeviceIds();
599            for (int i = 0; i < ids.length; i++) {
600                InputDevice device = mIm.getInputDevice(ids[i]);
601                Vibrator vibrator = device.getVibrator();
602                if (vibrator.hasVibrator()) {
603                    mInputDeviceVibrators.add(vibrator);
604                }
605            }
606            return true;
607        }
608        return changed;
609    }
610
611    private boolean updateLowPowerModeLocked() {
612        boolean lowPowerMode = mPowerManagerInternal
613                .getLowPowerState(ServiceType.VIBRATION).batterySaverEnabled;
614        if (lowPowerMode != mLowPowerMode) {
615            mLowPowerMode = lowPowerMode;
616            return true;
617        }
618        return false;
619    }
620
621    @Override
622    public void onInputDeviceAdded(int deviceId) {
623        updateVibrators();
624    }
625
626    @Override
627    public void onInputDeviceChanged(int deviceId) {
628        updateVibrators();
629    }
630
631    @Override
632    public void onInputDeviceRemoved(int deviceId) {
633        updateVibrators();
634    }
635
636    private boolean doVibratorExists() {
637        // For now, we choose to ignore the presence of input devices that have vibrators
638        // when reporting whether the device has a vibrator.  Applications often use this
639        // information to decide whether to enable certain features so they expect the
640        // result of hasVibrator() to be constant.  For now, just report whether
641        // the device has a built-in vibrator.
642        //synchronized (mInputDeviceVibrators) {
643        //    return !mInputDeviceVibrators.isEmpty() || vibratorExists();
644        //}
645        return vibratorExists();
646    }
647
648    private void doVibratorOn(long millis, int amplitude, int uid, int usageHint) {
649        synchronized (mInputDeviceVibrators) {
650            if (amplitude == VibrationEffect.DEFAULT_AMPLITUDE) {
651                amplitude = mDefaultVibrationAmplitude;
652            }
653            if (DEBUG) {
654                Slog.d(TAG, "Turning vibrator on for " + millis + " ms" +
655                        " with amplitude " + amplitude + ".");
656            }
657            noteVibratorOnLocked(uid, millis);
658            final int vibratorCount = mInputDeviceVibrators.size();
659            if (vibratorCount != 0) {
660                final AudioAttributes attributes =
661                        new AudioAttributes.Builder().setUsage(usageHint).build();
662                for (int i = 0; i < vibratorCount; i++) {
663                    mInputDeviceVibrators.get(i).vibrate(millis, attributes);
664                }
665            } else {
666                // Note: ordering is important here! Many haptic drivers will reset their amplitude
667                // when enabled, so we always have to enable frst, then set the amplitude.
668                vibratorOn(millis);
669                doVibratorSetAmplitude(amplitude);
670            }
671        }
672    }
673
674    private void doVibratorSetAmplitude(int amplitude) {
675        if (mSupportsAmplitudeControl) {
676            vibratorSetAmplitude(amplitude);
677        }
678    }
679
680    private void doVibratorOff() {
681        synchronized (mInputDeviceVibrators) {
682            if (DEBUG) {
683                Slog.d(TAG, "Turning vibrator off.");
684            }
685            noteVibratorOffLocked();
686            final int vibratorCount = mInputDeviceVibrators.size();
687            if (vibratorCount != 0) {
688                for (int i = 0; i < vibratorCount; i++) {
689                    mInputDeviceVibrators.get(i).cancel();
690                }
691            } else {
692                vibratorOff();
693            }
694        }
695    }
696
697    private long doVibratorPrebakedEffectLocked(Vibration vib) {
698        synchronized (mInputDeviceVibrators) {
699            VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.mEffect;
700            // Input devices don't support prebaked effect, so skip trying it with them.
701            final int vibratorCount = mInputDeviceVibrators.size();
702            if (vibratorCount == 0) {
703                long timeout = vibratorPerformEffect(prebaked.getId(), EffectStrength.MEDIUM);
704                if (timeout > 0) {
705                    noteVibratorOnLocked(vib.mUid, timeout);
706                    return timeout;
707                }
708            }
709            final int id = prebaked.getId();
710            if (id < 0 || id >= mFallbackEffects.length || mFallbackEffects[id] == null) {
711                Slog.w(TAG, "Failed to play prebaked effect, no fallback");
712                return 0;
713            }
714            VibrationEffect effect = mFallbackEffects[id];
715            Vibration fallbackVib =
716                    new Vibration(vib.mToken, effect, vib.mUsageHint, vib.mUid, vib.mOpPkg);
717            startVibrationInnerLocked(fallbackVib);
718        }
719        return 0;
720    }
721
722    private void noteVibratorOnLocked(int uid, long millis) {
723        try {
724            mBatteryStatsService.noteVibratorOn(uid, millis);
725            mCurVibUid = uid;
726        } catch (RemoteException e) {
727        }
728    }
729
730    private void noteVibratorOffLocked() {
731        if (mCurVibUid >= 0) {
732            try {
733                mBatteryStatsService.noteVibratorOff(mCurVibUid);
734            } catch (RemoteException e) { }
735            mCurVibUid = -1;
736        }
737    }
738
739    private class VibrateThread extends Thread {
740        private final VibrationEffect.Waveform mWaveform;
741        private final int mUid;
742        private final int mUsageHint;
743
744        private boolean mForceStop;
745
746        VibrateThread(VibrationEffect.Waveform waveform, int uid, int usageHint) {
747            mWaveform = waveform;
748            mUid = uid;
749            mUsageHint = usageHint;
750            mTmpWorkSource.set(uid);
751            mWakeLock.setWorkSource(mTmpWorkSource);
752        }
753
754        private long delayLocked(long duration) {
755            long durationRemaining = duration;
756            if (duration > 0) {
757                final long bedtime = duration + SystemClock.uptimeMillis();
758                do {
759                    try {
760                        this.wait(durationRemaining);
761                    }
762                    catch (InterruptedException e) { }
763                    if (mForceStop) {
764                        break;
765                    }
766                    durationRemaining = bedtime - SystemClock.uptimeMillis();
767                } while (durationRemaining > 0);
768                return duration - durationRemaining;
769            }
770            return 0;
771        }
772
773        public void run() {
774            Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
775            mWakeLock.acquire();
776            try {
777                boolean finished = playWaveform();
778                if (finished) {
779                    onVibrationFinished();
780                }
781            } finally {
782                mWakeLock.release();
783            }
784        }
785
786        /**
787         * Play the waveform.
788         *
789         * @return true if it finished naturally, false otherwise (e.g. it was canceled).
790         */
791        public boolean playWaveform() {
792            synchronized (this) {
793                final long[] timings = mWaveform.getTimings();
794                final int[] amplitudes = mWaveform.getAmplitudes();
795                final int len = timings.length;
796                final int repeat = mWaveform.getRepeatIndex();
797
798                int index = 0;
799                long onDuration = 0;
800                while (!mForceStop) {
801                    if (index < len) {
802                        final int amplitude = amplitudes[index];
803                        final long duration = timings[index++];
804                        if (duration <= 0) {
805                            continue;
806                        }
807                        if (amplitude != 0) {
808                            if (onDuration <= 0) {
809                                // Telling the vibrator to start multiple times usually causes
810                                // effects to feel "choppy" because the motor resets at every on
811                                // command.  Instead we figure out how long our next "on" period is
812                                // going to be, tell the motor to stay on for the full duration,
813                                // and then wake up to change the amplitude at the appropriate
814                                // intervals.
815                                onDuration =
816                                        getTotalOnDuration(timings, amplitudes, index - 1, repeat);
817                                doVibratorOn(onDuration, amplitude, mUid, mUsageHint);
818                            } else {
819                                doVibratorSetAmplitude(amplitude);
820                            }
821                        }
822
823                        long waitTime = delayLocked(duration);
824                        if (amplitude != 0) {
825                            onDuration -= waitTime;
826                        }
827                    } else if (repeat < 0) {
828                        break;
829                    } else {
830                        index = repeat;
831                    }
832                }
833                return !mForceStop;
834            }
835        }
836
837        public void cancel() {
838            synchronized (this) {
839                mThread.mForceStop = true;
840                mThread.notify();
841            }
842        }
843
844        /**
845         * Get the duration the vibrator will be on starting at startIndex until the next time it's
846         * off.
847         */
848        private long getTotalOnDuration(
849                long[] timings, int[] amplitudes, int startIndex, int repeatIndex) {
850            int i = startIndex;
851            long timing = 0;
852            while(amplitudes[i] != 0) {
853                timing += timings[i++];
854                if (i >= timings.length) {
855                    if (repeatIndex >= 0) {
856                        i = repeatIndex;
857                    } else {
858                        break;
859                    }
860                }
861                if (i == startIndex) {
862                    return 1000;
863                }
864            }
865            return timing;
866        }
867    }
868
869    BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
870        @Override
871        public void onReceive(Context context, Intent intent) {
872            if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
873                synchronized (mLock) {
874                    // When the system is entering a non-interactive state, we want
875                    // to cancel vibrations in case a misbehaving app has abandoned
876                    // them.  However it may happen that the system is currently playing
877                    // haptic feedback as part of the transition.  So we don't cancel
878                    // system vibrations.
879                    if (mCurrentVibration != null
880                            && !mCurrentVibration.isSystemHapticFeedback()) {
881                        doCancelVibrateLocked();
882                    }
883                }
884            }
885        }
886    };
887
888    @Override
889    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
890        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
891
892        pw.println("Previous vibrations:");
893        synchronized (mLock) {
894            for (VibrationInfo info : mPreviousVibrations) {
895                pw.print("  ");
896                pw.println(info.toString());
897            }
898        }
899    }
900
901    @Override
902    public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
903            String[] args, ShellCallback callback, ResultReceiver resultReceiver)
904            throws RemoteException {
905        new VibratorShellCommand(this).exec(this, in, out, err, args, callback, resultReceiver);
906    }
907
908    private final class VibratorShellCommand extends ShellCommand {
909
910        private static final long MAX_VIBRATION_MS = 200;
911
912        private final IBinder mToken;
913
914        private VibratorShellCommand(IBinder token) {
915            mToken = token;
916        }
917
918        @Override
919        public int onCommand(String cmd) {
920            if ("vibrate".equals(cmd)) {
921                return runVibrate();
922            }
923            return handleDefaultCommands(cmd);
924        }
925
926        private int runVibrate() {
927            final long duration = Long.parseLong(getNextArgRequired());
928            if (duration > MAX_VIBRATION_MS) {
929                throw new IllegalArgumentException("maximum duration is " + MAX_VIBRATION_MS);
930            }
931            String description = getNextArg();
932            if (description == null) {
933                description = "Shell command";
934            }
935
936            VibrationEffect effect =
937                    VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE);
938            vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
939                    mToken);
940            return 0;
941        }
942
943        @Override
944        public void onHelp() {
945            try (PrintWriter pw = getOutPrintWriter();) {
946                pw.println("Vibrator commands:");
947                pw.println("  help");
948                pw.println("    Prints this help text.");
949                pw.println("");
950                pw.println("  vibrate duration [description]");
951                pw.println("    Vibrates for duration milliseconds.");
952                pw.println("");
953            }
954        }
955    }
956
957}
958