diff options
| author | 2021-11-23 14:56:11 +0000 | |
|---|---|---|
| committer | 2021-12-02 12:09:26 +0000 | |
| commit | 27ca44272fa75cbd4050a3ae2510f82226c3a1ac (patch) | |
| tree | aee266044fd6f1608443c8ac3f5d174d27220e49 | |
| parent | 502ab7764e25ca8b40a274e2aef31aea84c82e86 (diff) | |
Enable hardware feedback in background and disable touch feedback in silent mode
Move method that validates all settings applied to a incoming vibration
into VibrationSettings. Modify battery-saver mode and backgound process
validation to use allowlist of usage filters.
Fix: 207362379
Fix: 165478128
Test: VibrationSettingsTest
Change-Id: I8e80c29a872aa000986ed5dbdd169d5550978b66
5 files changed, 512 insertions, 368 deletions
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java index 9612ca6addcd..58315736f37f 100644 --- a/core/java/android/os/VibrationAttributes.java +++ b/core/java/android/os/VibrationAttributes.java @@ -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; diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java index ddac9cd84d05..1d6e1585872d 100644 --- a/services/core/java/com/android/server/vibrator/Vibration.java +++ b/services/core/java/com/android/server/vibrator/Vibration.java @@ -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, } diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java index f82f99d6a253..1ee115dd28f2 100644 --- a/services/core/java/com/android/server/vibrator/VibrationSettings.java +++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java @@ -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 input devices should vibrate instead of this device. */ + public boolean shouldVibrateInputDevices() { + return mVibrateInputDevices; + } + /** - * Return {@code true} if the device should vibrate for current ringer mode. + * Check if given vibration should be ignored by the service. * - * <p>This checks the current {@link AudioManager#getRingerModeInternal()} against user settings - * for ringtone usage only. All other usages are allowed independently of ringer mode. + * @return One of Vibration.Status.IGNORED_* values if the vibration should be ignored, + * null otherwise. */ - public boolean shouldVibrateForRingerMode(int usageHint) { - if (usageHint != USAGE_RINGTONE) { - return true; - } + @Nullable + public Vibration.Status shouldIgnoreVibration(int uid, VibrationAttributes attrs) { + final int usage = attrs.getUsage(); synchronized (mLock) { - if (mAudioManager == null) { - return false; + if (!mUidObserver.isUidForeground(uid) + && !BACKGROUND_PROCESS_USAGE_ALLOWLIST.contains(usage)) { + return Vibration.Status.IGNORED_BACKGROUND; } - 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; + + if (mBatterySaverMode && !BATTERY_SAVER_USAGE_ALLOWLIST.contains(usage)) { + return Vibration.Status.IGNORED_FOR_POWER; } - } - } - /** - * 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); + 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; } /** - * Returns {@code true} if this vibration is allowed for current power mode state. + * Return {@code true} if the device should vibrate for current ringer mode. * - * <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. + * <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. */ - public boolean shouldVibrateForPowerMode(int usageHint) { - synchronized (mLock) { - return !mLowPowerMode || usageHint == USAGE_RINGTONE || usageHint == USAGE_ALARM - || usageHint == USAGE_COMMUNICATION_REQUEST; + @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; } } - /** 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 isClassAlarm(int usageHint) { - return (usageHint & VibrationAttributes.USAGE_CLASS_MASK) - == VibrationAttributes.USAGE_CLASS_ALARM; - } - /** 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, diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java index 97172015f9f0..478e86e5f710 100644 --- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java +++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java @@ -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,90 +758,53 @@ 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. - return null; + 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 && !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; - } + + if (mCurrentVibration == null || vib.isRepeating()) { + // Incoming repeating vibrations always take precedence over ongoing vibrations. + return null; } - return null; - } - /** - * 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; + Vibration currentVibration = mCurrentVibration.getVibration(); + if (currentVibration.hasEnded()) { + // Current vibration is finishing up, it should not block incoming vibrations. + return null; } - 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; + if (currentVibration.attrs.getUsage() == VibrationAttributes.USAGE_ALARM) { + return Vibration.Status.IGNORED_FOR_ALARM; } - return shouldIgnoreVibrationLocked(vib.uid, vib.opPkg, vib.attrs); + if (currentVibration.isRepeating()) { + return Vibration.Status.IGNORED_FOR_ONGOING; + } + return null; } /** - * Check if a vibration with given {@code uid}, {@code opPkg} and {@code attrs} should be - * ignored by this service. + * Check if given vibration 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); - 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); - } - } - + synchronized void dumpText(PrintWriter pw) { + for (int i = 0; i < mPreviousVibrations.size(); i++) { 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); } } - } - - synchronized void dumpProto(FileDescriptor fd) { - final ProtoOutputStream proto = new ProtoOutputStream(fd); - - synchronized (mLock) { - if (DEBUG) { - Slog.d(TAG, "Dumping vibrator manager service to proto..."); - } - 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(); + pw.println(); + pw.println(" Previous external vibrations:"); + for (Vibration.DebugInfo info : mPreviousExternalVibrations) { + pw.println(" " + info); + } + } + + 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; } - 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 : mPreviousVibrations.valueAt(i)) { + info.dumpProto(proto, fieldId); } + } - for (Vibration.DebugInfo info : mPreviousExternalVibrations) { - info.dumpProto(proto, - VibratorManagerServiceDumpProto.PREVIOUS_EXTERNAL_VIBRATIONS); - } + for (Vibration.DebugInfo info : mPreviousExternalVibrations) { + info.dumpProto(proto, + VibratorManagerServiceDumpProto.PREVIOUS_EXTERNAL_VIBRATIONS); } - proto.flush(); } } /** 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 { diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java index beee2a7520ec..ab9fbb581416 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java @@ -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())); + public void shouldIgnoreVibration_fromBackground_doesNotIgnoreUsagesFromAllowlist() { + int[] expectedAllowedVibrations = new int[] { + USAGE_RINGTONE, + USAGE_ALARM, + USAGE_NOTIFICATION, + USAGE_COMMUNICATION_REQUEST, + USAGE_HARDWARE_FEEDBACK, + USAGE_PHYSICAL_EMULATION, + }; - 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)); + mVibrationSettings.mUidObserver.onUidStateChanged( + UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0); + + for (int usage : expectedAllowedVibrations) { + assertNull("Error for usage " + VibrationAttributes.usageToString(usage), + mVibrationSettings.shouldIgnoreVibration(UID, + VibrationAttributes.createForUsage(usage))); + } } @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)); + public void shouldIgnoreVibration_fromBackground_ignoresUsagesNotInAllowlist() { + int[] expectedIgnoredVibrations = new int[] { + USAGE_TOUCH, + USAGE_UNKNOWN, + }; + + mVibrationSettings.mUidObserver.onUidStateChanged( + UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0); + + for (int usage : expectedIgnoredVibrations) { + assertEquals("Error for usage " + VibrationAttributes.usageToString(usage), + Vibration.Status.IGNORED_BACKGROUND, + mVibrationSettings.shouldIgnoreVibration(UID, + VibrationAttributes.createForUsage(usage))); + } } @Test - public void shouldVibrateForRingerMode_withVibrateWhenRinging_ignoreSettingsForSilentMode() { - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1); + public void shouldIgnoreVibration_fromForeground_allowsAnyUsage() { + mVibrationSettings.mUidObserver.onUidStateChanged( + UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0); - setRingerMode(AudioManager.RINGER_MODE_SILENT); - assertFalse(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE)); + assertNull(mVibrationSettings.shouldIgnoreVibration(UID, + VibrationAttributes.createForUsage(USAGE_TOUCH))); + assertNull(mVibrationSettings.shouldIgnoreVibration(UID, + VibrationAttributes.createForUsage(USAGE_ALARM))); + } - setRingerMode(AudioManager.RINGER_MODE_MAX); - assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE)); + @Test + public void shouldIgnoreVibration_inBatterySaverMode_doesNotIgnoreUsagesFromAllowlist() { + int[] expectedAllowedVibrations = new int[] { + USAGE_RINGTONE, + USAGE_ALARM, + USAGE_COMMUNICATION_REQUEST, + }; - setRingerMode(AudioManager.RINGER_MODE_NORMAL); - assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE)); + mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE); - setRingerMode(AudioManager.RINGER_MODE_VIBRATE); - assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE)); + for (int usage : expectedAllowedVibrations) { + assertNull("Error for usage " + VibrationAttributes.usageToString(usage), + mVibrationSettings.shouldIgnoreVibration(UID, + VibrationAttributes.createForUsage(usage))); + } } @Test - public void shouldVibrateForRingerMode_withApplyRampingRinger_ignoreSettingsForSilentMode() { - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); - setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1); + public void shouldIgnoreVibration_inBatterySaverMode_ignoresUsagesNotInAllowlist() { + int[] expectedIgnoredVibrations = new int[] { + USAGE_NOTIFICATION, + USAGE_HARDWARE_FEEDBACK, + USAGE_PHYSICAL_EMULATION, + USAGE_TOUCH, + USAGE_UNKNOWN, + }; - setRingerMode(AudioManager.RINGER_MODE_SILENT); - assertFalse(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE)); + mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE); - setRingerMode(AudioManager.RINGER_MODE_MAX); - assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE)); + for (int usage : expectedIgnoredVibrations) { + assertEquals("Error for usage " + VibrationAttributes.usageToString(usage), + Vibration.Status.IGNORED_FOR_POWER, + mVibrationSettings.shouldIgnoreVibration(UID, + VibrationAttributes.createForUsage(usage))); + } + } - setRingerMode(AudioManager.RINGER_MODE_NORMAL); - assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE)); + @Test + public void shouldIgnoreVibration_notInBatterySaverMode_allowsAnyUsage() { + mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE); - setRingerMode(AudioManager.RINGER_MODE_VIBRATE); - assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE)); + assertNull(mVibrationSettings.shouldIgnoreVibration(UID, + VibrationAttributes.createForUsage(USAGE_TOUCH))); + assertNull(mVibrationSettings.shouldIgnoreVibration(UID, + VibrationAttributes.createForUsage(USAGE_COMMUNICATION_REQUEST))); } @Test - public void shouldVibrateForRingerMode_withAllSettingsOff_onlyVibratesForVibrateMode() { - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); + 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); + + 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); - assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE)); - setRingerMode(AudioManager.RINGER_MODE_SILENT); - assertFalse(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE)); + 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))); + } - setRingerMode(AudioManager.RINGER_MODE_MAX); - assertFalse(mVibrationSettings.shouldVibrateForRingerMode(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); - assertFalse(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE)); + + 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 shouldVibrateForUid_withForegroundOnlyUsage_returnsTrueWhInForeground() { - assertTrue(mVibrationSettings.shouldVibrateForUid(UID, USAGE_TOUCH)); + public void shouldIgnoreVibration_withRingerModeNormalAndRampingRingerOn_allowsAllVibrations() { + setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); + setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1); + setRingerMode(AudioManager.RINGER_MODE_NORMAL); - mVibrationSettings.mUidObserver.onUidStateChanged( - UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0); - assertFalse(mVibrationSettings.shouldVibrateForUid(UID, USAGE_TOUCH)); + 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 shouldVibrateForUid_withBackgroundAllowedUsage_returnTrue() { - mVibrationSettings.mUidObserver.onUidStateChanged( - UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0); + public void shouldIgnoreVibration_withHapticFeedbackSettingsOff_ignoresTouchVibration() { + setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF); - 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)); + 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 shouldVibrateForPowerMode_withLowPowerAndAllowedUsage_returnTrue() { - mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE); + public void shouldIgnoreVibration_withHardwareFeedbackSettingsOff_ignoresHardwareVibrations() { + setUserSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF); - assertTrue(mVibrationSettings.shouldVibrateForPowerMode(USAGE_ALARM)); - assertTrue(mVibrationSettings.shouldVibrateForPowerMode(USAGE_RINGTONE)); - assertTrue(mVibrationSettings.shouldVibrateForPowerMode(USAGE_COMMUNICATION_REQUEST)); + 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 shouldVibrateForPowerMode_withRestrictedUsage_returnsFalseWhileInLowPowerMode() { - mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE); + public void shouldIgnoreVibration_withNotificationSettingsOff_ignoresNotificationVibrations() { + setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF); - assertTrue(mVibrationSettings.shouldVibrateForPowerMode(USAGE_TOUCH)); - assertTrue(mVibrationSettings.shouldVibrateForPowerMode(USAGE_NOTIFICATION)); + 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))); + } - mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE); + @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); - assertFalse(mVibrationSettings.shouldVibrateForPowerMode(USAGE_TOUCH)); - assertFalse(mVibrationSettings.shouldVibrateForPowerMode(USAGE_NOTIFICATION)); + 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 @@ -305,24 +423,6 @@ public class VibrationSettingsTest { } @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); mFakeVibrator.setDefaultNotificationVibrationIntensity(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()); |