Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5bdcbf94 authored by Ming-Shin Lu's avatar Ming-Shin Lu
Browse files

Add more vibrator traces

for easier measuring Audio-Haptics playback latency from Perfetto

Flag: EXEMPT refactor
Bug: 351968966
Test: play audio-haptic with go/get-audio-haptic-tests and
      dump perfetto trace
Change-Id: Icbbcd4d60207fa08ecd3e5160a07cfdc8b5a6c3d
parent ced9ebc3
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -138,11 +138,14 @@ public class SystemVibratorManager extends VibratorManager {
            Log.w(TAG, "Failed to vibrate; no vibrator manager service.");
            return;
        }
        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason=" + reason);
        try {
            mService.vibrate(uid, mContext.getDeviceId(), opPkg, effect, attributes, reason,
                    mToken);
        } catch (RemoteException e) {
            Log.w(TAG, "Failed to vibrate.", e);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
        }
    }

@@ -152,11 +155,14 @@ public class SystemVibratorManager extends VibratorManager {
            Log.w(TAG, "Failed to perform haptic feedback; no vibrator manager service.");
            return;
        }
        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "performHapticFeedback, reason=" + reason);
        try {
            mService.performHapticFeedback(mUid, mContext.getDeviceId(), mPackageName, constant,
                    reason, flags, privFlags);
        } catch (RemoteException e) {
            Log.w(TAG, "Failed to perform haptic feedback.", e);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
        }
    }

@@ -168,11 +174,15 @@ public class SystemVibratorManager extends VibratorManager {
                            + " no vibrator manager service.");
            return;
        }
        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR,
                "performHapticFeedbackForInputDevice, reason=" + reason);
        try {
            mService.performHapticFeedbackForInputDevice(mUid, mContext.getDeviceId(), mPackageName,
                    constant, inputDeviceId, inputSource, reason, flags, privFlags);
        } catch (RemoteException e) {
            Log.w(TAG, "Failed to perform haptic feedback for input device.", e);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
        }
    }

+12 −7
Original line number Diff line number Diff line
@@ -128,6 +128,8 @@ final class VibrationThread extends Thread {
     *  before the release callback.
     */
    boolean runVibrationOnVibrationThread(VibrationStepConductor conductor) {
        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runVibrationOnVibrationThread");
        try {
            synchronized (mLock) {
                if (mRequestedActiveConductor != null) {
                    Slog.wtf(TAG, "Attempt to start vibration when one already running");
@@ -137,6 +139,9 @@ final class VibrationThread extends Thread {
                mLock.notifyAll();
            }
            return true;
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
        }
    }

    @Override
+123 −70
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.os.IVibratorStateListener;
import android.os.Parcel;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.Trace;
import android.os.VibrationEffect;
import android.os.VibratorInfo;
import android.os.vibrator.PrebakedSegment;
@@ -123,6 +124,8 @@ final class VibratorController {

    /** Reruns the query to the vibrator to load the {@link VibratorInfo}, if not yet successful. */
    public void reloadVibratorInfoIfNeeded() {
        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorController#reloadVibratorInfoIfNeeded");
        try {
            // Early check outside lock, for quick return.
            if (mVibratorInfoLoadSuccessful) {
                return;
@@ -139,6 +142,9 @@ final class VibratorController {
                    Slog.e(TAG, "Failed retry of HAL getInfo for vibrator " + vibratorId);
                }
            }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
        }
    }

    /** Checks if the {@link VibratorInfo} was loaded from the vibrator hardware successfully. */
@@ -193,9 +199,14 @@ final class VibratorController {

    /** Return {@code true} if the underlying vibrator is currently available, false otherwise. */
    public boolean isAvailable() {
        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorController#isAvailable");
        try {
            synchronized (mLock) {
                return mNativeWrapper.isAvailable();
            }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
        }
    }

    /**
@@ -204,6 +215,8 @@ final class VibratorController {
     * <p>This will affect the state of {@link #isUnderExternalControl()}.
     */
    public void setExternalControl(boolean externalControl) {
        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "setExternalControl(" + externalControl + ")");
        try {
            if (!mVibratorInfo.hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
                return;
            }
@@ -211,6 +224,9 @@ final class VibratorController {
                mIsUnderExternalControl = externalControl;
                mNativeWrapper.setExternalControl(externalControl);
            }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
        }
    }

    /**
@@ -218,6 +234,8 @@ final class VibratorController {
     * if given {@code effect} is {@code null}.
     */
    public void updateAlwaysOn(int id, @Nullable PrebakedSegment prebaked) {
        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorController#updateAlwaysOn");
        try {
            if (!mVibratorInfo.hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) {
                return;
            }
@@ -229,10 +247,15 @@ final class VibratorController {
                            prebaked.getEffectStrength());
                }
            }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
        }
    }

    /** Set the vibration amplitude. This will NOT affect the state of {@link #isVibrating()}. */
    public void setAmplitude(float amplitude) {
        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorController#setAmplitude");
        try {
            synchronized (mLock) {
                if (mVibratorInfo.hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) {
                    mNativeWrapper.setAmplitude(amplitude);
@@ -241,6 +264,9 @@ final class VibratorController {
                    mCurrentAmplitude = amplitude;
                }
            }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
        }
    }

    /**
@@ -253,6 +279,8 @@ final class VibratorController {
     * do not support the input or a negative number if the operation failed.
     */
    public long on(long milliseconds, long vibrationId) {
        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorController#on");
        try {
            synchronized (mLock) {
                long duration = mNativeWrapper.on(milliseconds, vibrationId);
                if (duration > 0) {
@@ -261,6 +289,9 @@ final class VibratorController {
                }
                return duration;
            }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
        }
    }

    /**
@@ -273,6 +304,7 @@ final class VibratorController {
     * do not support the input or a negative number if the operation failed.
     */
    public long on(VibrationEffect.VendorEffect vendorEffect, long vibrationId) {
        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorController#on (vendor)");
        synchronized (mLock) {
            Parcel vendorData = Parcel.obtain();
            try {
@@ -288,6 +320,7 @@ final class VibratorController {
                return duration;
            } finally {
                vendorData.recycle();
                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
            }
        }
    }
@@ -302,6 +335,8 @@ final class VibratorController {
     * do not support the input or a negative number if the operation failed.
     */
    public long on(PrebakedSegment prebaked, long vibrationId) {
        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorController#on (Prebaked)");
        try {
            synchronized (mLock) {
                long duration = mNativeWrapper.perform(prebaked.getEffectId(),
                        prebaked.getEffectStrength(), vibrationId);
@@ -311,6 +346,9 @@ final class VibratorController {
                }
                return duration;
            }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
        }
    }

    /**
@@ -323,6 +361,8 @@ final class VibratorController {
     * do not support the input or a negative number if the operation failed.
     */
    public long on(PrimitiveSegment[] primitives, long vibrationId) {
        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorController#on (Primitive)");
        try {
            if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
                return 0;
            }
@@ -334,6 +374,9 @@ final class VibratorController {
                }
                return duration;
            }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
        }
    }

    /**
@@ -345,6 +388,8 @@ final class VibratorController {
     * @return The duration of the effect playing, or 0 if unsupported.
     */
    public long on(RampSegment[] primitives, long vibrationId) {
        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorController#on (PWLE)");
        try {
            if (!mVibratorInfo.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
                return 0;
            }
@@ -357,6 +402,9 @@ final class VibratorController {
                }
                return duration;
            }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
        }
    }

    /**
@@ -365,11 +413,16 @@ final class VibratorController {
     * <p>This will affect the state of {@link #isVibrating()}.
     */
    public void off() {
        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorController#off");
        try {
            synchronized (mLock) {
                mNativeWrapper.off();
                mCurrentAmplitude = 0;
                notifyListenerOnVibrating(false);
            }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
        }
    }

    /**
+275 −235
Original line number Diff line number Diff line
@@ -462,20 +462,31 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
    @Override // Binder call
    public void performHapticFeedback(int uid, int deviceId, String opPkg, int constant,
            String reason, int flags, int privFlags) {
        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "performHapticFeedback");
        // Note that the `performHapticFeedback` method does not take a token argument from the
        // caller, and instead, uses this service as the token. This is to mitigate performance
        // impact that would otherwise be caused due to marshal latency. Haptic feedback effects are
        // short-lived, so we don't need to cancel when the process dies.
        try {
            performHapticFeedbackInternal(uid, deviceId, opPkg, constant, reason, /* token= */
                    this, flags, privFlags);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
        }
    }

    @Override // Binder call
    public void performHapticFeedbackForInputDevice(int uid, int deviceId, String opPkg,
            int constant, int inputDeviceId, int inputSource, String reason, int flags,
            int privFlags) {
        performHapticFeedbackForInputDeviceInternal(uid, deviceId, opPkg, constant, inputDeviceId,
        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "performHapticFeedbackForInputDevice");
        try {
            performHapticFeedbackForInputDeviceInternal(uid, deviceId, opPkg, constant,
                    inputDeviceId,
                    inputSource, reason, /* token= */ this, flags, privFlags);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
        }
    }

    /**
@@ -919,8 +930,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
    @GuardedBy("mLock")
    @Nullable
    private Vibration.EndInfo startVibrationOnThreadLocked(VibrationStepConductor conductor) {
        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationThreadLocked");
        try {
        HalVibration vib = conductor.getVibration();
        int mode = startAppOpModeLocked(vib.callerInfo);
        switch (mode) {
@@ -941,9 +950,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
            default:
                return new Vibration.EndInfo(Status.IGNORED_APP_OPS);
        }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
        }
    }

    @GuardedBy("mLock")
@@ -1050,9 +1056,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {

    @GuardedBy("mLock")
    private void reportFinishedVibrationLocked(Vibration.EndInfo vibrationEndInfo) {
        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
        Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
        try {
        HalVibration vib = mCurrentVibration.getVibration();
        if (DEBUG) {
            Slog.d(TAG, "Reporting vibration " + vib.id + " finished with "
@@ -1062,9 +1066,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
        // vibration was released, after all cleanup. The metrics will be reported then.
        endVibrationLocked(vib, vibrationEndInfo, /* shouldWriteStats= */ false);
        finishAppOpModeLocked(vib.callerInfo);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
        }
    }

    private void onSyncedVibrationComplete(long vibrationId) {
@@ -1418,10 +1419,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {

    @GuardedBy("mLock")
    @Nullable
    private SparseArray<PrebakedSegment> fixupAlwaysOnEffectsLocked(
            CombinedVibration effect) {
        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "fixupAlwaysOnEffectsLocked");
        try {
    private SparseArray<PrebakedSegment> fixupAlwaysOnEffectsLocked(CombinedVibration effect) {
        SparseArray<VibrationEffect> effects;
        if (effect instanceof CombinedVibration.Mono) {
            VibrationEffect syncedEffect = ((CombinedVibration.Mono) effect).getEffect();
@@ -1449,9 +1447,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
            return null;
        }
        return result;
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
        }
    }

    @Nullable
@@ -1580,25 +1575,42 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {

        @Override
        public boolean prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds) {
            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "prepareSyncedVibration");
            try {
                if ((mCapabilities & requiredCapabilities) != requiredCapabilities) {
                // This sync step requires capabilities this device doesn't have, skipping sync...
                    // This sync step requires capabilities this device doesn't have, skipping
                    // sync...
                    return false;
                }
                return mNativeWrapper.prepareSynced(vibratorIds);
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
            }
        }

        @Override
        public boolean triggerSyncedVibration(long vibrationId) {
            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "triggerSyncedVibration");
            try {
                return mNativeWrapper.triggerSynced(vibrationId);
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
            }
        }

        @Override
        public void cancelSyncedVibration() {
            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "cancelSyncedVibration");
            try {
                mNativeWrapper.cancelSynced();
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
            }
        }

        @Override
        public void noteVibratorOn(int uid, long duration) {
            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "noteVibratorOn");
            try {
                if (duration <= 0) {
                    // Tried to turn vibrator ON and got:
@@ -1616,16 +1628,21 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                mFrameworkStatsLogger.writeVibratorStateOnAsync(uid, duration);
            } catch (RemoteException e) {
                Slog.e(TAG, "Error logging VibratorStateChanged to ON", e);
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
            }
        }

        @Override
        public void noteVibratorOff(int uid) {
            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "noteVibratorOff");
            try {
                mBatteryStatsService.noteVibratorOff(uid);
                mFrameworkStatsLogger.writeVibratorStateOffAsync(uid);
            } catch (RemoteException e) {
                Slog.e(TAG, "Error logging VibratorStateChanged to OFF", e);
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
            }
        }

@@ -1634,12 +1651,17 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
            if (DEBUG) {
                Slog.d(TAG, "Vibration " + vibrationId + " finished with " + vibrationEndInfo);
            }
            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "onVibrationCompleted");
            try {
                synchronized (mLock) {
                    if (mCurrentVibration != null
                            && mCurrentVibration.getVibration().id == vibrationId) {
                        reportFinishedVibrationLocked(vibrationEndInfo);
                    }
                }
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
            }
        }

        @Override
@@ -1647,6 +1669,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
            if (DEBUG) {
                Slog.d(TAG, "VibrationThread released after finished vibration");
            }
            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "onVibrationThreadReleased: " + vibrationId);
            try {
                synchronized (mLock) {
                    if (DEBUG) {
                        Slog.d(TAG, "Processing VibrationThread released callback");
@@ -1658,7 +1682,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                                mCurrentVibration.getVibration().id, vibrationId));
                    }
                    if (mCurrentVibration != null) {
                    // This is when we consider the current vibration complete, so report metrics.
                        // This is when we consider the current vibration complete, so report
                        // metrics.
                        mFrameworkStatsLogger.writeVibrationReportedAsync(
                                mCurrentVibration.getVibration().getStatsInfo(
                                        /* completionUptimeMillis= */ SystemClock.uptimeMillis()));
@@ -1676,6 +1701,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                        }
                    }
                }
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
            }
        }
    }

@@ -1917,8 +1945,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
    @GuardedBy("mLock")
    private void endExternalVibrateLocked(Vibration.EndInfo vibrationEndInfo,
            boolean continueExternalControl) {
        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "endExternalVibrateLocked");
        try {
        if (mCurrentExternalVibration == null) {
            return;
        }
@@ -1930,9 +1956,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
        endVibrationLocked(mCurrentExternalVibration, vibrationEndInfo,
                /* shouldWriteStats= */ true);
        mCurrentExternalVibration = null;
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
        }
    }

    private HapticFeedbackVibrationProvider getHapticVibrationProvider() {
@@ -1987,7 +2010,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {

        @Override
        public ExternalVibrationScale onExternalVibrationStart(ExternalVibration vib) {
            // Create Vibration.Stats as close to the received request as possible, for tracking.
            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "onExternalVibrationStart");
            try {
                // Create Vibration.Stats as close to the received request as possible, for
                // tracking.
                ExternalVibrationSession externalVibration = new ExternalVibrationSession(vib);
                // Mute the request until we run all the checks and accept the vibration.
                externalVibration.muteScale();
@@ -2002,7 +2028,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                        return externalVibration.getScale();
                    }

                if (ActivityManager.checkComponentPermission(android.Manifest.permission.VIBRATE,
                    if (ActivityManager.checkComponentPermission(
                            android.Manifest.permission.VIBRATE,
                            vib.getUid(), -1 /*owningUid*/, true /*exported*/)
                            != PackageManager.PERMISSION_GRANTED) {
                        Slog.w(TAG, "pkg=" + vib.getPackage() + ", uid=" + vib.getUid()
@@ -2038,7 +2065,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {

                    if (mCurrentExternalVibration == null) {
                        // If we're not under external control right now, then cancel any normal
                    // vibration that may be playing and ready the vibrator for external control.
                        // vibration that may be playing and ready the vibrator for external
                        // control.
                        if (mCurrentVibration != null) {
                            externalVibration.stats.reportInterruptedAnotherVibration(
                                    mCurrentVibration.getVibration().callerInfo);
@@ -2053,13 +2081,16 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                        }
                    } else {
                        // At this point we have an externally controlled vibration playing already.
                    // Since the interface defines that only one externally controlled vibration can
                    // play at a time, we need to first mute the ongoing vibration and then return
                        // Since the interface defines that only one externally controlled
                        // vibration can
                        // play at a time, we need to first mute the ongoing vibration and then
                        // return
                        // a scale from this function for the new one, so we can be assured that the
                        // ongoing will be muted in favor of the new vibration.
                        //
                    // Note that this doesn't support multiple concurrent external controls, as we
                    // would need to mute the old one still if it came from a different controller.
                        // Note that this doesn't support multiple concurrent external controls,
                        // as we would need to mute the old one still if it came from a different
                        // controller.
                        alreadyUnderExternalControl = true;
                        mCurrentExternalVibration.notifyEnded();
                        externalVibration.stats.reportInterruptedAnotherVibration(
@@ -2070,11 +2101,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                                /* continueExternalControl= */ true);
                    }

                VibrationAttributes attrs = fixupVibrationAttributes(vib.getVibrationAttributes(),
                    VibrationAttributes attrs = fixupVibrationAttributes(
                            vib.getVibrationAttributes(),
                            /* effect= */ null);
                    if (attrs.isFlagSet(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) {
                    // Force update of user settings before checking if this vibration effect should
                    // be ignored or scaled.
                        // Force update of user settings before checking if this vibration effect
                        // should be ignored or scaled.
                        mVibrationSettings.update();
                    }

@@ -2110,10 +2142,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                // Report current time as the vibration start time, for debugging.
                externalVibration.stats.reportStarted();
                return externalVibration.getScale();
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
            }
        }

        @Override
        public void onExternalVibrationStop(ExternalVibration vib) {
            Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "onExternalVibrationStop");
            try {
                synchronized (mLock) {
                    if (mCurrentExternalVibration != null
                            && mCurrentExternalVibration.isHoldingSameVibration(vib)) {
@@ -2125,6 +2162,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
                                /* continueExternalControl= */ false);
                    }
                }
            } finally {
                Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
            }
        }

        private boolean hasExternalControlCapability() {