Merge "Enable hardware feedback in background and disable touch feedback in silent mode"

This commit is contained in:
Lais Andrade
2021-12-02 16:34:26 +00:00
committed by Android (Google) Code Review
5 changed files with 545 additions and 401 deletions

View File

@@ -136,7 +136,7 @@ public final class VibrationAttributes implements Parcelable {
/**
* @hide
*/
@IntDef(prefix = { "FLAG_" }, value = {
@IntDef(prefix = { "FLAG_" }, flag = true, value = {
FLAG_BYPASS_INTERRUPTION_POLICY,
})
@Retention(RetentionPolicy.SOURCE)
@@ -162,7 +162,8 @@ public final class VibrationAttributes implements Parcelable {
private final int mFlags;
private final int mOriginalAudioUsage;
private VibrationAttributes(int usage, int audioUsage, int flags) {
private VibrationAttributes(@Usage int usage, @AudioAttributes.AttributeUsage int audioUsage,
@Flag int flags) {
mUsage = usage;
mOriginalAudioUsage = audioUsage;
mFlags = flags & FLAG_ALL_SUPPORTED;
@@ -172,6 +173,7 @@ public final class VibrationAttributes implements Parcelable {
* Return the vibration usage class.
* @return USAGE_CLASS_ALARM, USAGE_CLASS_FEEDBACK or USAGE_CLASS_UNKNOWN
*/
@UsageClass
public int getUsageClass() {
return mUsage & USAGE_CLASS_MASK;
}
@@ -180,6 +182,7 @@ public final class VibrationAttributes implements Parcelable {
* Return the vibration usage.
* @return one of the values that can be set in {@link Builder#setUsage(int)}
*/
@Usage
public int getUsage() {
return mUsage;
}
@@ -188,6 +191,7 @@ public final class VibrationAttributes implements Parcelable {
* Return the flags.
* @return a combined mask of all flags
*/
@Flag
public int getFlags() {
return mFlags;
}
@@ -196,7 +200,7 @@ public final class VibrationAttributes implements Parcelable {
* Check whether a flag is set
* @return true if a flag is set and false otherwise
*/
public boolean isFlagSet(int flag) {
public boolean isFlagSet(@Flag int flag) {
return (mFlags & flag) > 0;
}
@@ -206,6 +210,7 @@ public final class VibrationAttributes implements Parcelable {
* @hide
*/
@TestApi
@AudioAttributes.AttributeUsage
public int getAudioUsage() {
if (mOriginalAudioUsage != AudioAttributes.USAGE_UNKNOWN) {
// Return same audio usage set in the Builder.
@@ -292,7 +297,7 @@ public final class VibrationAttributes implements Parcelable {
}
/** @hide */
public static String usageToString(int usage) {
public static String usageToString(@Usage int usage) {
switch (usage) {
case USAGE_UNKNOWN:
return "UNKNOWN";
@@ -419,7 +424,7 @@ public final class VibrationAttributes implements Parcelable {
* {@link VibrationAttributes#USAGE_MEDIA}.
* @return the same Builder instance.
*/
public @NonNull Builder setUsage(int usage) {
public @NonNull Builder setUsage(@Usage int usage) {
mOriginalAudioUsage = AudioAttributes.USAGE_UNKNOWN;
mUsage = usage;
return this;
@@ -431,7 +436,7 @@ public final class VibrationAttributes implements Parcelable {
* @param mask Bit range that should be changed.
* @return the same Builder instance.
*/
public @NonNull Builder setFlags(int flags, int mask) {
public @NonNull Builder setFlags(@Flag int flags, int mask) {
mask &= FLAG_ALL_SUPPORTED;
mFlags = (mFlags & ~mask) | (flags & mask);
return this;

View File

@@ -53,13 +53,13 @@ final class Vibration {
IGNORED,
IGNORED_APP_OPS,
IGNORED_BACKGROUND,
IGNORED_RINGTONE,
IGNORED_UNKNOWN_VIBRATION,
IGNORED_UNSUPPORTED,
IGNORED_FOR_ALARM,
IGNORED_FOR_EXTERNAL,
IGNORED_FOR_ONGOING,
IGNORED_FOR_POWER,
IGNORED_FOR_RINGER_MODE,
IGNORED_FOR_SETTINGS,
IGNORED_SUPERSEDED,
}

View File

@@ -53,12 +53,44 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/** Controls all the system settings related to vibration. */
final class VibrationSettings {
private static final String TAG = "VibrationSettings";
/**
* Set of usages allowed for vibrations from background processes.
*
* <p>Some examples are notification, ringtone or alarm vibrations, that are allowed to vibrate
* unexpectedly as they are meant to grab the user's attention. Hardware feedback and physical
* emulation are also supported, as the trigger process might still be in the background when
* the user interaction wakes the device.
*/
private static final Set<Integer> BACKGROUND_PROCESS_USAGE_ALLOWLIST = new HashSet<>(
Arrays.asList(
USAGE_RINGTONE,
USAGE_ALARM,
USAGE_NOTIFICATION,
USAGE_COMMUNICATION_REQUEST,
USAGE_HARDWARE_FEEDBACK,
USAGE_PHYSICAL_EMULATION));
/**
* Set of usages allowed for vibrations in battery saver mode (low power).
*
* <p>Some examples are ringtone or alarm vibrations, that have high priority and should vibrate
* even when the device is saving battery.
*/
private static final Set<Integer> BATTERY_SAVER_USAGE_ALLOWLIST = new HashSet<>(
Arrays.asList(
USAGE_RINGTONE,
USAGE_ALARM,
USAGE_COMMUNICATION_REQUEST));
/** Listener for changes on vibration settings. */
interface OnVibratorSettingsChanged {
/** Callback triggered when any of the vibrator settings change. */
@@ -94,8 +126,6 @@ final class VibrationSettings {
@GuardedBy("mLock")
private boolean mApplyRampingRinger;
@GuardedBy("mLock")
private int mZenMode;
@GuardedBy("mLock")
private int mHapticFeedbackIntensity;
@GuardedBy("mLock")
private int mHardwareFeedbackIntensity;
@@ -104,7 +134,7 @@ final class VibrationSettings {
@GuardedBy("mLock")
private int mRingIntensity;
@GuardedBy("mLock")
private boolean mLowPowerMode;
private boolean mBatterySaverMode;
VibrationSettings(Context context, Handler handler) {
this(context, handler,
@@ -172,8 +202,8 @@ final class VibrationSettings {
public void onLowPowerModeChanged(PowerSaveState result) {
boolean shouldNotifyListeners;
synchronized (mLock) {
shouldNotifyListeners = result.batterySaverEnabled != mLowPowerMode;
mLowPowerMode = result.batterySaverEnabled;
shouldNotifyListeners = result.batterySaverEnabled != mBatterySaverMode;
mBatterySaverMode = result.batterySaverEnabled;
}
if (shouldNotifyListeners) {
notifyListeners();
@@ -187,7 +217,6 @@ final class VibrationSettings {
registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES));
registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING));
registerSettingsObserver(Settings.System.getUriFor(Settings.System.APPLY_RAMPING_RINGER));
registerSettingsObserver(Settings.Global.getUriFor(Settings.Global.ZEN_MODE));
registerSettingsObserver(
Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_INTENSITY));
registerSettingsObserver(
@@ -299,71 +328,78 @@ final class VibrationSettings {
return mFallbackEffects.get(effectId);
}
/**
* Return {@code true} if the device should vibrate for current ringer mode.
*
* <p>This checks the current {@link AudioManager#getRingerModeInternal()} against user settings
* for ringtone usage only. All other usages are allowed independently of ringer mode.
*/
public boolean shouldVibrateForRingerMode(int usageHint) {
if (usageHint != USAGE_RINGTONE) {
return true;
}
synchronized (mLock) {
if (mAudioManager == null) {
return false;
}
int ringerMode = mAudioManager.getRingerModeInternal();
if (mVibrateWhenRinging) {
return ringerMode != AudioManager.RINGER_MODE_SILENT;
} else if (mApplyRampingRinger) {
return ringerMode != AudioManager.RINGER_MODE_SILENT;
} else {
return ringerMode == AudioManager.RINGER_MODE_VIBRATE;
}
}
}
/**
* 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) || isClassAlarm(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) {
synchronized (mLock) {
return !mLowPowerMode || usageHint == USAGE_RINGTONE || usageHint == USAGE_ALARM
|| usageHint == USAGE_COMMUNICATION_REQUEST;
}
}
/** 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;
/**
* Check if given vibration should be ignored by the service.
*
* @return One of Vibration.Status.IGNORED_* values if the vibration should be ignored,
* null otherwise.
*/
@Nullable
public Vibration.Status shouldIgnoreVibration(int uid, VibrationAttributes attrs) {
final int usage = attrs.getUsage();
synchronized (mLock) {
if (!mUidObserver.isUidForeground(uid)
&& !BACKGROUND_PROCESS_USAGE_ALLOWLIST.contains(usage)) {
return Vibration.Status.IGNORED_BACKGROUND;
}
if (mBatterySaverMode && !BATTERY_SAVER_USAGE_ALLOWLIST.contains(usage)) {
return Vibration.Status.IGNORED_FOR_POWER;
}
int intensity = getCurrentIntensity(usage);
if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
return Vibration.Status.IGNORED_FOR_SETTINGS;
}
if (!shouldVibrateForRingerModeLocked(usage)) {
return Vibration.Status.IGNORED_FOR_RINGER_MODE;
}
}
return null;
}
private static boolean isClassAlarm(int usageHint) {
return (usageHint & VibrationAttributes.USAGE_CLASS_MASK)
== VibrationAttributes.USAGE_CLASS_ALARM;
/**
* Return {@code true} if the device should vibrate for current ringer mode.
*
* <p>This checks the current {@link AudioManager#getRingerModeInternal()} against user settings
* for touch and ringtone usages only. All other usages are allowed by this method.
*/
@GuardedBy("mLock")
private boolean shouldVibrateForRingerModeLocked(int usageHint) {
// If audio manager was not loaded yet then assume most restrictive mode.
int ringerMode = (mAudioManager == null)
? AudioManager.RINGER_MODE_SILENT
: mAudioManager.getRingerModeInternal();
switch (usageHint) {
case USAGE_TOUCH:
// Touch feedback disabled when phone is on silent mode.
return ringerMode != AudioManager.RINGER_MODE_SILENT;
case USAGE_RINGTONE:
switch (ringerMode) {
case AudioManager.RINGER_MODE_SILENT:
return false;
case AudioManager.RINGER_MODE_VIBRATE:
return true;
default:
// Ringtone vibrations also depend on 2 other settings:
return mVibrateWhenRinging || mApplyRampingRinger;
}
default:
// All other usages ignore ringer mode settings.
return true;
}
}
/** Updates all vibration settings and triggers registered listeners. */
public void updateSettings() {
@VisibleForTesting
void updateSettings() {
synchronized (mLock) {
mVibrateWhenRinging = getSystemSetting(Settings.System.VIBRATE_WHEN_RINGING, 0) != 0;
mApplyRampingRinger = getSystemSetting(Settings.System.APPLY_RAMPING_RINGER, 0) != 0;
@@ -378,7 +414,6 @@ final class VibrationSettings {
mRingIntensity = getSystemSetting(Settings.System.RING_VIBRATION_INTENSITY,
getDefaultIntensity(USAGE_RINGTONE));
mVibrateInputDevices = getSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0) > 0;
mZenMode = getGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
}
notifyListeners();
}
@@ -399,31 +434,33 @@ final class VibrationSettings {
@Override
public String toString() {
return "VibrationSettings{"
+ "mVibrateInputDevices=" + mVibrateInputDevices
+ ", mVibrateWhenRinging=" + mVibrateWhenRinging
+ ", mApplyRampingRinger=" + mApplyRampingRinger
+ ", mLowPowerMode=" + mLowPowerMode
+ ", mZenMode=" + Settings.Global.zenModeToString(mZenMode)
+ ", mProcStatesCache=" + mUidObserver.mProcStatesCache
+ ", mHapticChannelMaxVibrationAmplitude=" + getHapticChannelMaxVibrationAmplitude()
+ ", mRampStepDuration=" + mRampStepDuration
+ ", mRampDownDuration=" + mRampDownDuration
+ ", mHardwareHapticFeedbackIntensity="
+ intensityToString(getCurrentIntensity(USAGE_HARDWARE_FEEDBACK))
+ ", mHapticFeedbackIntensity="
+ intensityToString(getCurrentIntensity(USAGE_TOUCH))
+ ", mHapticFeedbackDefaultIntensity="
+ intensityToString(getDefaultIntensity(USAGE_TOUCH))
+ ", mNotificationIntensity="
+ intensityToString(getCurrentIntensity(USAGE_NOTIFICATION))
+ ", mNotificationDefaultIntensity="
+ intensityToString(getDefaultIntensity(USAGE_NOTIFICATION))
+ ", mRingIntensity="
+ intensityToString(getCurrentIntensity(USAGE_RINGTONE))
+ ", mRingDefaultIntensity="
+ intensityToString(getDefaultIntensity(USAGE_RINGTONE))
+ '}';
synchronized (mLock) {
return "VibrationSettings{"
+ "mVibrateInputDevices=" + mVibrateInputDevices
+ ", mVibrateWhenRinging=" + mVibrateWhenRinging
+ ", mApplyRampingRinger=" + mApplyRampingRinger
+ ", mBatterySaverMode=" + mBatterySaverMode
+ ", mProcStatesCache=" + mUidObserver.mProcStatesCache
+ ", mHapticChannelMaxVibrationAmplitude="
+ getHapticChannelMaxVibrationAmplitude()
+ ", mRampStepDuration=" + mRampStepDuration
+ ", mRampDownDuration=" + mRampDownDuration
+ ", mHardwareHapticFeedbackIntensity="
+ intensityToString(getCurrentIntensity(USAGE_HARDWARE_FEEDBACK))
+ ", mHapticFeedbackIntensity="
+ intensityToString(getCurrentIntensity(USAGE_TOUCH))
+ ", mHapticFeedbackDefaultIntensity="
+ intensityToString(getDefaultIntensity(USAGE_TOUCH))
+ ", mNotificationIntensity="
+ intensityToString(getCurrentIntensity(USAGE_NOTIFICATION))
+ ", mNotificationDefaultIntensity="
+ intensityToString(getDefaultIntensity(USAGE_NOTIFICATION))
+ ", mRingIntensity="
+ intensityToString(getCurrentIntensity(USAGE_RINGTONE))
+ ", mRingDefaultIntensity="
+ intensityToString(getDefaultIntensity(USAGE_RINGTONE))
+ '}';
}
}
/** Write current settings into given {@link ProtoOutputStream}. */
@@ -480,10 +517,6 @@ final class VibrationSettings {
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,

View File

@@ -46,7 +46,6 @@ import android.os.ShellCommand;
import android.os.Trace;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.VibratorInfo;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.VibrationEffectSegment;
@@ -394,13 +393,13 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if (DEBUG) {
Slog.d(TAG, "Starting vibrate for vibration " + vib.id);
}
Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(vib);
if (ignoreStatus != null) {
endVibrationLocked(vib, ignoreStatus);
return vib;
Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(
vib.uid, vib.opPkg, vib.attrs);
if (ignoreStatus == null) {
ignoreStatus = shouldIgnoreVibrationForOngoingLocked(vib);
}
ignoreStatus = shouldIgnoreVibrationForCurrentLocked(vib);
if (ignoreStatus != null) {
endVibrationLocked(vib, ignoreStatus);
return vib;
@@ -453,8 +452,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
&& shouldCancelVibration(
mCurrentExternalVibration.externalVibration.getVibrationAttributes(),
usageFilter)) {
mCurrentExternalVibration.end(Vibration.Status.CANCELLED);
mVibratorManagerRecords.record(mCurrentExternalVibration);
endVibrationLocked(mCurrentExternalVibration, Vibration.Status.CANCELLED);
mCurrentExternalVibration.externalVibration.mute();
mCurrentExternalVibration = null;
setExternalControl(false);
@@ -482,15 +480,76 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
try {
if (isDumpProto) {
mVibratorManagerRecords.dumpProto(fd);
dumpProto(fd);
} else {
mVibratorManagerRecords.dumpText(pw);
dumpText(pw);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private void dumpText(PrintWriter pw) {
if (DEBUG) {
Slog.d(TAG, "Dumping vibrator manager service to text...");
}
synchronized (mLock) {
pw.println("Vibrator Manager Service:");
pw.println(" mVibrationSettings:");
pw.println(" " + mVibrationSettings);
pw.println();
pw.println(" mVibratorControllers:");
for (int i = 0; i < mVibrators.size(); i++) {
pw.println(" " + mVibrators.valueAt(i));
}
pw.println();
pw.println(" mCurrentVibration:");
pw.println(" " + (mCurrentVibration == null
? null : mCurrentVibration.getVibration().getDebugInfo()));
pw.println();
pw.println(" mNextVibration:");
pw.println(" " + (mNextVibration == null
? null : mNextVibration.getVibration().getDebugInfo()));
pw.println();
pw.println(" mCurrentExternalVibration:");
pw.println(" " + (mCurrentExternalVibration == null
? null : mCurrentExternalVibration.getDebugInfo()));
pw.println();
}
mVibratorManagerRecords.dumpText(pw);
}
synchronized void dumpProto(FileDescriptor fd) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
if (DEBUG) {
Slog.d(TAG, "Dumping vibrator manager service to proto...");
}
synchronized (mLock) {
mVibrationSettings.dumpProto(proto);
if (mCurrentVibration != null) {
mCurrentVibration.getVibration().getDebugInfo().dumpProto(proto,
VibratorManagerServiceDumpProto.CURRENT_VIBRATION);
}
if (mCurrentExternalVibration != null) {
mCurrentExternalVibration.getDebugInfo().dumpProto(proto,
VibratorManagerServiceDumpProto.CURRENT_EXTERNAL_VIBRATION);
}
boolean isVibrating = false;
boolean isUnderExternalControl = false;
for (int i = 0; i < mVibrators.size(); i++) {
proto.write(VibratorManagerServiceDumpProto.VIBRATOR_IDS, mVibrators.keyAt(i));
isVibrating |= mVibrators.valueAt(i).isVibrating();
isUnderExternalControl |= mVibrators.valueAt(i).isUnderExternalControl();
}
proto.write(VibratorManagerServiceDumpProto.IS_VIBRATING, isVibrating);
proto.write(VibratorManagerServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL,
isUnderExternalControl);
}
mVibratorManagerRecords.dumpProto(proto);
proto.flush();
}
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback cb, ResultReceiver resultReceiver) {
@@ -515,8 +574,15 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
return;
}
if (inputDevicesChanged || !mVibrationSettings.shouldVibrateForPowerMode(
mCurrentVibration.getVibration().attrs.getUsage())) {
Vibration vib = mCurrentVibration.getVibration();
Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(
vib.uid, vib.opPkg, vib.attrs);
if (inputDevicesChanged || (ignoreStatus != null)) {
if (DEBUG) {
Slog.d(TAG, "Canceling vibration because settings changed: "
+ (inputDevicesChanged ? "input devices changed" : ignoreStatus));
}
mCurrentVibration.cancel();
}
}
@@ -602,15 +668,56 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
@GuardedBy("mLock")
private void endVibrationLocked(Vibration vib, Vibration.Status status) {
vib.end(status);
logVibrationStatus(vib.uid, vib.attrs, status);
mVibratorManagerRecords.record(vib);
}
@GuardedBy("mLock")
private void endVibrationLocked(ExternalVibrationHolder vib, Vibration.Status status) {
vib.end(status);
logVibrationStatus(vib.externalVibration.getUid(),
vib.externalVibration.getVibrationAttributes(), status);
mVibratorManagerRecords.record(vib);
}
private void logVibrationStatus(int uid, VibrationAttributes attrs, Vibration.Status status) {
switch (status) {
case IGNORED_BACKGROUND:
Slog.e(TAG, "Ignoring incoming vibration as process with"
+ " uid= " + uid + " is background," + " attrs= " + attrs);
break;
case IGNORED_ERROR_APP_OPS:
Slog.w(TAG, "Would be an error: vibrate from uid " + uid);
break;
case IGNORED_FOR_ALARM:
if (DEBUG) {
Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
}
break;
case IGNORED_FOR_EXTERNAL:
if (DEBUG) {
Slog.d(TAG, "Ignoring incoming vibration for current external vibration");
}
break;
case IGNORED_FOR_ONGOING:
if (DEBUG) {
Slog.d(TAG, "Ignoring incoming vibration in favor of repeating vibration");
}
break;
case IGNORED_FOR_RINGER_MODE:
if (DEBUG) {
Slog.d(TAG, "Ignoring incoming vibration because of ringer mode, attrs="
+ attrs);
}
break;
default:
if (DEBUG) {
Slog.d(TAG, "Vibration for uid=" + uid + " and with attrs=" + attrs
+ " ended with status " + status);
}
}
}
@GuardedBy("mLock")
private void reportFinishedVibrationLocked(Vibration.Status status) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
@@ -651,32 +758,36 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
/**
* Check if given vibration should be ignored in favour of one of the vibrations currently
* running on the same vibrators.
* Check if given vibration should be ignored by this service because of the ongoing vibration.
*
* @return One of Vibration.Status.IGNORED_* values if the vibration should be ignored.
* @return One of Vibration.Status.IGNORED_* values if the vibration should be ignored, null
* otherwise.
*/
@GuardedBy("mLock")
@Nullable
private Vibration.Status shouldIgnoreVibrationForCurrentLocked(Vibration vibration) {
if (vibration.isRepeating()) {
// Repeating vibrations always take precedence.
private Vibration.Status shouldIgnoreVibrationForOngoingLocked(Vibration vib) {
if (mCurrentExternalVibration != null) {
// If something has external control of the vibrator, assume that it's more important.
return Vibration.Status.IGNORED_FOR_EXTERNAL;
}
if (mCurrentVibration == null || vib.isRepeating()) {
// Incoming repeating vibrations always take precedence over ongoing vibrations.
return null;
}
if (mCurrentVibration != null && !mCurrentVibration.getVibration().hasEnded()) {
if (mCurrentVibration.getVibration().attrs.getUsage()
== VibrationAttributes.USAGE_ALARM) {
if (DEBUG) {
Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
}
return Vibration.Status.IGNORED_FOR_ALARM;
}
if (mCurrentVibration.getVibration().isRepeating()) {
if (DEBUG) {
Slog.d(TAG, "Ignoring incoming vibration in favor of repeating vibration");
}
return Vibration.Status.IGNORED_FOR_ONGOING;
}
Vibration currentVibration = mCurrentVibration.getVibration();
if (currentVibration.hasEnded()) {
// Current vibration is finishing up, it should not block incoming vibrations.
return null;
}
if (currentVibration.attrs.getUsage() == VibrationAttributes.USAGE_ALARM) {
return Vibration.Status.IGNORED_FOR_ALARM;
}
if (currentVibration.isRepeating()) {
return Vibration.Status.IGNORED_FOR_ONGOING;
}
return null;
}
@@ -684,57 +795,16 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
/**
* Check if given vibration should be ignored by this service.
*
* @return One of Vibration.Status.IGNORED_* values if the vibration should be ignored.
* @see #shouldIgnoreVibrationLocked(int, String, VibrationAttributes)
*/
@GuardedBy("mLock")
@Nullable
private Vibration.Status shouldIgnoreVibrationLocked(Vibration vib) {
// If something has external control of the vibrator, assume that it's more important.
if (mCurrentExternalVibration != null) {
if (DEBUG) {
Slog.d(TAG, "Ignoring incoming vibration for current external vibration");
}
return Vibration.Status.IGNORED_FOR_EXTERNAL;
}
if (!mVibrationSettings.shouldVibrateForUid(vib.uid, vib.attrs.getUsage())) {
Slog.e(TAG, "Ignoring incoming vibration as process with"
+ " uid= " + vib.uid + " is background,"
+ " attrs= " + vib.attrs);
return Vibration.Status.IGNORED_BACKGROUND;
}
return shouldIgnoreVibrationLocked(vib.uid, vib.opPkg, vib.attrs);
}
/**
* Check if a vibration with given {@code uid}, {@code opPkg} and {@code attrs} should be
* ignored by this service.
*
* @param uid The user id of this vibration
* @param opPkg The package name of this vibration
* @param attrs The attributes of this vibration
* @return One of Vibration.Status.IGNORED_* values if the vibration should be ignored.
* @return One of Vibration.Status.IGNORED_* values if the vibration should be ignored, null
* otherwise.
*/
@GuardedBy("mLock")
@Nullable
private Vibration.Status shouldIgnoreVibrationLocked(int uid, String opPkg,
VibrationAttributes attrs) {
if (!mVibrationSettings.shouldVibrateForPowerMode(attrs.getUsage())) {
return Vibration.Status.IGNORED_FOR_POWER;
}
int intensity = mVibrationSettings.getCurrentIntensity(attrs.getUsage());
if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
return Vibration.Status.IGNORED_FOR_SETTINGS;
}
if (!mVibrationSettings.shouldVibrateForRingerMode(attrs.getUsage())) {
if (DEBUG) {
Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones");
}
return Vibration.Status.IGNORED_RINGTONE;
Vibration.Status statusFromSettings = mVibrationSettings.shouldIgnoreVibration(uid, attrs);
if (statusFromSettings != null) {
return statusFromSettings;
}
int mode = checkAppOpModeLocked(uid, opPkg, attrs);
@@ -742,7 +812,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
if (mode == AppOpsManager.MODE_ERRORED) {
// We might be getting calls from within system_server, so we don't actually
// want to throw a SecurityException here.
Slog.w(TAG, "Would be an error: vibrate from uid " + uid);
return Vibration.Status.IGNORED_ERROR_APP_OPS;
} else {
return Vibration.Status.IGNORED_APP_OPS;
@@ -1236,21 +1305,18 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
/** Keep records of vibrations played and provide debug information for this service. */
private final class VibratorManagerRecords {
@GuardedBy("mLock")
private static final class VibratorManagerRecords {
private final SparseArray<LinkedList<Vibration.DebugInfo>> mPreviousVibrations =
new SparseArray<>();
@GuardedBy("mLock")
private final LinkedList<Vibration.DebugInfo> mPreviousExternalVibrations =
new LinkedList<>();
private final int mPreviousVibrationsLimit;
private VibratorManagerRecords(int limit) {
VibratorManagerRecords(int limit) {
mPreviousVibrationsLimit = limit;
}
@GuardedBy("mLock")
void record(Vibration vib) {
synchronized void record(Vibration vib) {
int usage = vib.attrs.getUsage();
if (!mPreviousVibrations.contains(usage)) {
mPreviousVibrations.put(usage, new LinkedList<>());
@@ -1258,122 +1324,67 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
record(mPreviousVibrations.get(usage), vib.getDebugInfo());
}
@GuardedBy("mLock")
void record(ExternalVibrationHolder vib) {
synchronized void record(ExternalVibrationHolder vib) {
record(mPreviousExternalVibrations, vib.getDebugInfo());
}
@GuardedBy("mLock")
void record(LinkedList<Vibration.DebugInfo> records, Vibration.DebugInfo info) {
synchronized void record(LinkedList<Vibration.DebugInfo> records,
Vibration.DebugInfo info) {
if (records.size() > mPreviousVibrationsLimit) {
records.removeFirst();
}
records.addLast(info);
}
void dumpText(PrintWriter pw) {
pw.println("Vibrator Manager Service:");
synchronized (mLock) {
if (DEBUG) {
Slog.d(TAG, "Dumping vibrator manager service to text...");
}
pw.println(" mVibrationSettings:");
pw.println(" " + mVibrationSettings);
synchronized void dumpText(PrintWriter pw) {
for (int i = 0; i < mPreviousVibrations.size(); i++) {
pw.println();
pw.println(" mVibratorControllers:");
for (int i = 0; i < mVibrators.size(); i++) {
pw.println(" " + mVibrators.valueAt(i));
}
pw.println();
pw.println(" mCurrentVibration:");
pw.println(" " + (mCurrentVibration == null
? null : mCurrentVibration.getVibration().getDebugInfo()));
pw.println();
pw.println(" mNextVibration:");
pw.println(" " + (mNextVibration == null
? null : mNextVibration.getVibration().getDebugInfo()));
pw.println();
pw.println(" mCurrentExternalVibration:");
pw.println(" " + (mCurrentExternalVibration == null
? null : mCurrentExternalVibration.getDebugInfo()));
pw.println();
for (int i = 0; i < mPreviousVibrations.size(); i++) {
pw.println();
pw.print(" Previous vibrations for usage ");
pw.print(VibrationAttributes.usageToString(mPreviousVibrations.keyAt(i)));
pw.println(":");
for (Vibration.DebugInfo info : mPreviousVibrations.valueAt(i)) {
pw.println(" " + info);
}
}
pw.println();
pw.println(" Previous external vibrations:");
for (Vibration.DebugInfo info : mPreviousExternalVibrations) {
pw.print(" Previous vibrations for usage ");
pw.print(VibrationAttributes.usageToString(mPreviousVibrations.keyAt(i)));
pw.println(":");
for (Vibration.DebugInfo info : mPreviousVibrations.valueAt(i)) {
pw.println(" " + info);
}
}
pw.println();
pw.println(" Previous external vibrations:");
for (Vibration.DebugInfo info : mPreviousExternalVibrations) {
pw.println(" " + info);
}
}
synchronized void dumpProto(FileDescriptor fd) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
synchronized (mLock) {
if (DEBUG) {
Slog.d(TAG, "Dumping vibrator manager service to proto...");
synchronized void dumpProto(ProtoOutputStream proto) {
for (int i = 0; i < mPreviousVibrations.size(); i++) {
long fieldId;
switch (mPreviousVibrations.keyAt(i)) {
case VibrationAttributes.USAGE_RINGTONE:
fieldId = VibratorManagerServiceDumpProto.PREVIOUS_RING_VIBRATIONS;
break;
case VibrationAttributes.USAGE_NOTIFICATION:
fieldId = VibratorManagerServiceDumpProto
.PREVIOUS_NOTIFICATION_VIBRATIONS;
break;
case VibrationAttributes.USAGE_ALARM:
fieldId = VibratorManagerServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS;
break;
default:
fieldId = VibratorManagerServiceDumpProto.PREVIOUS_VIBRATIONS;
}
mVibrationSettings.dumpProto(proto);
if (mCurrentVibration != null) {
mCurrentVibration.getVibration().getDebugInfo().dumpProto(proto,
VibratorManagerServiceDumpProto.CURRENT_VIBRATION);
}
if (mCurrentExternalVibration != null) {
mCurrentExternalVibration.getDebugInfo().dumpProto(proto,
VibratorManagerServiceDumpProto.CURRENT_EXTERNAL_VIBRATION);
}
boolean isVibrating = false;
boolean isUnderExternalControl = false;
for (int i = 0; i < mVibrators.size(); i++) {
proto.write(VibratorManagerServiceDumpProto.VIBRATOR_IDS, mVibrators.keyAt(i));
isVibrating |= mVibrators.valueAt(i).isVibrating();
isUnderExternalControl |= mVibrators.valueAt(i).isUnderExternalControl();
}
proto.write(VibratorManagerServiceDumpProto.IS_VIBRATING, isVibrating);
proto.write(VibratorManagerServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL,
isUnderExternalControl);
for (int i = 0; i < mPreviousVibrations.size(); i++) {
long fieldId;
switch (mPreviousVibrations.keyAt(i)) {
case VibrationAttributes.USAGE_RINGTONE:
fieldId = VibratorManagerServiceDumpProto.PREVIOUS_RING_VIBRATIONS;
break;
case VibrationAttributes.USAGE_NOTIFICATION:
fieldId = VibratorManagerServiceDumpProto
.PREVIOUS_NOTIFICATION_VIBRATIONS;
break;
case VibrationAttributes.USAGE_ALARM:
fieldId = VibratorManagerServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS;
break;
default:
fieldId = VibratorManagerServiceDumpProto.PREVIOUS_VIBRATIONS;
}
for (Vibration.DebugInfo info : mPreviousVibrations.valueAt(i)) {
info.dumpProto(proto, fieldId);
}
}
for (Vibration.DebugInfo info : mPreviousExternalVibrations) {
info.dumpProto(proto,
VibratorManagerServiceDumpProto.PREVIOUS_EXTERNAL_VIBRATIONS);
for (Vibration.DebugInfo info : mPreviousVibrations.valueAt(i)) {
info.dumpProto(proto, fieldId);
}
}
proto.flush();
for (Vibration.DebugInfo info : mPreviousExternalVibrations) {
info.dumpProto(proto,
VibratorManagerServiceDumpProto.PREVIOUS_EXTERNAL_VIBRATIONS);
}
}
}
/** Clears mNextVibration if set, ending it cleanly */
@GuardedBy("mLock")
private void clearNextVibrationLocked(Vibration.Status endStatus) {
if (mNextVibration != null) {
endVibrationLocked(mNextVibration.getVibration(), endStatus);
@@ -1482,6 +1493,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub {
}
}
@GuardedBy("mLock")
private void stopExternalVibrateLocked(Vibration.Status status) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "stopExternalVibrateLocked");
try {

View File

@@ -32,6 +32,7 @@ import static android.os.Vibrator.VIBRATION_INTENSITY_OFF;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@@ -52,6 +53,7 @@ import android.os.Handler;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.UserHandle;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
@@ -124,10 +126,11 @@ public class VibrationSettingsTest {
new Handler(mTestLooper.getLooper()));
mVibrationSettings.onSystemReady();
// Simulate System defaults.
setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0);
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
setGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
}
@After
@@ -142,13 +145,12 @@ public class VibrationSettingsTest {
setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
setGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_ALARMS);
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
setUserSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
verify(mListenerMock, times(8)).onChange();
verify(mListenerMock, times(7)).onChange();
}
@Test
@@ -173,126 +175,242 @@ public class VibrationSettingsTest {
verifyNoMoreInteractions(mListenerMock);
setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
setGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_ALARMS);
}
@Test
public void shouldVibrateForRingerMode_beforeSystemReady_returnsFalseOnlyForRingtone() {
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
setRingerMode(AudioManager.RINGER_MODE_MAX);
VibrationSettings vibrationSettings = new VibrationSettings(mContextSpy,
new Handler(mTestLooper.getLooper()));
assertFalse(vibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_ALARM));
assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_TOUCH));
assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_NOTIFICATION));
assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_COMMUNICATION_REQUEST));
assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_HARDWARE_FEEDBACK));
}
@Test
public void shouldVibrateForRingerMode_withoutRingtoneUsage_returnsTrue() {
assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_ALARM));
assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_TOUCH));
assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_NOTIFICATION));
assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_COMMUNICATION_REQUEST));
assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_HARDWARE_FEEDBACK));
}
@Test
public void shouldVibrateForRingerMode_withVibrateWhenRinging_ignoreSettingsForSilentMode() {
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
setRingerMode(AudioManager.RINGER_MODE_SILENT);
assertFalse(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_MAX);
assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
}
@Test
public void shouldVibrateForRingerMode_withApplyRampingRinger_ignoreSettingsForSilentMode() {
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1);
setRingerMode(AudioManager.RINGER_MODE_SILENT);
assertFalse(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_MAX);
assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
}
@Test
public void shouldVibrateForRingerMode_withAllSettingsOff_onlyVibratesForVibrateMode() {
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_SILENT);
assertFalse(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_MAX);
assertFalse(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
assertFalse(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
}
@Test
public void shouldVibrateForUid_withForegroundOnlyUsage_returnsTrueWhInForeground() {
assertTrue(mVibrationSettings.shouldVibrateForUid(UID, USAGE_TOUCH));
public void shouldIgnoreVibration_fromBackground_doesNotIgnoreUsagesFromAllowlist() {
int[] expectedAllowedVibrations = new int[] {
USAGE_RINGTONE,
USAGE_ALARM,
USAGE_NOTIFICATION,
USAGE_COMMUNICATION_REQUEST,
USAGE_HARDWARE_FEEDBACK,
USAGE_PHYSICAL_EMULATION,
};
mVibrationSettings.mUidObserver.onUidStateChanged(
UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
assertFalse(mVibrationSettings.shouldVibrateForUid(UID, USAGE_TOUCH));
for (int usage : expectedAllowedVibrations) {
assertNull("Error for usage " + VibrationAttributes.usageToString(usage),
mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(usage)));
}
}
@Test
public void shouldVibrateForUid_withBackgroundAllowedUsage_returnTrue() {
public void shouldIgnoreVibration_fromBackground_ignoresUsagesNotInAllowlist() {
int[] expectedIgnoredVibrations = new int[] {
USAGE_TOUCH,
USAGE_UNKNOWN,
};
mVibrationSettings.mUidObserver.onUidStateChanged(
UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
assertTrue(mVibrationSettings.shouldVibrateForUid(UID, USAGE_ALARM));
assertTrue(mVibrationSettings.shouldVibrateForUid(UID, USAGE_COMMUNICATION_REQUEST));
assertTrue(mVibrationSettings.shouldVibrateForUid(UID, USAGE_NOTIFICATION));
assertTrue(mVibrationSettings.shouldVibrateForUid(UID, USAGE_RINGTONE));
for (int usage : expectedIgnoredVibrations) {
assertEquals("Error for usage " + VibrationAttributes.usageToString(usage),
Vibration.Status.IGNORED_BACKGROUND,
mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(usage)));
}
}
@Test
public void shouldVibrateForPowerMode_withLowPowerAndAllowedUsage_returnTrue() {
public void shouldIgnoreVibration_fromForeground_allowsAnyUsage() {
mVibrationSettings.mUidObserver.onUidStateChanged(
UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_TOUCH)));
assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_ALARM)));
}
@Test
public void shouldIgnoreVibration_inBatterySaverMode_doesNotIgnoreUsagesFromAllowlist() {
int[] expectedAllowedVibrations = new int[] {
USAGE_RINGTONE,
USAGE_ALARM,
USAGE_COMMUNICATION_REQUEST,
};
mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
assertTrue(mVibrationSettings.shouldVibrateForPowerMode(USAGE_ALARM));
assertTrue(mVibrationSettings.shouldVibrateForPowerMode(USAGE_RINGTONE));
assertTrue(mVibrationSettings.shouldVibrateForPowerMode(USAGE_COMMUNICATION_REQUEST));
for (int usage : expectedAllowedVibrations) {
assertNull("Error for usage " + VibrationAttributes.usageToString(usage),
mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(usage)));
}
}
@Test
public void shouldVibrateForPowerMode_withRestrictedUsage_returnsFalseWhileInLowPowerMode() {
public void shouldIgnoreVibration_inBatterySaverMode_ignoresUsagesNotInAllowlist() {
int[] expectedIgnoredVibrations = new int[] {
USAGE_NOTIFICATION,
USAGE_HARDWARE_FEEDBACK,
USAGE_PHYSICAL_EMULATION,
USAGE_TOUCH,
USAGE_UNKNOWN,
};
mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
for (int usage : expectedIgnoredVibrations) {
assertEquals("Error for usage " + VibrationAttributes.usageToString(usage),
Vibration.Status.IGNORED_FOR_POWER,
mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(usage)));
}
}
@Test
public void shouldIgnoreVibration_notInBatterySaverMode_allowsAnyUsage() {
mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
assertTrue(mVibrationSettings.shouldVibrateForPowerMode(USAGE_TOUCH));
assertTrue(mVibrationSettings.shouldVibrateForPowerMode(USAGE_NOTIFICATION));
assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_TOUCH)));
assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_COMMUNICATION_REQUEST)));
}
mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
@Test
public void shouldIgnoreVibration_withRingerModeSilent_ignoresRingtoneAndTouch() {
// Vibrating settings on are overruled by ringer mode.
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
setRingerMode(AudioManager.RINGER_MODE_SILENT);
assertFalse(mVibrationSettings.shouldVibrateForPowerMode(USAGE_TOUCH));
assertFalse(mVibrationSettings.shouldVibrateForPowerMode(USAGE_NOTIFICATION));
assertEquals(Vibration.Status.IGNORED_FOR_RINGER_MODE,
mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_RINGTONE)));
assertEquals(Vibration.Status.IGNORED_FOR_RINGER_MODE,
mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_TOUCH)));
assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_COMMUNICATION_REQUEST)));
assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_HARDWARE_FEEDBACK)));
}
@Test
public void shouldIgnoreVibration_withRingerModeVibrate_allowsAllVibrations() {
// Vibrating settings off are overruled by ringer mode.
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_TOUCH)));
assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_PHYSICAL_EMULATION)));
assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_RINGTONE)));
}
@Test
public void shouldIgnoreVibration_withRingerModeNormalAndRingSettingsOff_ignoresRingtoneOnly() {
// Vibrating settings off are respected for normal ringer mode.
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
assertEquals(Vibration.Status.IGNORED_FOR_RINGER_MODE,
mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_RINGTONE)));
assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_TOUCH)));
assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_NOTIFICATION)));
}
@Test
public void shouldIgnoreVibration_withRingerModeNormalAndRingSettingsOn_allowsAllVibrations() {
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_TOUCH)));
assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_RINGTONE)));
assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_ALARM)));
}
@Test
public void shouldIgnoreVibration_withRingerModeNormalAndRampingRingerOn_allowsAllVibrations() {
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1);
setRingerMode(AudioManager.RINGER_MODE_NORMAL);
assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_TOUCH)));
assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_RINGTONE)));
assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_COMMUNICATION_REQUEST)));
}
@Test
public void shouldIgnoreVibration_withHapticFeedbackSettingsOff_ignoresTouchVibration() {
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS,
mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_TOUCH)));
assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_RINGTONE)));
assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_HARDWARE_FEEDBACK)));
}
@Test
public void shouldIgnoreVibration_withHardwareFeedbackSettingsOff_ignoresHardwareVibrations() {
setUserSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS,
mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_HARDWARE_FEEDBACK)));
assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS,
mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_PHYSICAL_EMULATION)));
assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_TOUCH)));
assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_NOTIFICATION)));
}
@Test
public void shouldIgnoreVibration_withNotificationSettingsOff_ignoresNotificationVibrations() {
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS,
mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_NOTIFICATION)));
assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_ALARM)));
assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_RINGTONE)));
}
@Test
public void shouldIgnoreVibration_withRingSettingsOff_ignoresRingtoneVibrations() {
// Vibrating settings on are overruled by ring intensity setting.
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1);
setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS,
mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_RINGTONE)));
assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_NOTIFICATION)));
assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_ALARM)));
assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
VibrationAttributes.createForUsage(USAGE_TOUCH)));
}
@Test
@@ -304,24 +422,6 @@ public class VibrationSettingsTest {
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_beforeSystemReady_returnsMediumToAllExceptAlarm() {
mFakeVibrator.setDefaultHapticFeedbackIntensity(VIBRATION_INTENSITY_HIGH);
@@ -464,12 +564,6 @@ public class VibrationSettingsTest {
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();
}
private void setRingerMode(int ringerMode) {
mAudioManager.setRingerModeInternal(ringerMode);
assertEquals(ringerMode, mAudioManager.getRingerModeInternal());