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

Commit 77007f2a authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Extract battery saver and uid checks to VibrationSettings"

parents 27049987 ee962900
Loading
Loading
Loading
Loading
+2 −37
Original line number Diff line number Diff line
@@ -27,9 +27,6 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.IVibratorManagerService;
import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.ShellCommand;
@@ -98,8 +95,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {

    private VibrationSettings mVibrationSettings;
    private VibrationScaler mVibrationScaler;
    @GuardedBy("mLock")
    private boolean mLowPowerMode;

    static native long nativeInit();

@@ -142,23 +137,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {

            mVibrationSettings.addListener(this::updateServiceState);

            PowerManagerInternal pm = LocalServices.getService(PowerManagerInternal.class);
            pm.registerLowPowerModeObserver(
                    new PowerManagerInternal.LowPowerModeListener() {
                        @Override
                        public int getServiceType() {
                            return PowerManager.ServiceType.VIBRATION;
                        }

                        @Override
                        public void onLowPowerModeChanged(PowerSaveState result) {
                            synchronized (mLock) {
                                mLowPowerMode = result.batterySaverEnabled;
                            }
                            updateServiceState();
                        }
                    });

            updateServiceState();
        } finally {
            Slog.v(TAG, "VibratorManager service initialized");
@@ -277,7 +255,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
    @Nullable
    private Vibration.Status shouldIgnoreVibrationLocked(int uid, String opPkg,
            VibrationAttributes attrs) {
        if (!shouldVibrateForPowerModeLocked(attrs)) {
        if (!mVibrationSettings.shouldVibrateForPowerMode(attrs.getUsage())) {
            return Vibration.Status.IGNORED_FOR_POWER;
        }

@@ -286,8 +264,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
            return Vibration.Status.IGNORED_FOR_SETTINGS;
        }

        if (attrs.getUsage() == VibrationAttributes.USAGE_RINGTONE
                && !mVibrationSettings.shouldVibrateForRingtone()) {
        if (!mVibrationSettings.shouldVibrateForRingerMode(attrs.getUsage())) {
            if (DEBUG) {
                Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones");
            }
@@ -309,18 +286,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
        return null;
    }

    /** Return true is current power mode allows this vibration to happen. */
    @GuardedBy("mLock")
    private boolean shouldVibrateForPowerModeLocked(VibrationAttributes attrs) {
        if (!mLowPowerMode) {
            return true;
        }
        int usage = attrs.getUsage();
        return usage == VibrationAttributes.USAGE_RINGTONE
                || usage == VibrationAttributes.USAGE_ALARM
                || usage == VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
    }

    /**
     * Check which mode should be set for a vibration with given {@code uid}, {@code opPkg} and
     * {@code attrs}. This will return one of the AppOpsManager.MODE_*.
+24 −117
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ package com.android.server;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.IUidObserver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -37,9 +36,6 @@ import android.os.IVibratorService;
import android.os.IVibratorStateListener;
import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerManager.ServiceType;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -54,7 +50,6 @@ import android.os.Vibrator;
import android.os.VibratorInfo;
import android.os.WorkSource;
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;

import com.android.internal.annotations.GuardedBy;
@@ -95,7 +90,6 @@ public class VibratorService extends IVibratorService.Stub {
    private final LinkedList<Vibration.DebugInfo> mPreviousExternalVibrations;
    private final LinkedList<Vibration.DebugInfo> mPreviousVibrations;
    private final int mPreviousVibrationsLimit;
    private final SparseArray<Integer> mProcStatesCache = new SparseArray<>();
    private final WorkSource mTmpWorkSource = new WorkSource();
    private final Handler mH;
    private final Object mLock = new Object();
@@ -106,7 +100,6 @@ public class VibratorService extends IVibratorService.Stub {
    private final AppOpsManager mAppOps;
    private final IBatteryStats mBatteryStatsService;
    private final String mSystemUiPackage;
    private PowerManagerInternal mPowerManagerInternal;
    private VibrationSettings mVibrationSettings;
    private VibrationScaler mVibrationScaler;
    private InputDeviceDelegate mInputDeviceDelegate;
@@ -119,30 +112,6 @@ public class VibratorService extends IVibratorService.Stub {
    private VibrationDeathRecipient mCurrentVibrationDeathRecipient;
    private int mCurVibUid = -1;
    private ExternalVibrationHolder mCurrentExternalVibration;
    private boolean mLowPowerMode;

    private final IUidObserver mUidObserver = new IUidObserver.Stub() {
        @Override public void onUidStateChanged(int uid, int procState, long procStateSeq,
                int capability) {
            mProcStatesCache.put(uid, procState);
        }

        @Override public void onUidGone(int uid, boolean disabled) {
            mProcStatesCache.delete(uid);
        }

        @Override
        public void onUidActive(int uid) {
        }

        @Override
        public void onUidIdle(int uid, boolean disabled) {
        }

        @Override
        public void onUidCachedChanged(int uid, boolean cached) {
        }
    };

    /**
     * Implementation of {@link OnVibrationCompleteListener} with a weak reference to this service.
@@ -276,37 +245,15 @@ public class VibratorService extends IVibratorService.Stub {
            mVibrationScaler = new VibrationScaler(mContext, mVibrationSettings);
            mInputDeviceDelegate = new InputDeviceDelegate(mContext, mH);

            mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
            mPowerManagerInternal.registerLowPowerModeObserver(
                    new PowerManagerInternal.LowPowerModeListener() {
                        @Override
                        public int getServiceType() {
                            return ServiceType.VIBRATION;
                        }

                        @Override
                        public void onLowPowerModeChanged(PowerSaveState result) {
                            updateVibrators();
                        }
            });

            mVibrationSettings.addListener(this::updateVibrators);

            mContext.registerReceiver(new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    updateVibrators();
                    mVibrationSettings.updateSettings();
                }
            }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mH);

            try {
                ActivityManager.getService().registerUidObserver(mUidObserver,
                        ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE,
                        ActivityManager.PROCESS_STATE_UNKNOWN, null);
            } catch (RemoteException e) {
                // ignored; both services live in system_server
            }

            updateVibrators();
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
@@ -492,9 +439,7 @@ public class VibratorService extends IVibratorService.Stub {
                    return;
                }

                if (mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)
                        > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
                        && !isNotification(vib) && !isRingtone(vib) && !isAlarm(vib)) {
                if (!mVibrationSettings.shouldVibrateForUid(uid, vib.attrs.getUsage())) {
                    Slog.e(TAG, "Ignoring incoming vibration as process with"
                            + " uid= " + uid + " is background,"
                            + " attrs= " + vib.attrs);
@@ -531,16 +476,19 @@ public class VibratorService extends IVibratorService.Stub {

    private void endVibrationLocked(Vibration vib, Vibration.Status status) {
        final LinkedList<Vibration.DebugInfo> previousVibrations;
        if (isRingtone(vib)) {
            previousVibrations = mPreviousRingVibrations;
        } else if (isNotification(vib)) {
        switch (vib.attrs.getUsage()) {
            case VibrationAttributes.USAGE_NOTIFICATION:
                previousVibrations = mPreviousNotificationVibrations;
        } else if (isAlarm(vib)) {
                break;
            case VibrationAttributes.USAGE_RINGTONE:
                previousVibrations = mPreviousRingVibrations;
                break;
            case VibrationAttributes.USAGE_ALARM:
                previousVibrations = mPreviousAlarmVibrations;
        } else {
                break;
            default:
                previousVibrations = mPreviousVibrations;
        }

        if (previousVibrations.size() > mPreviousVibrationsLimit) {
            previousVibrations.removeFirst();
        }
@@ -655,17 +603,6 @@ public class VibratorService extends IVibratorService.Stub {
        }
    }

    private boolean shouldVibrateForPowerModeLocked(Vibration vib) {
        if (!mLowPowerMode) {
            return true;
        }

        int usage = vib.attrs.getUsage();
        return usage == VibrationAttributes.USAGE_RINGTONE
                || usage == VibrationAttributes.USAGE_ALARM
                || usage == VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
    }

    /** Scale the vibration effect by the intensity as appropriate based its intent. */
    private void applyVibrationIntensityScalingLocked(Vibration vib) {
        vib.updateEffect(mVibrationScaler.scale(vib.getEffect(), vib.attrs.getUsage()));
@@ -693,7 +630,7 @@ public class VibratorService extends IVibratorService.Stub {
    }

    private boolean shouldVibrate(Vibration vib) {
        if (!shouldVibrateForPowerModeLocked(vib)) {
        if (!mVibrationSettings.shouldVibrateForPowerMode(vib.attrs.getUsage())) {
            endVibrationLocked(vib, Vibration.Status.IGNORED_FOR_POWER);
            return false;
        }
@@ -704,7 +641,7 @@ public class VibratorService extends IVibratorService.Stub {
            return false;
        }

        if (isRingtone(vib) && !mVibrationSettings.shouldVibrateForRingtone()) {
        if (!mVibrationSettings.shouldVibrateForRingerMode(vib.attrs.getUsage())) {
            if (DEBUG) {
                Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones");
            }
@@ -770,26 +707,13 @@ public class VibratorService extends IVibratorService.Stub {

    private void updateVibrators() {
        synchronized (mLock) {
            boolean devicesUpdated = mInputDeviceDelegate.updateInputDeviceVibrators(
            mInputDeviceDelegate.updateInputDeviceVibrators(
                    mVibrationSettings.shouldVibrateInputDevices());
            boolean lowPowerModeUpdated = updateLowPowerModeLocked();

            if (devicesUpdated || lowPowerModeUpdated) {
            // If the state changes out from under us then just reset.
            doCancelVibrateLocked(Vibration.Status.CANCELLED);
        }
    }
    }

    private boolean updateLowPowerModeLocked() {
        boolean lowPowerMode = mPowerManagerInternal
                .getLowPowerState(ServiceType.VIBRATION).batterySaverEnabled;
        if (lowPowerMode != mLowPowerMode) {
            mLowPowerMode = lowPowerMode;
            return true;
        }
        return false;
    }

    private void doVibratorOn(Vibration vib) {
        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn");
@@ -923,23 +847,10 @@ public class VibratorService extends IVibratorService.Stub {

    }

    private static boolean isNotification(Vibration vib) {
        return vib.attrs.getUsage() == VibrationAttributes.USAGE_NOTIFICATION;
    }

    private static boolean isRingtone(Vibration vib) {
        return vib.attrs.getUsage() == VibrationAttributes.USAGE_RINGTONE;
    }

    private static boolean isHapticFeedback(Vibration vib) {
        return vib.attrs.getUsage() == VibrationAttributes.USAGE_TOUCH;
    }

    private static boolean isAlarm(Vibration vib) {
        return vib.attrs.getUsage() == VibrationAttributes.USAGE_ALARM;
    private boolean isSystemHapticFeedback(Vibration vib) {
        if (vib.attrs.getUsage() != VibrationAttributes.USAGE_TOUCH) {
            return false;
        }

    private boolean isFromSystem(Vibration vib) {
        return vib.uid == Process.SYSTEM_UID || vib.uid == 0 || mSystemUiPackage.equals(vib.opPkg);
    }

@@ -979,7 +890,6 @@ public class VibratorService extends IVibratorService.Stub {
            } else {
                pw.println("null");
            }
            pw.println("  mLowPowerMode=" + mLowPowerMode);
            pw.println("  mVibratorController=" + mVibratorController);
            pw.println("  mVibrationSettings=" + mVibrationSettings);
            pw.println();
@@ -1026,7 +936,6 @@ public class VibratorService extends IVibratorService.Stub {
            proto.write(VibratorServiceDumpProto.IS_VIBRATING, mVibratorController.isVibrating());
            proto.write(VibratorServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL,
                    mVibratorController.isUnderExternalControl());
            proto.write(VibratorServiceDumpProto.LOW_POWER_MODE, mLowPowerMode);
            proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_INTENSITY,
                    mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_TOUCH));
            proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_DEFAULT_INTENSITY,
@@ -1240,9 +1149,7 @@ public class VibratorService extends IVibratorService.Stub {
                    // them.  However it may happen that the system is currently playing
                    // haptic feedback as part of the transition.  So we don't cancel
                    // system vibrations.
                    if (mCurrentVibration != null
                            && !(isHapticFeedback(mCurrentVibration)
                                && isFromSystem(mCurrentVibration))) {
                    if (mCurrentVibration != null && !isSystemHapticFeedback(mCurrentVibration)) {
                        doCancelVibrateLocked(Vibration.Status.CANCELLED);
                    }
                }
+114 −12
Original line number Diff line number Diff line
@@ -16,12 +16,18 @@

package com.android.server.vibrator;

import android.app.ActivityManager;
import android.app.IUidObserver;
import android.content.Context;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Handler;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
@@ -30,7 +36,7 @@ import android.provider.Settings;
import android.util.SparseArray;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;

import java.util.ArrayList;
import java.util.List;
@@ -53,6 +59,7 @@ public final class VibrationSettings {
    private final Vibrator mVibrator;
    private final AudioManager mAudioManager;
    private final SettingsObserver mSettingObserver;
    private final UidObserver mUidObserver;

    @GuardedBy("mLock")
    private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>();
@@ -72,12 +79,15 @@ public final class VibrationSettings {
    private int mNotificationIntensity;
    @GuardedBy("mLock")
    private int mRingIntensity;
    @GuardedBy("mLock")
    private boolean mLowPowerMode;

    public VibrationSettings(Context context, Handler handler) {
        mContext = context;
        mVibrator = context.getSystemService(Vibrator.class);
        mAudioManager = context.getSystemService(AudioManager.class);
        mSettingObserver = new SettingsObserver(handler);
        mUidObserver = new UidObserver();

        registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES));
        registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING));
@@ -107,6 +117,35 @@ public final class VibrationSettings {
        mFallbackEffects.put(VibrationEffect.EFFECT_TEXTURE_TICK,
                VibrationEffect.get(VibrationEffect.EFFECT_TICK, false));

        try {
            ActivityManager.getService().registerUidObserver(mUidObserver,
                    ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE,
                    ActivityManager.PROCESS_STATE_UNKNOWN, null);
        } catch (RemoteException e) {
            // ignored; both services live in system_server
        }

        PowerManagerInternal pm = LocalServices.getService(PowerManagerInternal.class);
        pm.registerLowPowerModeObserver(
                new PowerManagerInternal.LowPowerModeListener() {
                    @Override
                    public int getServiceType() {
                        return PowerManager.ServiceType.VIBRATION;
                    }

                    @Override
                    public void onLowPowerModeChanged(PowerSaveState result) {
                        boolean shouldNotifyListeners;
                        synchronized (mLock) {
                            shouldNotifyListeners = result.batterySaverEnabled != mLowPowerMode;
                            mLowPowerMode = result.batterySaverEnabled;
                        }
                        if (shouldNotifyListeners) {
                            notifyListeners();
                        }
                    }
                });

        // Update with current values from settings.
        updateSettings();
    }
@@ -184,12 +223,15 @@ public final class VibrationSettings {
    }

    /**
     * Return {@code true} if the device should vibrate for ringtones.
     * Return {@code true} if the device should vibrate for current ringer mode.
     *
     * <p>This checks the current {@link AudioManager#getRingerModeInternal()} against user settings
     * for vibrations while ringing.
     * for ringtone usage only. All other usages are allowed independently of ringer mode.
     */
    public boolean shouldVibrateForRingtone() {
    public boolean shouldVibrateForRingerMode(int usageHint) {
        if (!isRingtone(usageHint)) {
            return true;
        }
        int ringerMode = mAudioManager.getRingerModeInternal();
        synchronized (mLock) {
            if (mVibrateWhenRinging) {
@@ -202,6 +244,28 @@ public final class VibrationSettings {
        }
    }

    /**
     * Returns {@code true} if this vibration is allowed for given {@code uid}.
     *
     * <p>This checks if the user is aware of this foreground process, or if the vibration usage is
     * allowed to play in the background (i.e. it's a notification, ringtone or alarm vibration).
     */
    public boolean shouldVibrateForUid(int uid, int usageHint) {
        return mUidObserver.isUidForeground(uid) || isNotification(usageHint)
                || isRingtone(usageHint) || isAlarm(usageHint);
    }

    /**
     * Returns {@code true} if this vibration is allowed for current power mode state.
     *
     * <p>This checks if the device is in battery saver mode, in which case only alarm, ringtone and
     * {@link VibrationAttributes#USAGE_COMMUNICATION_REQUEST} usages are allowed to vibrate.
     */
    public boolean shouldVibrateForPowerMode(int usageHint) {
        return !mLowPowerMode || isRingtone(usageHint) || isAlarm(usageHint)
                || usageHint == VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
    }

    /** Return {@code true} if input devices should vibrate instead of this device. */
    public boolean shouldVibrateInputDevices() {
        return mVibrateInputDevices;
@@ -229,9 +293,7 @@ public final class VibrationSettings {
    }

    /** Updates all vibration settings and triggers registered listeners. */
    @VisibleForTesting
    public void updateSettings() {
        List<OnVibratorSettingsChanged> currentListeners;
        synchronized (mLock) {
            mVibrateWhenRinging = getSystemSetting(Settings.System.VIBRATE_WHEN_RINGING, 0) != 0;
            mApplyRampingRinger = getGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0) != 0;
@@ -244,12 +306,8 @@ public final class VibrationSettings {
                    mVibrator.getDefaultRingVibrationIntensity());
            mVibrateInputDevices = getSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0) > 0;
            mZenMode = getGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
            currentListeners = new ArrayList<>(mListeners);
        }

        for (OnVibratorSettingsChanged listener : currentListeners) {
            listener.onChange();
        }
        notifyListeners();
    }

    @Override
@@ -258,7 +316,9 @@ public final class VibrationSettings {
                + "mVibrateInputDevices=" + mVibrateInputDevices
                + ", mVibrateWhenRinging=" + mVibrateWhenRinging
                + ", mApplyRampingRinger=" + mApplyRampingRinger
                + ", mLowPowerMode=" + mLowPowerMode
                + ", mZenMode=" + Settings.Global.zenModeToString(mZenMode)
                + ", mProcStatesCache=" + mUidObserver.mProcStatesCache
                + ", mHapticFeedbackIntensity="
                + intensityToString(getCurrentIntensity(VibrationAttributes.USAGE_TOUCH))
                + ", mHapticFeedbackDefaultIntensity="
@@ -274,6 +334,16 @@ public final class VibrationSettings {
                + '}';
    }

    private void notifyListeners() {
        List<OnVibratorSettingsChanged> currentListeners;
        synchronized (mLock) {
            currentListeners = new ArrayList<>(mListeners);
        }
        for (OnVibratorSettingsChanged listener : currentListeners) {
            listener.onChange();
        }
    }

    private static String intensityToString(int intensity) {
        switch (intensity) {
            case Vibrator.VIBRATION_INTENSITY_OFF:
@@ -342,4 +412,36 @@ public final class VibrationSettings {
            updateSettings();
        }
    }

    /** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */
    private final class UidObserver extends IUidObserver.Stub {
        private final SparseArray<Integer> mProcStatesCache = new SparseArray<>();

        public boolean isUidForeground(int uid) {
            return mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)
                    <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
        }

        @Override
        public void onUidGone(int uid, boolean disabled) {
            mProcStatesCache.delete(uid);
        }

        @Override
        public void onUidActive(int uid) {
        }

        @Override
        public void onUidIdle(int uid, boolean disabled) {
        }

        @Override
        public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
            mProcStatesCache.put(uid, procState);
        }

        @Override
        public void onUidCachedChanged(int uid, boolean cached) {
        }
    }
}
+27 −5

File changed.

Preview size limit exceeded, changes collapsed.

+12 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.os.Handler;
import android.os.IExternalVibratorService;
import android.os.PowerManagerInternal;
import android.os.UserHandle;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
@@ -40,7 +41,9 @@ import androidx.test.InstrumentationRegistry;

import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
import com.android.server.LocalServices;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -62,6 +65,7 @@ public class VibrationScalerTest {

    // TODO(b/131311651): replace with a FakeVibrator instead.
    @Mock private Vibrator mVibratorMock;
    @Mock private PowerManagerInternal mPowerManagerInternalMock;

    private TestLooper mTestLooper;
    private ContextWrapper mContextSpy;
@@ -77,11 +81,19 @@ public class VibrationScalerTest {
        when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
        when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mVibratorMock);

        LocalServices.removeServiceForTest(PowerManagerInternal.class);
        LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock);

        mVibrationSettings = new VibrationSettings(
                mContextSpy, new Handler(mTestLooper.getLooper()));
        mVibrationScaler = new VibrationScaler(mContextSpy, mVibrationSettings);
    }

    @After
    public void tearDown() throws Exception {
        LocalServices.removeServiceForTest(PowerManagerInternal.class);
    }

    @Test
    public void testGetExternalVibrationScale() {
        when(mVibratorMock.getDefaultHapticFeedbackIntensity())
Loading