Extract vibration settings and scaling code from VibratorService

Move the existing logic around vibration settings and ContentObserver to
a separate class, that will be used by VibratiorManagerService once
migration is complete.

Also extract the vibration scaling logic to a separate controller, which
will also be used by the VibratorManagerService after migration.

Bug: 167946816
Bug: 131311651
Test: atest FrameworksServiceTests:VibratorServiceTest
      atest FrameworksServiceTests:VibrationSettingsTest
      atest FrameworksServiceTests:VibrationScaleTest
Change-Id: Idf081e662aa218e93a38cd3262a1357e2e3faf0d
This commit is contained in:
Lais Andrade
2020-10-21 11:33:08 +00:00
parent a2a253995d
commit 6b83aec26f
9 changed files with 1166 additions and 281 deletions

View File

@@ -413,7 +413,7 @@ public abstract class VibrationEffect implements Parcelable {
*
* @hide
*/
public abstract VibrationEffect resolve(int defaultAmplitude);
public abstract <T extends VibrationEffect> T resolve(int defaultAmplitude);
/**
* Scale the vibration effect intensity with the given constraints.
@@ -425,7 +425,7 @@ public abstract class VibrationEffect implements Parcelable {
*
* @hide
*/
public abstract VibrationEffect scale(float scaleFactor);
public abstract <T extends VibrationEffect> T scale(float scaleFactor);
/**
* Scale given vibration intensity by the given factor.

View File

@@ -29,11 +29,8 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.input.InputManager;
import android.hardware.vibrator.IVibrator;
import android.hardware.vibrator.V1_0.EffectStrength;
import android.media.AudioManager;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.ExternalVibration;
@@ -56,14 +53,10 @@ import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.WorkSource;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.DebugUtils;
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
@@ -74,6 +67,8 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.vibrator.VibrationScaler;
import com.android.server.vibrator.VibrationSettings;
import libcore.util.NativeAllocationRegistry;
@@ -97,22 +92,6 @@ public class VibratorService extends IVibratorService.Stub
private static final long[] DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS = { 0, 30, 100, 30 };
// Scale levels. Each level, except MUTE, is defined as the delta between the current setting
// and the default intensity for that type of vibration (i.e. current - default).
private static final int SCALE_MUTE = IExternalVibratorService.SCALE_MUTE; // -100
private static final int SCALE_VERY_LOW = IExternalVibratorService.SCALE_VERY_LOW; // -2
private static final int SCALE_LOW = IExternalVibratorService.SCALE_LOW; // -1
private static final int SCALE_NONE = IExternalVibratorService.SCALE_NONE; // 0
private static final int SCALE_HIGH = IExternalVibratorService.SCALE_HIGH; // 1
private static final int SCALE_VERY_HIGH = IExternalVibratorService.SCALE_VERY_HIGH; // 2
// Scale factors for each level.
private static final float SCALE_FACTOR_VERY_LOW = 0.6f;
private static final float SCALE_FACTOR_LOW = 0.8f;
private static final float SCALE_FACTOR_NONE = 1f;
private static final float SCALE_FACTOR_HIGH = 1.2f;
private static final float SCALE_FACTOR_VERY_HIGH = 1.4f;
// Default vibration attributes. Used when vibration is requested without attributes
private static final VibrationAttributes DEFAULT_ATTRIBUTES =
new VibrationAttributes.Builder().build();
@@ -120,11 +99,6 @@ public class VibratorService extends IVibratorService.Stub
// Used to generate globally unique vibration ids.
private final AtomicInteger mNextVibrationId = new AtomicInteger(1); // 0 = no callback
// A mapping from the intensity adjustment to the scaling to apply, where the intensity
// adjustment is defined as the delta between the default intensity level and the user selected
// intensity level. It's important that we apply the scaling on the delta between the two so
// that the default intensity level applies no scaling to application provided effects.
private final SparseArray<ScaleLevel> mScaleLevels;
private final LinkedList<VibrationInfo> mPreviousRingVibrations;
private final LinkedList<VibrationInfo> mPreviousNotificationVibrations;
private final LinkedList<VibrationInfo> mPreviousAlarmVibrations;
@@ -149,8 +123,8 @@ public class VibratorService extends IVibratorService.Stub
private final String mSystemUiPackage;
private PowerManagerInternal mPowerManagerInternal;
private InputManager mIm;
private Vibrator mVibrator;
private SettingsObserver mSettingObserver;
private VibrationSettings mVibrationSettings;
private VibrationScaler mVibrationScaler;
private final NativeWrapper mNativeWrapper;
private volatile VibrateWaveformThread mThread;
@@ -172,9 +146,6 @@ public class VibratorService extends IVibratorService.Stub
@GuardedBy("mLock")
private final RemoteCallbackList<IVibratorStateListener> mVibratorStateListeners =
new RemoteCallbackList<>();
private int mHapticFeedbackIntensity;
private int mNotificationIntensity;
private int mRingIntensity;
private SparseArray<Vibration> mAlwaysOnEffects = new SparseArray<>();
static native long vibratorInit(OnCompleteListener listener);
@@ -343,7 +314,7 @@ public class VibratorService extends IVibratorService.Stub
private ExternalVibrationHolder(ExternalVibration externalVibration) {
this.externalVibration = externalVibration;
this.scale = SCALE_NONE;
this.scale = IExternalVibratorService.SCALE_NONE;
mStartTimeDebug = System.currentTimeMillis();
mStatus = VibrationInfo.Status.RUNNING;
}
@@ -518,20 +489,6 @@ public class VibratorService extends IVibratorService.Stub
}
}
/** Represents the scale that must be applied to a vibration effect intensity. */
private static final class ScaleLevel {
public final float factor;
ScaleLevel(float factor) {
this.factor = factor;
}
@Override
public String toString() {
return "ScaleLevel{factor=" + factor + "}";
}
}
VibratorService(Context context) {
this(context, new Injector());
}
@@ -599,13 +556,6 @@ public class VibratorService extends IVibratorService.Stub
mFallbackEffects.put(VibrationEffect.EFFECT_TEXTURE_TICK,
VibrationEffect.get(VibrationEffect.EFFECT_TICK, false));
mScaleLevels = new SparseArray<>();
mScaleLevels.put(SCALE_VERY_LOW, new ScaleLevel(SCALE_FACTOR_VERY_LOW));
mScaleLevels.put(SCALE_LOW, new ScaleLevel(SCALE_FACTOR_LOW));
mScaleLevels.put(SCALE_NONE, new ScaleLevel(SCALE_FACTOR_NONE));
mScaleLevels.put(SCALE_HIGH, new ScaleLevel(SCALE_FACTOR_HIGH));
mScaleLevels.put(SCALE_VERY_HIGH, new ScaleLevel(SCALE_FACTOR_VERY_HIGH));
injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService());
}
@@ -628,8 +578,8 @@ public class VibratorService extends IVibratorService.Stub
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorService#systemReady");
try {
mIm = mContext.getSystemService(InputManager.class);
mVibrator = mContext.getSystemService(Vibrator.class);
mSettingObserver = new SettingsObserver(mH);
mVibrationSettings = new VibrationSettings(mContext, mH);
mVibrationScaler = new VibrationScaler(mContext, mVibrationSettings);
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
mPowerManagerInternal.registerLowPowerModeObserver(
@@ -645,25 +595,7 @@ public class VibratorService extends IVibratorService.Stub
}
});
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.NOTIFICATION_VIBRATION_INTENSITY),
true, mSettingObserver, UserHandle.USER_ALL);
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(Settings.System.RING_VIBRATION_INTENSITY),
true, mSettingObserver, UserHandle.USER_ALL);
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.ZEN_MODE),
true, mSettingObserver, UserHandle.USER_ALL);
mVibrationSettings.addListener(this::updateVibrators);
mContext.registerReceiver(new BroadcastReceiver() {
@Override
@@ -686,17 +618,6 @@ public class VibratorService extends IVibratorService.Stub
}
}
private final class SettingsObserver extends ContentObserver {
public SettingsObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean SelfChange) {
updateVibrators();
}
}
/** Callback for when vibration is complete, to be called by native. */
@VisibleForTesting
public void onVibrationComplete(long vibrationId) {
@@ -1118,11 +1039,10 @@ public class VibratorService extends IVibratorService.Stub
private void startVibrationLocked(final Vibration vib) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked");
try {
final int intensity = getCurrentIntensityLocked(vib.attrs.getUsage());
if (!shouldVibrate(vib, intensity)) {
if (!shouldVibrate(vib)) {
return;
}
applyVibrationIntensityScalingLocked(vib, intensity);
applyVibrationIntensityScalingLocked(vib);
startVibrationInnerLocked(vib);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
@@ -1171,73 +1091,12 @@ public class VibratorService extends IVibratorService.Stub
|| usage == VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
}
private int getCurrentIntensityLocked(int usageHint) {
if (isRingtone(usageHint)) {
return mRingIntensity;
} else if (isNotification(usageHint)) {
return mNotificationIntensity;
} else if (isHapticFeedback(usageHint)) {
return mHapticFeedbackIntensity;
} else if (isAlarm(usageHint)) {
return Vibrator.VIBRATION_INTENSITY_HIGH;
} else {
return Vibrator.VIBRATION_INTENSITY_MEDIUM;
}
}
private int getDefaultIntensity(int usageHint) {
if (isRingtone(usageHint)) {
return mVibrator.getDefaultRingVibrationIntensity();
} else if (isNotification(usageHint)) {
return mVibrator.getDefaultNotificationVibrationIntensity();
} else if (isHapticFeedback(usageHint)) {
return mVibrator.getDefaultHapticFeedbackIntensity();
} else if (isAlarm(usageHint)) {
return Vibrator.VIBRATION_INTENSITY_HIGH;
} else {
return Vibrator.VIBRATION_INTENSITY_MEDIUM;
}
}
/**
* Scale the vibration effect by the intensity as appropriate based its intent.
*/
private void applyVibrationIntensityScalingLocked(Vibration vib, int intensity) {
if (vib.effect instanceof VibrationEffect.Prebaked) {
// Prebaked effects are always just a direct translation from intensity to
// EffectStrength.
VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect;
prebaked.setEffectStrength(intensityToEffectStrength(intensity));
return;
}
final int defaultIntensity = getDefaultIntensity(vib.attrs.getUsage());
final ScaleLevel scale = mScaleLevels.get(intensity - defaultIntensity);
if (scale == null) {
// We should have scaling levels for all cases, so not being able to scale because of a
// missing level is unexpected.
Slog.e(TAG, "No configured scaling level!"
+ " (current=" + intensity + ", default= " + defaultIntensity + ")");
return;
}
vib.originalEffect = vib.effect;
vib.effect = vib.effect.resolve(mDefaultVibrationAmplitude).scale(scale.factor);
vib.scale = scale.factor;
}
private boolean shouldVibrateForRingtone() {
AudioManager audioManager = mContext.getSystemService(AudioManager.class);
int ringerMode = audioManager.getRingerModeInternal();
// "Also vibrate for calls" Setting in Sound
if (Settings.System.getInt(
mContext.getContentResolver(), Settings.System.VIBRATE_WHEN_RINGING, 0) != 0) {
return ringerMode != AudioManager.RINGER_MODE_SILENT;
} else if (Settings.Global.getInt(
mContext.getContentResolver(), Settings.Global.APPLY_RAMPING_RINGER, 0) != 0) {
return ringerMode != AudioManager.RINGER_MODE_SILENT;
} else {
return ringerMode == AudioManager.RINGER_MODE_VIBRATE;
/** Scale the vibration effect by the intensity as appropriate based its intent. */
private void applyVibrationIntensityScalingLocked(Vibration vib) {
VibrationEffect scaled = mVibrationScaler.scale(vib.effect, vib.attrs.getUsage());
if (!scaled.equals(vib.effect)) {
vib.originalEffect = vib.effect;
vib.effect = scaled;
}
}
@@ -1262,18 +1121,19 @@ public class VibratorService extends IVibratorService.Stub
return mode;
}
private boolean shouldVibrate(Vibration vib, int intensity) {
private boolean shouldVibrate(Vibration vib) {
if (!shouldVibrateForPowerModeLocked(vib)) {
endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_POWER);
return false;
}
int intensity = mVibrationSettings.getCurrentIntensity(vib.attrs.getUsage());
if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_SETTINGS);
return false;
}
if (vib.isRingtone() && !shouldVibrateForRingtone()) {
if (vib.isRingtone() && !mVibrationSettings.shouldVibrateForRingtone()) {
if (DEBUG) {
Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones");
}
@@ -1335,7 +1195,6 @@ public class VibratorService extends IVibratorService.Stub
synchronized (mLock) {
boolean devicesUpdated = updateInputDeviceVibratorsLocked();
boolean lowPowerModeUpdated = updateLowPowerModeLocked();
updateVibrationIntensityLocked();
if (devicesUpdated || lowPowerModeUpdated) {
// If the state changes out from under us then just reset.
@@ -1348,13 +1207,7 @@ public class VibratorService extends IVibratorService.Stub
private boolean updateInputDeviceVibratorsLocked() {
boolean changed = false;
boolean vibrateInputDevices = false;
try {
vibrateInputDevices = Settings.System.getIntForUser(
mContext.getContentResolver(),
Settings.System.VIBRATE_INPUT_DEVICES, UserHandle.USER_CURRENT) > 0;
} catch (SettingNotFoundException snfe) {
}
boolean vibrateInputDevices = mVibrationSettings.shouldVibrateInputDevices();
if (vibrateInputDevices != mVibrateInputDevicesSetting) {
changed = true;
mVibrateInputDevicesSetting = vibrateInputDevices;
@@ -1397,26 +1250,13 @@ public class VibratorService extends IVibratorService.Stub
return false;
}
private void updateVibrationIntensityLocked() {
mHapticFeedbackIntensity = Settings.System.getIntForUser(mContext.getContentResolver(),
Settings.System.HAPTIC_FEEDBACK_INTENSITY,
mVibrator.getDefaultHapticFeedbackIntensity(), UserHandle.USER_CURRENT);
mNotificationIntensity = Settings.System.getIntForUser(mContext.getContentResolver(),
Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
mVibrator.getDefaultNotificationVibrationIntensity(), UserHandle.USER_CURRENT);
mRingIntensity = Settings.System.getIntForUser(mContext.getContentResolver(),
Settings.System.RING_VIBRATION_INTENSITY,
mVibrator.getDefaultRingVibrationIntensity(), UserHandle.USER_CURRENT);
}
private void updateAlwaysOnLocked(int id, Vibration vib) {
final int intensity = getCurrentIntensityLocked(vib.attrs.getUsage());
if (!shouldVibrate(vib, intensity)) {
if (!shouldVibrate(vib)) {
mNativeWrapper.vibratorAlwaysOnDisable(id);
} else {
final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect;
final int strength = intensityToEffectStrength(intensity);
mNativeWrapper.vibratorAlwaysOnEnable(id, prebaked.getId(), strength);
VibrationEffect.Prebaked scaled = mVibrationScaler.scale(vib.effect,
vib.attrs.getUsage());
mNativeWrapper.vibratorAlwaysOnEnable(id, scaled.getId(), scaled.getEffectStrength());
}
}
@@ -1459,8 +1299,8 @@ public class VibratorService extends IVibratorService.Stub
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn");
try {
synchronized (mInputDeviceVibrators) {
final VibrationEffect.OneShot oneShot =
(VibrationEffect.OneShot) vib.effect.resolve(mDefaultVibrationAmplitude);
final VibrationEffect.OneShot oneShot = vib.effect.resolve(
mDefaultVibrationAmplitude);
if (DEBUG) {
Slog.d(TAG, "Turning vibrator on for " + oneShot.getDuration() + " ms"
+ " with amplitude " + oneShot.getAmplitude() + ".");
@@ -1547,9 +1387,8 @@ public class VibratorService extends IVibratorService.Stub
vib.opPkg, vib.reason + " (fallback)");
// Set current vibration before starting it, so callback will work.
mCurrentVibration = fallbackVib;
final int intensity = getCurrentIntensityLocked(fallbackVib.attrs.getUsage());
linkVibration(fallbackVib);
applyVibrationIntensityScalingLocked(fallbackVib, intensity);
applyVibrationIntensityScalingLocked(fallbackVib);
startVibrationInnerLocked(fallbackVib);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
@@ -1594,25 +1433,6 @@ public class VibratorService extends IVibratorService.Stub
return mFallbackEffects.get(effectId);
}
/**
* Return the current desired effect strength.
*
* If the returned value is &lt; 0 then the vibration shouldn't be played at all.
*/
private static int intensityToEffectStrength(int intensity) {
switch (intensity) {
case Vibrator.VIBRATION_INTENSITY_LOW:
return EffectStrength.LIGHT;
case Vibrator.VIBRATION_INTENSITY_MEDIUM:
return EffectStrength.MEDIUM;
case Vibrator.VIBRATION_INTENSITY_HIGH:
return EffectStrength.STRONG;
default:
Slog.w(TAG, "Got unexpected vibration intensity: " + intensity);
return EffectStrength.STRONG;
}
}
private static boolean isNotification(int usageHint) {
return usageHint == VibrationAttributes.USAGE_NOTIFICATION;
}
@@ -1690,14 +1510,7 @@ public class VibratorService extends IVibratorService.Stub
pw.println(" mVibratorStateListeners Count="
+ mVibratorStateListeners.getRegisteredCallbackCount());
pw.println(" mLowPowerMode=" + mLowPowerMode);
pw.println(" mHapticFeedbackIntensity=" + mHapticFeedbackIntensity);
pw.println(" mHapticFeedbackDefaultIntensity="
+ mVibrator.getDefaultHapticFeedbackIntensity());
pw.println(" mNotificationIntensity=" + mNotificationIntensity);
pw.println(" mNotificationDefaultIntensity="
+ mVibrator.getDefaultNotificationVibrationIntensity());
pw.println(" mRingIntensity=" + mRingIntensity);
pw.println(" mRingDefaultIntensity=" + mVibrator.getDefaultRingVibrationIntensity());
pw.println(" mVibrationSettings=" + mVibrationSettings);
pw.println(" mSupportedEffects=" + mSupportedEffects);
pw.println(" mSupportedPrimitives=" + mSupportedPrimitives);
pw.println();
@@ -1746,15 +1559,17 @@ public class VibratorService extends IVibratorService.Stub
mVibratorUnderExternalControl);
proto.write(VibratorServiceDumpProto.LOW_POWER_MODE, mLowPowerMode);
proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_INTENSITY,
mHapticFeedbackIntensity);
mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_TOUCH));
proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_DEFAULT_INTENSITY,
mVibrator.getDefaultHapticFeedbackIntensity());
proto.write(VibratorServiceDumpProto.NOTIFICATION_INTENSITY, mNotificationIntensity);
mVibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_TOUCH));
proto.write(VibratorServiceDumpProto.NOTIFICATION_INTENSITY,
mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_NOTIFICATION));
proto.write(VibratorServiceDumpProto.NOTIFICATION_DEFAULT_INTENSITY,
mVibrator.getDefaultNotificationVibrationIntensity());
proto.write(VibratorServiceDumpProto.RING_INTENSITY, mRingIntensity);
mVibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_NOTIFICATION));
proto.write(VibratorServiceDumpProto.RING_INTENSITY,
mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_RINGTONE));
proto.write(VibratorServiceDumpProto.RING_DEFAULT_INTENSITY,
mVibrator.getDefaultRingVibrationIntensity());
mVibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_RINGTONE));
for (VibrationInfo info : mPreviousRingVibrations) {
info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_RING_VIBRATIONS);
@@ -2080,7 +1895,7 @@ public class VibratorService extends IVibratorService.Stub
@Override
public int onExternalVibrationStart(ExternalVibration vib) {
if (!hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
return SCALE_MUTE;
return IExternalVibratorService.SCALE_MUTE;
}
if (ActivityManager.checkComponentPermission(android.Manifest.permission.VIBRATE,
vib.getUid(), -1 /*owningUid*/, true /*exported*/)
@@ -2088,7 +1903,7 @@ public class VibratorService extends IVibratorService.Stub
Slog.w(TAG, "pkg=" + vib.getPackage() + ", uid=" + vib.getUid()
+ " tried to play externally controlled vibration"
+ " without VIBRATE permission, ignoring.");
return SCALE_MUTE;
return IExternalVibratorService.SCALE_MUTE;
}
int mode = getAppOpMode(vib.getUid(), vib.getPackage(), vib.getVibrationAttributes());
@@ -2101,10 +1916,9 @@ public class VibratorService extends IVibratorService.Stub
} else {
endVibrationLocked(vibHolder, VibrationInfo.Status.IGNORED_APP_OPS);
}
return SCALE_MUTE;
return IExternalVibratorService.SCALE_MUTE;
}
final int scaleLevel;
synchronized (mLock) {
if (mCurrentExternalVibration != null
&& mCurrentExternalVibration.externalVibration.equals(vib)) {
@@ -2131,23 +1945,12 @@ public class VibratorService extends IVibratorService.Stub
mCurrentExternalVibration = new ExternalVibrationHolder(vib);
mCurrentExternalDeathRecipient = new ExternalVibrationDeathRecipient();
vib.linkToDeath(mCurrentExternalDeathRecipient);
mCurrentExternalVibration.scale = mVibrationScaler.getExternalVibrationScale(
vib.getVibrationAttributes().getUsage());
if (DEBUG) {
Slog.e(TAG, "Playing external vibration: " + vib);
}
int usage = vib.getVibrationAttributes().getUsage();
int defaultIntensity = getDefaultIntensity(usage);
int currentIntensity = getCurrentIntensityLocked(usage);
scaleLevel = currentIntensity - defaultIntensity;
}
if (scaleLevel >= SCALE_VERY_LOW && scaleLevel <= SCALE_VERY_HIGH) {
mCurrentExternalVibration.scale = scaleLevel;
return scaleLevel;
} else {
// Presumably we want to play this but something about our scaling has gone
// wrong, so just play with no scaling.
Slog.w(TAG, "Error in scaling calculations, ended up with invalid scale level "
+ scaleLevel + " for vibration " + vib);
return SCALE_NONE;
return mCurrentExternalVibration.scale;
}
}
@@ -2232,21 +2035,12 @@ public class VibratorService extends IVibratorService.Stub
}
private boolean checkDoNotDisturb(CommonOptions opts) {
try {
final int zenMode = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ZEN_MODE);
if (zenMode != Settings.Global.ZEN_MODE_OFF && !opts.force) {
try (PrintWriter pw = getOutPrintWriter();) {
pw.print("Ignoring because device is on DND mode ");
pw.println(DebugUtils.flagsToString(Settings.Global.class, "ZEN_MODE_",
zenMode));
return true;
}
if (mVibrationSettings.isInZenMode() && !opts.force) {
try (PrintWriter pw = getOutPrintWriter();) {
pw.print("Ignoring because device is on DND mode ");
return true;
}
} catch (SettingNotFoundException e) {
// ignore
}
return false;
}

View File

@@ -0,0 +1,158 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.vibrator;
import android.content.Context;
import android.hardware.vibrator.V1_0.EffectStrength;
import android.os.IExternalVibratorService;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.util.Slog;
import android.util.SparseArray;
/** Controls vibration scaling. */
// TODO(b/159207608): Make this package-private once vibrator services are moved to this package
public final class VibrationScaler {
private static final String TAG = "VibrationScaler";
// Scale levels. Each level, except MUTE, is defined as the delta between the current setting
// and the default intensity for that type of vibration (i.e. current - default).
private static final int SCALE_MUTE = IExternalVibratorService.SCALE_MUTE; // -100
private static final int SCALE_VERY_LOW = IExternalVibratorService.SCALE_VERY_LOW; // -2
private static final int SCALE_LOW = IExternalVibratorService.SCALE_LOW; // -1
private static final int SCALE_NONE = IExternalVibratorService.SCALE_NONE; // 0
private static final int SCALE_HIGH = IExternalVibratorService.SCALE_HIGH; // 1
private static final int SCALE_VERY_HIGH = IExternalVibratorService.SCALE_VERY_HIGH; // 2
// Scale factors for each level.
private static final float SCALE_FACTOR_VERY_LOW = 0.6f;
private static final float SCALE_FACTOR_LOW = 0.8f;
private static final float SCALE_FACTOR_NONE = 1f;
private static final float SCALE_FACTOR_HIGH = 1.2f;
private static final float SCALE_FACTOR_VERY_HIGH = 1.4f;
// A mapping from the intensity adjustment to the scaling to apply, where the intensity
// adjustment is defined as the delta between the default intensity level and the user selected
// intensity level. It's important that we apply the scaling on the delta between the two so
// that the default intensity level applies no scaling to application provided effects.
private final SparseArray<ScaleLevel> mScaleLevels;
private final VibrationSettings mSettingsController;
private final int mDefaultVibrationAmplitude;
public VibrationScaler(Context context, VibrationSettings settingsController) {
mSettingsController = settingsController;
mDefaultVibrationAmplitude = context.getResources().getInteger(
com.android.internal.R.integer.config_defaultVibrationAmplitude);
mScaleLevels = new SparseArray<>();
mScaleLevels.put(SCALE_VERY_LOW, new ScaleLevel(SCALE_FACTOR_VERY_LOW));
mScaleLevels.put(SCALE_LOW, new ScaleLevel(SCALE_FACTOR_LOW));
mScaleLevels.put(SCALE_NONE, new ScaleLevel(SCALE_FACTOR_NONE));
mScaleLevels.put(SCALE_HIGH, new ScaleLevel(SCALE_FACTOR_HIGH));
mScaleLevels.put(SCALE_VERY_HIGH, new ScaleLevel(SCALE_FACTOR_VERY_HIGH));
}
/**
* Calculates the scale to be applied to external vibration with given usage.
*
* @param usageHint one of VibrationAttributes.USAGE_*
* @return one of IExternalVibratorService.SCALE_*
*/
public int getExternalVibrationScale(int usageHint) {
int defaultIntensity = mSettingsController.getDefaultIntensity(usageHint);
int currentIntensity = mSettingsController.getCurrentIntensity(usageHint);
int scaleLevel = currentIntensity - defaultIntensity;
if (scaleLevel >= SCALE_VERY_LOW && scaleLevel <= SCALE_VERY_HIGH) {
return scaleLevel;
} else {
// Something about our scaling has gone wrong, so just play with no scaling.
Slog.w(TAG, "Error in scaling calculations, ended up with invalid scale level "
+ scaleLevel + " for vibration with usage " + usageHint);
return SCALE_NONE;
}
}
/**
* Scale a {@link VibrationEffect} based on the given usage hint for this vibration.
*
* @param effect the effect to be scaled
* @param usageHint one of VibrationAttributes.USAGE_*
* @return The same given effect, if no changes were made, or a new {@link VibrationEffect} with
* resolved and scaled amplitude
*/
public <T extends VibrationEffect> T scale(VibrationEffect effect, int usageHint) {
if (effect instanceof VibrationEffect.Prebaked) {
// Prebaked effects are always just a direct translation to EffectStrength.
int intensity = mSettingsController.getCurrentIntensity(usageHint);
int newStrength = intensityToEffectStrength(intensity);
VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect;
if (prebaked.getEffectStrength() == newStrength) {
return (T) prebaked;
}
return (T) new VibrationEffect.Prebaked(
prebaked.getId(), prebaked.shouldFallback(), newStrength);
}
effect = effect.resolve(mDefaultVibrationAmplitude);
int defaultIntensity = mSettingsController.getDefaultIntensity(usageHint);
int currentIntensity = mSettingsController.getCurrentIntensity(usageHint);
ScaleLevel scale = mScaleLevels.get(currentIntensity - defaultIntensity);
if (scale == null) {
// Something about our scaling has gone wrong, so just play with no scaling.
Slog.e(TAG, "No configured scaling level!"
+ " (current=" + currentIntensity + ", default= " + defaultIntensity + ")");
return (T) effect;
}
return effect.scale(scale.factor);
}
/** Mapping of Vibrator.VIBRATION_INTENSITY_* values to {@link EffectStrength}. */
private static int intensityToEffectStrength(int intensity) {
switch (intensity) {
case Vibrator.VIBRATION_INTENSITY_LOW:
return EffectStrength.LIGHT;
case Vibrator.VIBRATION_INTENSITY_MEDIUM:
return EffectStrength.MEDIUM;
case Vibrator.VIBRATION_INTENSITY_HIGH:
return EffectStrength.STRONG;
default:
Slog.w(TAG, "Got unexpected vibration intensity: " + intensity);
return EffectStrength.STRONG;
}
}
/** Represents the scale that must be applied to a vibration effect intensity. */
private static final class ScaleLevel {
public final float factor;
ScaleLevel(float factor) {
this.factor = factor;
}
@Override
public String toString() {
return "ScaleLevel{factor=" + factor + "}";
}
}
}

View File

@@ -0,0 +1,284 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.vibrator;
import android.content.Context;
import android.database.ContentObserver;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Handler;
import android.os.UserHandle;
import android.os.VibrationAttributes;
import android.os.Vibrator;
import android.provider.Settings;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.List;
/** Controls all the system settings related to vibration. */
// TODO(b/159207608): Make this package-private once vibrator services are moved to this package
public final class VibrationSettings {
private static final String TAG = "VibrationSettings";
/** Listener for changes on vibration settings. */
public interface OnVibratorSettingsChanged {
/** Callback triggered when any of the vibrator settings change. */
void onChange();
}
private final Object mLock = new Object();
private final Context mContext;
private final Vibrator mVibrator;
private final AudioManager mAudioManager;
private final SettingsObserver mSettingObserver;
@GuardedBy("mLock")
private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>();
@GuardedBy("mLock")
private boolean mVibrateInputDevices;
@GuardedBy("mLock")
private boolean mVibrateWhenRinging;
@GuardedBy("mLock")
private boolean mApplyRampingRinger;
@GuardedBy("mLock")
private int mZenMode;
@GuardedBy("mLock")
private int mHapticFeedbackIntensity;
@GuardedBy("mLock")
private int mNotificationIntensity;
@GuardedBy("mLock")
private int mRingIntensity;
public VibrationSettings(Context context, Handler handler) {
mContext = context;
mVibrator = context.getSystemService(Vibrator.class);
mAudioManager = context.getSystemService(AudioManager.class);
mSettingObserver = new SettingsObserver(handler);
registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES));
registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING));
registerSettingsObserver(Settings.Global.getUriFor(Settings.Global.APPLY_RAMPING_RINGER));
registerSettingsObserver(Settings.Global.getUriFor(Settings.Global.ZEN_MODE));
registerSettingsObserver(
Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_INTENSITY));
registerSettingsObserver(
Settings.System.getUriFor(Settings.System.NOTIFICATION_VIBRATION_INTENSITY));
registerSettingsObserver(
Settings.System.getUriFor(Settings.System.RING_VIBRATION_INTENSITY));
// Update with current values from settings.
updateSettings();
}
/**
* Add listener to vibrator settings changes. This will trigger the listener with current state
* immediately and every time one of the settings change.
*/
public void addListener(OnVibratorSettingsChanged listener) {
synchronized (mLock) {
if (!mListeners.contains(listener)) {
mListeners.add(listener);
}
}
}
/** Remove listener to vibrator settings. */
public void removeListener(OnVibratorSettingsChanged listener) {
synchronized (mLock) {
mListeners.remove(listener);
}
}
/**
* Return default vibration intensity for given usage.
*
* @param usageHint one of VibrationAttributes.USAGE_*
* @return The vibration intensity, one of Vibrator.VIBRATION_INTENSITY_*
*/
public int getDefaultIntensity(int usageHint) {
if (isRingtone(usageHint)) {
return mVibrator.getDefaultRingVibrationIntensity();
} else if (isNotification(usageHint)) {
return mVibrator.getDefaultNotificationVibrationIntensity();
} else if (isHapticFeedback(usageHint)) {
return mVibrator.getDefaultHapticFeedbackIntensity();
} else if (isAlarm(usageHint)) {
return Vibrator.VIBRATION_INTENSITY_HIGH;
} else {
return Vibrator.VIBRATION_INTENSITY_MEDIUM;
}
}
/**
* Return the current vibration intensity set for given usage at the user settings.
*
* @param usageHint one of VibrationAttributes.USAGE_*
* @return The vibration intensity, one of Vibrator.VIBRATION_INTENSITY_*
*/
public int getCurrentIntensity(int usageHint) {
synchronized (mLock) {
if (isRingtone(usageHint)) {
return mRingIntensity;
} else if (isNotification(usageHint)) {
return mNotificationIntensity;
} else if (isHapticFeedback(usageHint)) {
return mHapticFeedbackIntensity;
} else if (isAlarm(usageHint)) {
return Vibrator.VIBRATION_INTENSITY_HIGH;
} else {
return Vibrator.VIBRATION_INTENSITY_MEDIUM;
}
}
}
/**
* Return {@code true} if the device should vibrate for ringtones.
*
* <p>This checks the current {@link AudioManager#getRingerMode()} against user settings for
* vibrations while ringing.
*/
public boolean shouldVibrateForRingtone() {
int ringerMode = mAudioManager.getRingerModeInternal();
synchronized (mLock) {
if (mVibrateWhenRinging) {
return ringerMode != AudioManager.RINGER_MODE_SILENT;
} else if (mApplyRampingRinger) {
return ringerMode != AudioManager.RINGER_MODE_SILENT;
} else {
return ringerMode == AudioManager.RINGER_MODE_VIBRATE;
}
}
}
/** Return {@code true} if input devices should vibrate instead of this device. */
public boolean shouldVibrateInputDevices() {
return mVibrateInputDevices;
}
/** Return {@code true} if setting for {@link Settings.Global#ZEN_MODE} is not OFF. */
public boolean isInZenMode() {
return mZenMode != Settings.Global.ZEN_MODE_OFF;
}
private static boolean isNotification(int usageHint) {
return usageHint == VibrationAttributes.USAGE_NOTIFICATION;
}
private static boolean isRingtone(int usageHint) {
return usageHint == VibrationAttributes.USAGE_RINGTONE;
}
private static boolean isHapticFeedback(int usageHint) {
return usageHint == VibrationAttributes.USAGE_TOUCH;
}
private static boolean isAlarm(int usageHint) {
return usageHint == VibrationAttributes.USAGE_ALARM;
}
/** 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;
mHapticFeedbackIntensity = getSystemSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
mVibrator.getDefaultHapticFeedbackIntensity());
mNotificationIntensity = getSystemSetting(
Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
mVibrator.getDefaultNotificationVibrationIntensity());
mRingIntensity = getSystemSetting(Settings.System.RING_VIBRATION_INTENSITY,
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();
}
}
@Override
public String toString() {
return "VibrationSettings{"
+ "mVibrateInputDevices=" + mVibrateInputDevices
+ ", mVibrateWhenRinging=" + mVibrateWhenRinging
+ ", mApplyRampingRinger=" + mApplyRampingRinger
+ ", mZenMode=" + Settings.Global.zenModeToString(mZenMode)
+ ", mHapticFeedbackIntensity="
+ intensityToString(getCurrentIntensity(VibrationAttributes.USAGE_TOUCH))
+ ", mHapticFeedbackDefaultIntensity="
+ intensityToString(getDefaultIntensity(VibrationAttributes.USAGE_TOUCH))
+ ", mNotificationIntensity="
+ intensityToString(getCurrentIntensity(VibrationAttributes.USAGE_NOTIFICATION))
+ ", mNotificationDefaultIntensity="
+ intensityToString(getDefaultIntensity(VibrationAttributes.USAGE_NOTIFICATION))
+ ", mRingIntensity="
+ intensityToString(getCurrentIntensity(VibrationAttributes.USAGE_RINGTONE))
+ ", mRingDefaultIntensity="
+ intensityToString(getDefaultIntensity(VibrationAttributes.USAGE_RINGTONE))
+ '}';
}
private static String intensityToString(int intensity) {
switch (intensity) {
case Vibrator.VIBRATION_INTENSITY_OFF:
return "OFF";
case Vibrator.VIBRATION_INTENSITY_LOW:
return "LOW";
case Vibrator.VIBRATION_INTENSITY_MEDIUM:
return "MEDIUM";
case Vibrator.VIBRATION_INTENSITY_HIGH:
return "HIGH";
default:
return "UNKNOWN INTENSITY " + intensity;
}
}
private int getSystemSetting(String settingName, int defaultValue) {
return Settings.System.getIntForUser(mContext.getContentResolver(),
settingName, defaultValue, UserHandle.USER_CURRENT);
}
private int getGlobalSetting(String settingName, int defaultValue) {
return Settings.Global.getInt(mContext.getContentResolver(), settingName, defaultValue);
}
private void registerSettingsObserver(Uri settingUri) {
mContext.getContentResolver().registerContentObserver(
settingUri, /* notifyForDescendants= */ true, mSettingObserver,
UserHandle.USER_ALL);
}
/** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */
private final class SettingsObserver extends ContentObserver {
SettingsObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
updateSettings();
}
}
}

View File

@@ -39,11 +39,13 @@ import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.PackageManagerInternal;
import android.hardware.vibrator.IVibrator;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.os.Handler;
import android.os.IBinder;
import android.os.IVibratorStateListener;
@@ -61,11 +63,11 @@ import android.os.Vibrator;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import androidx.test.InstrumentationRegistry;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
import org.junit.After;
import org.junit.Before;
@@ -107,11 +109,13 @@ public class VibratorServiceTest {
new VibrationAttributes.Builder().setUsage(
VibrationAttributes.USAGE_RINGTONE).build();
@Rule public MockitoRule rule = MockitoJUnit.rule();
@Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
@Mock private PackageManagerInternal mPackageManagerInternalMock;
@Mock private PowerManagerInternal mPowerManagerInternalMock;
@Mock private PowerSaveState mPowerSaveStateMock;
// TODO(b/131311651): replace with a FakeVibrator instead.
@Mock private Vibrator mVibratorMock;
@Mock private AppOpsManager mAppOpsManagerMock;
@Mock private VibratorService.NativeWrapper mNativeWrapperMock;
@@ -126,9 +130,7 @@ public class VibratorServiceTest {
mTestLooper = new TestLooper();
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
MockContentResolver contentResolver = new MockContentResolver(mContextSpy);
contentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mVibratorMock);
when(mContextSpy.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManagerMock);
@@ -144,28 +146,26 @@ public class VibratorServiceTest {
when(mPowerManagerInternalMock.getLowPowerState(PowerManager.ServiceType.VIBRATION))
.thenReturn(mPowerSaveStateMock);
setVibrationIntensityUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
setVibrationIntensityUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_MEDIUM);
setVibrationIntensityUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_MEDIUM);
setVibrationIntensityUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
Vibrator.VIBRATION_INTENSITY_MEDIUM);
addLocalServiceMock(PackageManagerInternal.class, mPackageManagerInternalMock);
addLocalServiceMock(PowerManagerInternal.class, mPowerManagerInternalMock);
FakeSettingsProvider.clearSettingsProvider();
}
@After
public void tearDown() throws Exception {
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.removeServiceForTest(PowerManagerInternal.class);
FakeSettingsProvider.clearSettingsProvider();
}
private VibratorService createService() {
return new VibratorService(mContextSpy,
VibratorService service = new VibratorService(mContextSpy,
new VibratorService.Injector() {
@Override
VibratorService.NativeWrapper getNativeWrapper() {
@@ -182,6 +182,8 @@ public class VibratorServiceTest {
// ignore
}
});
service.systemReady();
return service;
}
@Test
@@ -304,10 +306,30 @@ public class VibratorServiceTest {
verify(mNativeWrapperMock, never()).vibratorAlwaysOnEnable(anyLong(), anyLong(), anyLong());
}
@Test
public void vibrate_withRingtone_usesRingtoneSettings() {
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
vibrate(createService(), VibrationEffect.createOneShot(1, 1), RINGTONE_ATTRS);
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 1);
vibrate(createService(), VibrationEffect.createOneShot(10, 10), RINGTONE_ATTRS);
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
vibrate(createService(), VibrationEffect.createOneShot(100, 100), RINGTONE_ATTRS);
InOrder inOrderVerifier = inOrder(mNativeWrapperMock);
inOrderVerifier.verify(mNativeWrapperMock, never()).vibratorOn(eq(1L), anyLong());
inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(10L), anyLong());
inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(100L), anyLong());
}
@Test
public void vibrate_withAudioAttributes_usesOriginalAudioUsageInAppOpsManager() {
VibratorService service = createService();
service.systemReady();
VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
AudioAttributes audioAttributes = new AudioAttributes.Builder()
@@ -324,7 +346,6 @@ public class VibratorServiceTest {
@Test
public void vibrate_withVibrationAttributes_usesCorrespondingAudioUsageInAppOpsManager() {
VibratorService service = createService();
service.systemReady();
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), ALARM_ATTRS);
vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_TICK), NOTIFICATION_ATTRS);
@@ -624,14 +645,13 @@ public class VibratorServiceTest {
@Test
public void scale_withPrebaked_userIntensitySettingAsEffectStrength() {
// Alarm vibration is always VIBRATION_INTENSITY_HIGH.
setVibrationIntensityUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_MEDIUM);
setVibrationIntensityUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
Vibrator.VIBRATION_INTENSITY_LOW);
setVibrationIntensityUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_OFF);
VibratorService service = createService();
service.systemReady();
vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK),
ALARM_ATTRS);
@@ -659,16 +679,15 @@ public class VibratorServiceTest {
public void scale_withOneShotAndWaveform_usesScaleLevelOnAmplitude() throws Exception {
when(mVibratorMock.getDefaultNotificationVibrationIntensity())
.thenReturn(Vibrator.VIBRATION_INTENSITY_LOW);
setVibrationIntensityUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_HIGH);
setVibrationIntensityUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
Vibrator.VIBRATION_INTENSITY_LOW);
setVibrationIntensityUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_OFF);
mockVibratorCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL);
VibratorService service = createService();
service.systemReady();
vibrate(service, VibrationEffect.createOneShot(20, 100), ALARM_ATTRS);
vibrate(service, VibrationEffect.createOneShot(20, 100), NOTIFICATION_ATTRS);
@@ -694,16 +713,15 @@ public class VibratorServiceTest {
public void scale_withComposed_usesScaleLevelOnPrimitiveScaleValues() {
when(mVibratorMock.getDefaultNotificationVibrationIntensity())
.thenReturn(Vibrator.VIBRATION_INTENSITY_LOW);
setVibrationIntensityUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_HIGH);
setVibrationIntensityUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
Vibrator.VIBRATION_INTENSITY_LOW);
setVibrationIntensityUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_OFF);
mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
VibratorService service = createService();
service.systemReady();
VibrationEffect effect = VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f)
@@ -785,8 +803,18 @@ public class VibratorServiceTest {
LocalServices.addService(clazz, mock);
}
private void setVibrationIntensityUserSetting(String settingName, int value) {
private void setRingerMode(int ringerMode) {
AudioManager audioManager = mContextSpy.getSystemService(AudioManager.class);
audioManager.setRingerModeInternal(ringerMode);
assertEquals(ringerMode, audioManager.getRingerMode());
}
private void setUserSetting(String settingName, int value) {
Settings.System.putIntForUser(
mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
}
private void setGlobalSetting(String settingName, int value) {
Settings.Global.putInt(mContextSpy.getContentResolver(), settingName, value);
}
}

View File

@@ -0,0 +1,238 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.vibrator;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.ContentResolver;
import android.content.Context;
import android.content.ContextWrapper;
import android.os.Handler;
import android.os.IExternalVibratorService;
import android.os.UserHandle;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
import androidx.test.InstrumentationRegistry;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
/**
* Tests for {@link VibrationScaler}.
*
* Build/Install/Run:
* atest FrameworksServicesTests:VibrationScalerTest
*/
@Presubmit
public class VibrationScalerTest {
@Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
// TODO(b/131311651): replace with a FakeVibrator instead.
@Mock private Vibrator mVibratorMock;
private TestLooper mTestLooper;
private ContextWrapper mContextSpy;
private VibrationSettings mVibrationSettings;
private VibrationScaler mVibrationScaler;
@Before
public void setUp() throws Exception {
mTestLooper = new TestLooper();
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mVibratorMock);
mVibrationSettings = new VibrationSettings(
mContextSpy, new Handler(mTestLooper.getLooper()));
mVibrationScaler = new VibrationScaler(mContextSpy, mVibrationSettings);
}
@Test
public void testGetExternalVibrationScale() {
when(mVibratorMock.getDefaultHapticFeedbackIntensity())
.thenReturn(Vibrator.VIBRATION_INTENSITY_LOW);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
Vibrator.VIBRATION_INTENSITY_HIGH);
assertEquals(IExternalVibratorService.SCALE_VERY_HIGH,
mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH));
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
Vibrator.VIBRATION_INTENSITY_MEDIUM);
assertEquals(IExternalVibratorService.SCALE_HIGH,
mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH));
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
Vibrator.VIBRATION_INTENSITY_LOW);
assertEquals(IExternalVibratorService.SCALE_NONE,
mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH));
when(mVibratorMock.getDefaultHapticFeedbackIntensity())
.thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM);
assertEquals(IExternalVibratorService.SCALE_LOW,
mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH));
when(mVibratorMock.getDefaultHapticFeedbackIntensity())
.thenReturn(Vibrator.VIBRATION_INTENSITY_HIGH);
assertEquals(IExternalVibratorService.SCALE_VERY_LOW,
mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH));
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
Vibrator.VIBRATION_INTENSITY_OFF);
// Unexpected vibration intensity will be treated as SCALE_NONE.
assertEquals(IExternalVibratorService.SCALE_NONE,
mVibrationScaler.getExternalVibrationScale(VibrationAttributes.USAGE_TOUCH));
}
@Test
public void scale_withPrebaked_setsEffectStrengthBasedOnSettings() {
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_HIGH);
VibrationEffect effect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK);
VibrationEffect.Prebaked scaled = mVibrationScaler.scale(
effect, VibrationAttributes.USAGE_NOTIFICATION);
assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG);
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_MEDIUM);
scaled = mVibrationScaler.scale(effect, VibrationAttributes.USAGE_NOTIFICATION);
assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_MEDIUM);
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_LOW);
scaled = mVibrationScaler.scale(effect, VibrationAttributes.USAGE_NOTIFICATION);
assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_LIGHT);
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_OFF);
scaled = mVibrationScaler.scale(effect, VibrationAttributes.USAGE_NOTIFICATION);
// Unexpected intensity setting will be mapped to STRONG.
assertEquals(scaled.getEffectStrength(), VibrationEffect.EFFECT_STRENGTH_STRONG);
}
@Test
public void scale_withOneShotAndWaveform_resolvesAmplitude() {
// No scale, default amplitude still resolved
when(mVibratorMock.getDefaultRingVibrationIntensity())
.thenReturn(Vibrator.VIBRATION_INTENSITY_LOW);
setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_LOW);
VibrationEffect.OneShot oneShot = mVibrationScaler.scale(
VibrationEffect.createOneShot(10, VibrationEffect.DEFAULT_AMPLITUDE),
VibrationAttributes.USAGE_RINGTONE);
assertTrue(oneShot.getAmplitude() > 0);
VibrationEffect.Waveform waveform = mVibrationScaler.scale(
VibrationEffect.createWaveform(new long[]{10},
new int[]{VibrationEffect.DEFAULT_AMPLITUDE}, -1),
VibrationAttributes.USAGE_RINGTONE);
assertTrue(waveform.getAmplitudes()[0] > 0);
}
@Test
public void scale_withOneShotWaveform_scalesAmplitude() {
when(mVibratorMock.getDefaultRingVibrationIntensity())
.thenReturn(Vibrator.VIBRATION_INTENSITY_LOW);
setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_HIGH);
when(mVibratorMock.getDefaultNotificationVibrationIntensity())
.thenReturn(Vibrator.VIBRATION_INTENSITY_HIGH);
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_LOW);
when(mVibratorMock.getDefaultHapticFeedbackIntensity())
.thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
Vibrator.VIBRATION_INTENSITY_MEDIUM);
VibrationEffect.OneShot oneShot = mVibrationScaler.scale(
VibrationEffect.createOneShot(100, 100), VibrationAttributes.USAGE_RINGTONE);
// Ringtone scales up.
assertTrue(oneShot.getAmplitude() > 100);
VibrationEffect.Waveform waveform = mVibrationScaler.scale(
VibrationEffect.createWaveform(new long[]{100}, new int[]{100}, -1),
VibrationAttributes.USAGE_NOTIFICATION);
// Notification scales down.
assertTrue(waveform.getAmplitudes()[0] < 100);
oneShot = mVibrationScaler.scale(VibrationEffect.createOneShot(100, 100),
VibrationAttributes.USAGE_TOUCH);
// Haptic feedback does not scale.
assertEquals(100, oneShot.getAmplitude());
}
@Test
public void scale_withComposed_scalesPrimitives() {
when(mVibratorMock.getDefaultRingVibrationIntensity())
.thenReturn(Vibrator.VIBRATION_INTENSITY_LOW);
setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_HIGH);
when(mVibratorMock.getDefaultNotificationVibrationIntensity())
.thenReturn(Vibrator.VIBRATION_INTENSITY_HIGH);
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_LOW);
when(mVibratorMock.getDefaultHapticFeedbackIntensity())
.thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
Vibrator.VIBRATION_INTENSITY_MEDIUM);
VibrationEffect composed = VibrationEffect.startComposition()
.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f).compose();
VibrationEffect.Composed scaled = mVibrationScaler.scale(composed,
VibrationAttributes.USAGE_RINGTONE);
// Ringtone scales up.
assertTrue(scaled.getPrimitiveEffects().get(0).scale > 0.5f);
scaled = mVibrationScaler.scale(composed, VibrationAttributes.USAGE_NOTIFICATION);
// Notification scales down.
assertTrue(scaled.getPrimitiveEffects().get(0).scale < 0.5f);
scaled = mVibrationScaler.scale(composed, VibrationAttributes.USAGE_TOUCH);
// Haptic feedback does not scale.
assertEquals(0.5, scaled.getPrimitiveEffects().get(0).scale, 1e-5);
}
private void setUserSetting(String settingName, int value) {
Settings.System.putIntForUser(
mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
// FakeSettingsProvider don't support testing triggering ContentObserver yet.
mVibrationSettings.updateSettings();
}
}

View File

@@ -0,0 +1,299 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.vibrator;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.ContentResolver;
import android.content.Context;
import android.content.ContextWrapper;
import android.media.AudioManager;
import android.os.Handler;
import android.os.UserHandle;
import android.os.VibrationAttributes;
import android.os.Vibrator;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
import androidx.test.InstrumentationRegistry;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
/**
* Tests for {@link VibrationSettings}.
*
* Build/Install/Run:
* atest FrameworksServicesTests:VibrationSettingsTest
*/
@Presubmit
public class VibrationSettingsTest {
@Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
// TODO(b/131311651): replace with a FakeVibrator instead.
@Mock private Vibrator mVibratorMock;
@Mock private VibrationSettings.OnVibratorSettingsChanged mListenerMock;
private TestLooper mTestLooper;
private ContextWrapper mContextSpy;
private AudioManager mAudioManager;
private VibrationSettings mVibrationSettings;
@Before
public void setUp() throws Exception {
mTestLooper = new TestLooper();
mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy);
when(mContextSpy.getContentResolver()).thenReturn(contentResolver);
when(mContextSpy.getSystemService(eq(Context.VIBRATOR_SERVICE))).thenReturn(mVibratorMock);
when(mVibratorMock.hasVibrator()).thenReturn(true);
mAudioManager = mContextSpy.getSystemService(AudioManager.class);
mVibrationSettings = new VibrationSettings(
mContextSpy, new Handler(mTestLooper.getLooper()));
setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0);
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
setGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
}
@After
public void tearDown() throws Exception {
FakeSettingsProvider.clearSettingsProvider();
}
@Test
public void addListener_settingsChangeTriggerListener() {
mVibrationSettings.addListener(mListenerMock);
setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
setGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_ALARMS);
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_OFF);
setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_OFF);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
Vibrator.VIBRATION_INTENSITY_OFF);
verify(mListenerMock, times(7)).onChange();
}
@Test
public void removeListener_noMoreCallbacksToListener() {
mVibrationSettings.addListener(mListenerMock);
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
verify(mListenerMock).onChange();
mVibrationSettings.removeListener(mListenerMock);
verifyNoMoreInteractions(mListenerMock);
setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
setGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_ALARMS);
}
@Test
public void shouldVibrateForRingtones_withVibrateWhenRinging_onlyIgnoreSettingsForSilentMode() {
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_SILENT);
assertEquals(AudioManager.RINGER_MODE_SILENT, mAudioManager.getRingerMode());
assertFalse(mVibrationSettings.shouldVibrateForRingtone());
mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_MAX);
assertEquals(AudioManager.RINGER_MODE_MAX, mAudioManager.getRingerMode());
assertTrue(mVibrationSettings.shouldVibrateForRingtone());
mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL);
assertEquals(AudioManager.RINGER_MODE_NORMAL, mAudioManager.getRingerMode());
assertTrue(mVibrationSettings.shouldVibrateForRingtone());
mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_VIBRATE);
assertEquals(AudioManager.RINGER_MODE_VIBRATE, mAudioManager.getRingerMode());
assertTrue(mVibrationSettings.shouldVibrateForRingtone());
}
@Test
public void shouldVibrateForRingtones_withApplyRampingRinger_onlyIgnoreSettingsForSilentMode() {
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 1);
mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_SILENT);
assertEquals(AudioManager.RINGER_MODE_SILENT, mAudioManager.getRingerMode());
assertFalse(mVibrationSettings.shouldVibrateForRingtone());
mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_MAX);
assertEquals(AudioManager.RINGER_MODE_MAX, mAudioManager.getRingerMode());
assertTrue(mVibrationSettings.shouldVibrateForRingtone());
mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL);
assertEquals(AudioManager.RINGER_MODE_NORMAL, mAudioManager.getRingerMode());
assertTrue(mVibrationSettings.shouldVibrateForRingtone());
mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_VIBRATE);
assertEquals(AudioManager.RINGER_MODE_VIBRATE, mAudioManager.getRingerMode());
assertTrue(mVibrationSettings.shouldVibrateForRingtone());
}
@Test
public void shouldVibrateForRingtones_withAllSettingsOff_onlyVibratesForVibrateMode() {
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
setGlobalSetting(Settings.Global.APPLY_RAMPING_RINGER, 0);
mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_VIBRATE);
assertEquals(AudioManager.RINGER_MODE_VIBRATE, mAudioManager.getRingerMode());
assertTrue(mVibrationSettings.shouldVibrateForRingtone());
mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_SILENT);
assertEquals(AudioManager.RINGER_MODE_SILENT, mAudioManager.getRingerMode());
assertFalse(mVibrationSettings.shouldVibrateForRingtone());
mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_MAX);
assertEquals(AudioManager.RINGER_MODE_MAX, mAudioManager.getRingerMode());
assertFalse(mVibrationSettings.shouldVibrateForRingtone());
mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL);
assertEquals(AudioManager.RINGER_MODE_NORMAL, mAudioManager.getRingerMode());
assertFalse(mVibrationSettings.shouldVibrateForRingtone());
}
@Test
public void shouldVibrateInputDevices_returnsSettingsValue() {
setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
assertTrue(mVibrationSettings.shouldVibrateInputDevices());
setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0);
assertFalse(mVibrationSettings.shouldVibrateInputDevices());
}
@Test
public void isInZenMode_returnsSettingsValue() {
setGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
assertFalse(mVibrationSettings.isInZenMode());
setGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_NO_INTERRUPTIONS);
assertTrue(mVibrationSettings.isInZenMode());
setGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_ALARMS);
assertTrue(mVibrationSettings.isInZenMode());
setGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
assertFalse(mVibrationSettings.isInZenMode());
setGlobalSetting(Settings.Global.ZEN_MODE,
Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
assertTrue(mVibrationSettings.isInZenMode());
}
@Test
public void getDefaultIntensity_returnsIntensityFromVibratorService() {
when(mVibratorMock.getDefaultHapticFeedbackIntensity())
.thenReturn(Vibrator.VIBRATION_INTENSITY_HIGH);
when(mVibratorMock.getDefaultNotificationVibrationIntensity())
.thenReturn(Vibrator.VIBRATION_INTENSITY_MEDIUM);
when(mVibratorMock.getDefaultRingVibrationIntensity())
.thenReturn(Vibrator.VIBRATION_INTENSITY_LOW);
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_OFF);
setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_OFF);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
Vibrator.VIBRATION_INTENSITY_OFF);
assertEquals(Vibrator.VIBRATION_INTENSITY_HIGH,
mVibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_ALARM));
assertEquals(Vibrator.VIBRATION_INTENSITY_HIGH,
mVibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_TOUCH));
assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
mVibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_NOTIFICATION));
assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
mVibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_UNKNOWN));
assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
mVibrationSettings.getDefaultIntensity(
VibrationAttributes.USAGE_PHYSICAL_EMULATION));
assertEquals(Vibrator.VIBRATION_INTENSITY_LOW,
mVibrationSettings.getDefaultIntensity(VibrationAttributes.USAGE_RINGTONE));
}
@Test
public void getCurrentIntensity_returnsIntensityFromSettings() {
when(mVibratorMock.getDefaultHapticFeedbackIntensity())
.thenReturn(Vibrator.VIBRATION_INTENSITY_OFF);
when(mVibratorMock.getDefaultNotificationVibrationIntensity())
.thenReturn(Vibrator.VIBRATION_INTENSITY_OFF);
when(mVibratorMock.getDefaultRingVibrationIntensity())
.thenReturn(Vibrator.VIBRATION_INTENSITY_OFF);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
Vibrator.VIBRATION_INTENSITY_HIGH);
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_MEDIUM);
setUserSetting(Settings.System.RING_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_LOW);
assertEquals(Vibrator.VIBRATION_INTENSITY_HIGH,
mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_ALARM));
assertEquals(Vibrator.VIBRATION_INTENSITY_HIGH,
mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_TOUCH));
assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_NOTIFICATION));
assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_UNKNOWN));
assertEquals(Vibrator.VIBRATION_INTENSITY_MEDIUM,
mVibrationSettings.getCurrentIntensity(
VibrationAttributes.USAGE_PHYSICAL_EMULATION));
assertEquals(Vibrator.VIBRATION_INTENSITY_LOW,
mVibrationSettings.getCurrentIntensity(VibrationAttributes.USAGE_RINGTONE));
}
private void setUserSetting(String settingName, int value) {
Settings.System.putIntForUser(
mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
// FakeSettingsProvider don't support testing triggering ContentObserver yet.
mVibrationSettings.updateSettings();
}
private void setGlobalSetting(String settingName, int value) {
Settings.Global.putInt(mContextSpy.getContentResolver(), settingName, value);
// FakeSettingsProvider don't support testing triggering ContentObserver yet.
mVibrationSettings.updateSettings();
mAudioManager.reloadAudioSettings();
}
}

View File

@@ -91,6 +91,14 @@ public class FakeSettingsProvider extends MockContentProvider {
}
}
/**
* Creates a {@link org.junit.rules.TestRule} that makes sure {@link #clearSettingsProvider()}
* is triggered before and after each test.
*/
public static FakeSettingsProviderRule rule() {
return new FakeSettingsProviderRule();
}
/**
* This needs to be called before and after using the FakeSettingsProvider class.
*/

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.util.test;
import android.content.Context;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
/**
* JUnit Rule helps keeping test {@link FakeSettingsProvider} clean.
*
* <p>It clears {@link FakeSettingsProvider} before and after each test. Example use:
* <pre class="code"><code class="java">
* public class ExampleTest {
*
* &#064;Rule public FakeSettingsProviderRule rule = FakeSettingsProvider.rule();
*
* &#064;Test
* public void shouldDoSomething() {
* ContextResolver cr = rule.mockContentResolver(mContext);
* Settings.Global.putInt(cr, "my_setting_name", 1);
* // Test code relying on my_setting_name value using cr
* }
* }
* </code></pre>
*
* @see FakeSettingsProvider
*/
public final class FakeSettingsProviderRule implements TestRule {
/** Prevent initialization outside {@link FakeSettingsProvider}. */
FakeSettingsProviderRule() {
}
/**
* Creates a {@link MockContentResolver} that uses the {@link FakeSettingsProvider} as the
* {@link Settings#AUTHORITY} provider.
*/
public MockContentResolver mockContentResolver(Context context) {
MockContentResolver contentResolver = new MockContentResolver(context);
contentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
return contentResolver;
}
@Override
public Statement apply(Statement base, Description description) {
return new Statement() {
public void evaluate() throws Throwable {
FakeSettingsProvider.clearSettingsProvider();
try {
base.evaluate();
} finally {
FakeSettingsProvider.clearSettingsProvider();
}
}
};
}
}