Merge "Add tracing tags to vibrator"
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index fa96dd3..f93b39f 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -89,6 +89,8 @@
     public static final long TRACE_TAG_NETWORK = 1L << 21;
     /** @hide */
     public static final long TRACE_TAG_ADB = 1L << 22;
+    /** @hide */
+    public static final long TRACE_TAG_VIBRATOR = 1L << 23;
 
     private static final long TRACE_TAG_NOT_READY = 1L << 63;
     private static final int MAX_SECTION_NAME_LEN = 127;
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 7fe5d7c..14c99b2 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -44,6 +44,7 @@
 import android.os.ShellCallback;
 import android.os.ShellCommand;
 import android.os.SystemClock;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.os.Vibrator;
 import android.os.VibrationEffect;
@@ -288,13 +289,12 @@
                 com.android.internal.R.array.config_virtualKeyVibePattern);
         VibrationEffect clickEffect = createEffect(clickEffectTimings);
         VibrationEffect doubleClickEffect = VibrationEffect.createWaveform(
-               DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS, -1 /*repeatIndex*/);
+                DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS, -1 /*repeatIndex*/);
         long[] tickEffectTimings = getLongIntArray(context.getResources(),
                 com.android.internal.R.array.config_clockTickVibePattern);
         VibrationEffect tickEffect = createEffect(tickEffectTimings);
 
         mFallbackEffects = new VibrationEffect[] { clickEffect, doubleClickEffect, tickEffect };
-
     }
 
     private static VibrationEffect createEffect(long[] timings) {
@@ -308,44 +308,49 @@
     }
 
     public void systemReady() {
-        mIm = mContext.getSystemService(InputManager.class);
-        mVibrator = mContext.getSystemService(Vibrator.class);
-        mSettingObserver = new SettingsObserver(mH);
+        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorService#systemReady");
+        try {
+            mIm = mContext.getSystemService(InputManager.class);
+            mVibrator = mContext.getSystemService(Vibrator.class);
+            mSettingObserver = new SettingsObserver(mH);
 
-        mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
-        mPowerManagerInternal.registerLowPowerModeObserver(
-                new PowerManagerInternal.LowPowerModeListener() {
-                    @Override
-                    public int getServiceType() {
-                        return ServiceType.VIBRATION;
-                    }
+            mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
+            mPowerManagerInternal.registerLowPowerModeObserver(
+                    new PowerManagerInternal.LowPowerModeListener() {
+                        @Override
+                        public int getServiceType() {
+                            return ServiceType.VIBRATION;
+                        }
 
-                    @Override
-                    public void onLowPowerModeChanged(PowerSaveState result) {
-                        updateVibrators();
-                    }
-        });
+                        @Override
+                        public void onLowPowerModeChanged(PowerSaveState result) {
+                            updateVibrators();
+                        }
+            });
 
-        mContext.getContentResolver().registerContentObserver(
-                Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES),
-                true, mSettingObserver, UserHandle.USER_ALL);
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES),
+                    true, mSettingObserver, UserHandle.USER_ALL);
 
-        mContext.getContentResolver().registerContentObserver(
-                Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_INTENSITY),
-                true, mSettingObserver, UserHandle.USER_ALL);
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_INTENSITY),
+                    true, mSettingObserver, UserHandle.USER_ALL);
 
-        mContext.getContentResolver().registerContentObserver(
-                Settings.System.getUriFor(Settings.System.NOTIFICATION_VIBRATION_INTENSITY),
-                true, mSettingObserver, UserHandle.USER_ALL);
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.System.getUriFor(Settings.System.NOTIFICATION_VIBRATION_INTENSITY),
+                    true, mSettingObserver, UserHandle.USER_ALL);
 
-        mContext.registerReceiver(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                updateVibrators();
-            }
-        }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mH);
+            mContext.registerReceiver(new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    updateVibrators();
+                }
+            }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mH);
 
-        updateVibrators();
+            updateVibrators();
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+        }
     }
 
     private final class SettingsObserver extends ContentObserver {
@@ -422,60 +427,66 @@
     @Override // Binder call
     public void vibrate(int uid, String opPkg, VibrationEffect effect, int usageHint,
             IBinder token) {
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Requires VIBRATE permission");
-        }
-        if (token == null) {
-            Slog.e(TAG, "token must not be null");
-            return;
-        }
-        verifyIncomingUid(uid);
-        if (!verifyVibrationEffect(effect)) {
-            return;
-        }
-
-        // If our current vibration is longer than the new vibration and is the same amplitude,
-        // then just let the current one finish.
-        synchronized (mLock) {
-            if (effect instanceof VibrationEffect.OneShot
-                    && mCurrentVibration != null
-                    && mCurrentVibration.effect instanceof VibrationEffect.OneShot) {
-                VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect;
-                VibrationEffect.OneShot currentOneShot =
-                        (VibrationEffect.OneShot) mCurrentVibration.effect;
-                if (mCurrentVibration.hasTimeoutLongerThan(newOneShot.getDuration())
-                        && newOneShot.getAmplitude() == currentOneShot.getAmplitude()) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "Ignoring incoming vibration in favor of current vibration");
-                    }
-                    return;
-                }
+        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate");
+        try {
+            if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
+                    != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException("Requires VIBRATE permission");
             }
-
-            // If the current vibration is repeating and the incoming one is non-repeating, then
-            // ignore the non-repeating vibration. This is so that we don't cancel vibrations that
-            // are meant to grab the attention of the user, like ringtones and alarms, in favor of
-            // one-shot vibrations that are likely quite short.
-            if (!isRepeatingVibration(effect)
-                    && mCurrentVibration != null
-                    && isRepeatingVibration(mCurrentVibration.effect)) {
-                if (DEBUG) {
-                    Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
-                }
+            if (token == null) {
+                Slog.e(TAG, "token must not be null");
+                return;
+            }
+            verifyIncomingUid(uid);
+            if (!verifyVibrationEffect(effect)) {
                 return;
             }
 
-            Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg);
-            linkVibration(vib);
-            long ident = Binder.clearCallingIdentity();
-            try {
-                doCancelVibrateLocked();
-                startVibrationLocked(vib);
-                addToPreviousVibrationsLocked(vib);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
+            // If our current vibration is longer than the new vibration and is the same amplitude,
+            // then just let the current one finish.
+            synchronized (mLock) {
+                if (effect instanceof VibrationEffect.OneShot
+                        && mCurrentVibration != null
+                        && mCurrentVibration.effect instanceof VibrationEffect.OneShot) {
+                    VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect;
+                    VibrationEffect.OneShot currentOneShot =
+                            (VibrationEffect.OneShot) mCurrentVibration.effect;
+                    if (mCurrentVibration.hasTimeoutLongerThan(newOneShot.getDuration())
+                            && newOneShot.getAmplitude() == currentOneShot.getAmplitude()) {
+                        if (DEBUG) {
+                            Slog.d(TAG,
+                                    "Ignoring incoming vibration in favor of current vibration");
+                        }
+                        return;
+                    }
+                }
+
+                // If the current vibration is repeating and the incoming one is non-repeating,
+                // then ignore the non-repeating vibration. This is so that we don't cancel
+                // vibrations that are meant to grab the attention of the user, like ringtones and
+                // alarms, in favor of one-shot vibrations that are likely quite short.
+                if (!isRepeatingVibration(effect)
+                        && mCurrentVibration != null
+                        && isRepeatingVibration(mCurrentVibration.effect)) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
+                    }
+                    return;
+                }
+
+                Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg);
+                linkVibration(vib);
+                long ident = Binder.clearCallingIdentity();
+                try {
+                    doCancelVibrateLocked();
+                    startVibrationLocked(vib);
+                    addToPreviousVibrationsLocked(vib);
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
             }
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
         }
     }
 
@@ -520,13 +531,19 @@
 
     @GuardedBy("mLock")
     private void doCancelVibrateLocked() {
-        mH.removeCallbacks(mVibrationEndRunnable);
-        if (mThread != null) {
-            mThread.cancel();
-            mThread = null;
+        Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
+        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doCancelVibrateLocked");
+        try {
+            mH.removeCallbacks(mVibrationEndRunnable);
+            if (mThread != null) {
+                mThread.cancel();
+                mThread = null;
+            }
+            doVibratorOff();
+            reportFinishVibrationLocked();
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
         }
-        doVibratorOff();
-        reportFinishVibrationLocked();
     }
 
     // Callback for whenever the current vibration has finished played out
@@ -543,55 +560,68 @@
 
     @GuardedBy("mLock")
     private void startVibrationLocked(final Vibration vib) {
-        if (!isAllowedToVibrateLocked(vib)) {
-            return;
-        }
-
-        final int intensity = getCurrentIntensityLocked(vib);
-        if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
-            return;
-        }
-
-        if (vib.isRingtone() && !shouldVibrateForRingtone()) {
-            if (DEBUG) {
-                Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones");
+        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked");
+        try {
+            if (!isAllowedToVibrateLocked(vib)) {
+                return;
             }
-            return;
-        }
 
-        final int mode = getAppOpMode(vib);
-        if (mode != AppOpsManager.MODE_ALLOWED) {
-            if (mode == AppOpsManager.MODE_ERRORED) {
-                // We might be getting calls from within system_server, so we don't actually want
-                // to throw a SecurityException here.
-                Slog.w(TAG, "Would be an error: vibrate from uid " + vib.uid);
+            final int intensity = getCurrentIntensityLocked(vib);
+            if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
+                return;
             }
-            return;
+
+            if (vib.isRingtone() && !shouldVibrateForRingtone()) {
+                if (DEBUG) {
+                    Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones");
+                }
+                return;
+            }
+
+            final int mode = getAppOpMode(vib);
+            if (mode != AppOpsManager.MODE_ALLOWED) {
+                if (mode == AppOpsManager.MODE_ERRORED) {
+                    // We might be getting calls from within system_server, so we don't actually
+                    // want to throw a SecurityException here.
+                    Slog.w(TAG, "Would be an error: vibrate from uid " + vib.uid);
+                }
+                return;
+            }
+            applyVibrationIntensityScalingLocked(vib, intensity);
+            startVibrationInnerLocked(vib);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
         }
-        applyVibrationIntensityScalingLocked(vib, intensity);
-        startVibrationInnerLocked(vib);
     }
 
     @GuardedBy("mLock")
     private void startVibrationInnerLocked(Vibration vib) {
-        mCurrentVibration = vib;
-        if (vib.effect instanceof VibrationEffect.OneShot) {
-            VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect;
-            doVibratorOn(oneShot.getDuration(), oneShot.getAmplitude(), vib.uid, vib.usageHint);
-            mH.postDelayed(mVibrationEndRunnable, oneShot.getDuration());
-        } else if (vib.effect instanceof VibrationEffect.Waveform) {
-            // mThread better be null here. doCancelVibrate should always be
-            // called before startNextVibrationLocked or startVibrationLocked.
-            VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect;
-            mThread = new VibrateThread(waveform, vib.uid, vib.usageHint);
-            mThread.start();
-        } else if (vib.effect instanceof VibrationEffect.Prebaked) {
-            long timeout = doVibratorPrebakedEffectLocked(vib);
-            if (timeout > 0) {
-                mH.postDelayed(mVibrationEndRunnable, timeout);
+        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationInnerLocked");
+        try {
+            mCurrentVibration = vib;
+            if (vib.effect instanceof VibrationEffect.OneShot) {
+                Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
+                VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect;
+                doVibratorOn(oneShot.getDuration(), oneShot.getAmplitude(), vib.uid, vib.usageHint);
+                mH.postDelayed(mVibrationEndRunnable, oneShot.getDuration());
+            } else if (vib.effect instanceof VibrationEffect.Waveform) {
+                // mThread better be null here. doCancelVibrate should always be
+                // called before startNextVibrationLocked or startVibrationLocked.
+                Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
+                VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect;
+                mThread = new VibrateThread(waveform, vib.uid, vib.usageHint);
+                mThread.start();
+            } else if (vib.effect instanceof VibrationEffect.Prebaked) {
+                Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
+                long timeout = doVibratorPrebakedEffectLocked(vib);
+                if (timeout > 0) {
+                    mH.postDelayed(mVibrationEndRunnable, timeout);
+                }
+            } else {
+                Slog.e(TAG, "Unknown vibration type, ignoring");
             }
-        } else {
-            Slog.e(TAG, "Unknown vibration type, ignoring");
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
         }
     }
 
@@ -708,14 +738,19 @@
 
     @GuardedBy("mLock")
     private void reportFinishVibrationLocked() {
-        if (mCurrentVibration != null) {
-            try {
-                mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
-                        AppOpsManager.OP_VIBRATE, mCurrentVibration.uid,
-                        mCurrentVibration.opPkg);
-            } catch (RemoteException e) { }
-            unlinkVibration(mCurrentVibration);
-            mCurrentVibration = null;
+        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
+        try {
+            if (mCurrentVibration != null) {
+                try {
+                    mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
+                            AppOpsManager.OP_VIBRATE, mCurrentVibration.uid,
+                            mCurrentVibration.opPkg);
+                } catch (RemoteException e) { }
+                unlinkVibration(mCurrentVibration);
+                mCurrentVibration = null;
+            }
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
         }
     }
 
@@ -838,28 +873,34 @@
     }
 
     private void doVibratorOn(long millis, int amplitude, int uid, int usageHint) {
-        synchronized (mInputDeviceVibrators) {
-            if (amplitude == VibrationEffect.DEFAULT_AMPLITUDE) {
-                amplitude = mDefaultVibrationAmplitude;
-            }
-            if (DEBUG) {
-                Slog.d(TAG, "Turning vibrator on for " + millis + " ms" +
-                        " with amplitude " + amplitude + ".");
-            }
-            noteVibratorOnLocked(uid, millis);
-            final int vibratorCount = mInputDeviceVibrators.size();
-            if (vibratorCount != 0) {
-                final AudioAttributes attributes =
-                        new AudioAttributes.Builder().setUsage(usageHint).build();
-                for (int i = 0; i < vibratorCount; i++) {
-                    mInputDeviceVibrators.get(i).vibrate(millis, attributes);
+        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn");
+        try {
+            synchronized (mInputDeviceVibrators) {
+                if (amplitude == VibrationEffect.DEFAULT_AMPLITUDE) {
+                    amplitude = mDefaultVibrationAmplitude;
                 }
-            } else {
-                // Note: ordering is important here! Many haptic drivers will reset their amplitude
-                // when enabled, so we always have to enable frst, then set the amplitude.
-                vibratorOn(millis);
-                doVibratorSetAmplitude(amplitude);
+                if (DEBUG) {
+                    Slog.d(TAG, "Turning vibrator on for " + millis + " ms" +
+                            " with amplitude " + amplitude + ".");
+                }
+                noteVibratorOnLocked(uid, millis);
+                final int vibratorCount = mInputDeviceVibrators.size();
+                if (vibratorCount != 0) {
+                    final AudioAttributes attributes =
+                            new AudioAttributes.Builder().setUsage(usageHint).build();
+                    for (int i = 0; i < vibratorCount; i++) {
+                        mInputDeviceVibrators.get(i).vibrate(millis, attributes);
+                    }
+                } else {
+                    // Note: ordering is important here! Many haptic drivers will reset their
+                    // amplitude when enabled, so we always have to enable frst, then set the
+                    // amplitude.
+                    vibratorOn(millis);
+                    doVibratorSetAmplitude(amplitude);
+                }
             }
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
         }
     }
 
@@ -870,52 +911,63 @@
     }
 
     private void doVibratorOff() {
-        synchronized (mInputDeviceVibrators) {
-            if (DEBUG) {
-                Slog.d(TAG, "Turning vibrator off.");
-            }
-            noteVibratorOffLocked();
-            final int vibratorCount = mInputDeviceVibrators.size();
-            if (vibratorCount != 0) {
-                for (int i = 0; i < vibratorCount; i++) {
-                    mInputDeviceVibrators.get(i).cancel();
+        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOff");
+        try {
+            synchronized (mInputDeviceVibrators) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Turning vibrator off.");
                 }
-            } else {
-                vibratorOff();
+                noteVibratorOffLocked();
+                final int vibratorCount = mInputDeviceVibrators.size();
+                if (vibratorCount != 0) {
+                    for (int i = 0; i < vibratorCount; i++) {
+                        mInputDeviceVibrators.get(i).cancel();
+                    }
+                } else {
+                    vibratorOff();
+                }
             }
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
         }
     }
 
     @GuardedBy("mLock")
     private long doVibratorPrebakedEffectLocked(Vibration vib) {
-        final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect;
-        final boolean usingInputDeviceVibrators;
-        synchronized (mInputDeviceVibrators) {
-            usingInputDeviceVibrators = !mInputDeviceVibrators.isEmpty();
-        }
-        // Input devices don't support prebaked effect, so skip trying it with them.
-        if (!usingInputDeviceVibrators) {
-            long timeout = vibratorPerformEffect(prebaked.getId(), prebaked.getEffectStrength());
-            if (timeout > 0) {
-                noteVibratorOnLocked(vib.uid, timeout);
-                return timeout;
+        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorPrebakedEffectLocked");
+        try {
+            final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect;
+            final boolean usingInputDeviceVibrators;
+            synchronized (mInputDeviceVibrators) {
+                usingInputDeviceVibrators = !mInputDeviceVibrators.isEmpty();
             }
-        }
-        if (!prebaked.shouldFallback()) {
+            // Input devices don't support prebaked effect, so skip trying it with them.
+            if (!usingInputDeviceVibrators) {
+                long timeout = vibratorPerformEffect(prebaked.getId(),
+                        prebaked.getEffectStrength());
+                if (timeout > 0) {
+                    noteVibratorOnLocked(vib.uid, timeout);
+                    return timeout;
+                }
+            }
+            if (!prebaked.shouldFallback()) {
+                return 0;
+            }
+            VibrationEffect effect = getFallbackEffect(prebaked.getId());
+            if (effect == null) {
+                Slog.w(TAG, "Failed to play prebaked effect, no fallback");
+                return 0;
+            }
+            Vibration fallbackVib =
+                    new Vibration(vib.token, effect, vib.usageHint, vib.uid, vib.opPkg);
+            final int intensity = getCurrentIntensityLocked(fallbackVib);
+            linkVibration(fallbackVib);
+            applyVibrationIntensityScalingLocked(fallbackVib, intensity);
+            startVibrationInnerLocked(fallbackVib);
             return 0;
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
         }
-        VibrationEffect effect = getFallbackEffect(prebaked.getId());
-        if (effect == null) {
-            Slog.w(TAG, "Failed to play prebaked effect, no fallback");
-            return 0;
-        }
-        Vibration fallbackVib =
-                new Vibration(vib.token, effect, vib.usageHint, vib.uid, vib.opPkg);
-        final int intensity = getCurrentIntensityLocked(fallbackVib);
-        linkVibration(fallbackVib);
-        applyVibrationIntensityScalingLocked(fallbackVib, intensity);
-        startVibrationInnerLocked(fallbackVib);
-        return 0;
     }
 
     private VibrationEffect getFallbackEffect(int effectId) {
@@ -977,22 +1029,27 @@
         }
 
         private long delayLocked(long duration) {
-            long durationRemaining = duration;
-            if (duration > 0) {
-                final long bedtime = duration + SystemClock.uptimeMillis();
-                do {
-                    try {
-                        this.wait(durationRemaining);
-                    }
-                    catch (InterruptedException e) { }
-                    if (mForceStop) {
-                        break;
-                    }
-                    durationRemaining = bedtime - SystemClock.uptimeMillis();
-                } while (durationRemaining > 0);
-                return duration - durationRemaining;
+            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "delayLocked");
+            try {
+                long durationRemaining = duration;
+                if (duration > 0) {
+                    final long bedtime = duration + SystemClock.uptimeMillis();
+                    do {
+                        try {
+                            this.wait(durationRemaining);
+                        }
+                        catch (InterruptedException e) { }
+                        if (mForceStop) {
+                            break;
+                        }
+                        durationRemaining = bedtime - SystemClock.uptimeMillis();
+                    } while (durationRemaining > 0);
+                    return duration - durationRemaining;
+                }
+                return 0;
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
             }
-            return 0;
         }
 
         public void run() {
@@ -1014,48 +1071,53 @@
          * @return true if it finished naturally, false otherwise (e.g. it was canceled).
          */
         public boolean playWaveform() {
-            synchronized (this) {
-                final long[] timings = mWaveform.getTimings();
-                final int[] amplitudes = mWaveform.getAmplitudes();
-                final int len = timings.length;
-                final int repeat = mWaveform.getRepeatIndex();
+            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playWaveform");
+            try {
+                synchronized (this) {
+                    final long[] timings = mWaveform.getTimings();
+                    final int[] amplitudes = mWaveform.getAmplitudes();
+                    final int len = timings.length;
+                    final int repeat = mWaveform.getRepeatIndex();
 
-                int index = 0;
-                long onDuration = 0;
-                while (!mForceStop) {
-                    if (index < len) {
-                        final int amplitude = amplitudes[index];
-                        final long duration = timings[index++];
-                        if (duration <= 0) {
-                            continue;
-                        }
-                        if (amplitude != 0) {
-                            if (onDuration <= 0) {
-                                // Telling the vibrator to start multiple times usually causes
-                                // effects to feel "choppy" because the motor resets at every on
-                                // command.  Instead we figure out how long our next "on" period is
-                                // going to be, tell the motor to stay on for the full duration,
-                                // and then wake up to change the amplitude at the appropriate
-                                // intervals.
-                                onDuration =
-                                        getTotalOnDuration(timings, amplitudes, index - 1, repeat);
-                                doVibratorOn(onDuration, amplitude, mUid, mUsageHint);
-                            } else {
-                                doVibratorSetAmplitude(amplitude);
+                    int index = 0;
+                    long onDuration = 0;
+                    while (!mForceStop) {
+                        if (index < len) {
+                            final int amplitude = amplitudes[index];
+                            final long duration = timings[index++];
+                            if (duration <= 0) {
+                                continue;
                             }
-                        }
+                            if (amplitude != 0) {
+                                if (onDuration <= 0) {
+                                    // Telling the vibrator to start multiple times usually causes
+                                    // effects to feel "choppy" because the motor resets at every on
+                                    // command.  Instead we figure out how long our next "on" period
+                                    // is going to be, tell the motor to stay on for the full
+                                    // duration, and then wake up to change the amplitude at the
+                                    // appropriate intervals.
+                                    onDuration = getTotalOnDuration(timings, amplitudes, index - 1,
+                                            repeat);
+                                    doVibratorOn(onDuration, amplitude, mUid, mUsageHint);
+                                } else {
+                                    doVibratorSetAmplitude(amplitude);
+                                }
+                            }
 
-                        long waitTime = delayLocked(duration);
-                        if (amplitude != 0) {
-                            onDuration -= waitTime;
+                            long waitTime = delayLocked(duration);
+                            if (amplitude != 0) {
+                                onDuration -= waitTime;
+                            }
+                        } else if (repeat < 0) {
+                            break;
+                        } else {
+                            index = repeat;
                         }
-                    } else if (repeat < 0) {
-                        break;
-                    } else {
-                        index = repeat;
                     }
+                    return !mForceStop;
                 }
-                return !mForceStop;
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
             }
         }
 
@@ -1161,35 +1223,40 @@
         }
 
         private int runVibrate() {
+            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runVibrate");
             try {
-                final int zenMode = Settings.Global.getInt(mContext.getContentResolver(),
-                        Settings.Global.ZEN_MODE);
-                if (zenMode != Settings.Global.ZEN_MODE_OFF) {
-                    try (PrintWriter pw = getOutPrintWriter();) {
-                        pw.print("Ignoring because device is on DND mode ");
-                        pw.println(DebugUtils.flagsToString(Settings.Global.class, "ZEN_MODE_",
-                                zenMode));
-                        return 0;
+                try {
+                    final int zenMode = Settings.Global.getInt(mContext.getContentResolver(),
+                            Settings.Global.ZEN_MODE);
+                    if (zenMode != Settings.Global.ZEN_MODE_OFF) {
+                        try (PrintWriter pw = getOutPrintWriter();) {
+                            pw.print("Ignoring because device is on DND mode ");
+                            pw.println(DebugUtils.flagsToString(Settings.Global.class, "ZEN_MODE_",
+                                    zenMode));
+                            return 0;
+                        }
                     }
+                } catch (SettingNotFoundException e) {
+                    // ignore
                 }
-            } catch (SettingNotFoundException e) {
-                // ignore
-            }
 
-            final long duration = Long.parseLong(getNextArgRequired());
-            if (duration > MAX_VIBRATION_MS) {
-                throw new IllegalArgumentException("maximum duration is " + MAX_VIBRATION_MS);
-            }
-            String description = getNextArg();
-            if (description == null) {
-                description = "Shell command";
-            }
+                final long duration = Long.parseLong(getNextArgRequired());
+                if (duration > MAX_VIBRATION_MS) {
+                    throw new IllegalArgumentException("maximum duration is " + MAX_VIBRATION_MS);
+                }
+                String description = getNextArg();
+                if (description == null) {
+                    description = "Shell command";
+                }
 
-            VibrationEffect effect =
-                    VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE);
-            vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
-                    mToken);
-            return 0;
+                VibrationEffect effect =
+                        VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE);
+                vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
+                        mToken);
+                return 0;
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
+            }
         }
 
         @Override