diff options
9 files changed, 245 insertions, 73 deletions
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 8f2d218a20c1..13ca2c34b27e 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -463,21 +463,22 @@ public final class PowerManager { /** * @hide */ - public static String sleepReasonToString(int sleepReason) { + public static String sleepReasonToString(@GoToSleepReason int sleepReason) { switch (sleepReason) { + case GO_TO_SLEEP_REASON_ACCESSIBILITY: return "accessibility"; case GO_TO_SLEEP_REASON_APPLICATION: return "application"; case GO_TO_SLEEP_REASON_DEVICE_ADMIN: return "device_admin"; - case GO_TO_SLEEP_REASON_TIMEOUT: return "timeout"; + case GO_TO_SLEEP_REASON_DEVICE_FOLD: return "device_folded"; + case GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED: return "display_group_removed"; + case GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF: return "display_groups_turned_off"; + case GO_TO_SLEEP_REASON_FORCE_SUSPEND: return "force_suspend"; + case GO_TO_SLEEP_REASON_HDMI: return "hdmi"; + case GO_TO_SLEEP_REASON_INATTENTIVE: return "inattentive"; case GO_TO_SLEEP_REASON_LID_SWITCH: return "lid_switch"; case GO_TO_SLEEP_REASON_POWER_BUTTON: return "power_button"; - case GO_TO_SLEEP_REASON_HDMI: return "hdmi"; + case GO_TO_SLEEP_REASON_QUIESCENT: return "quiescent"; case GO_TO_SLEEP_REASON_SLEEP_BUTTON: return "sleep_button"; - case GO_TO_SLEEP_REASON_ACCESSIBILITY: return "accessibility"; - case GO_TO_SLEEP_REASON_FORCE_SUSPEND: return "force_suspend"; - case GO_TO_SLEEP_REASON_INATTENTIVE: return "inattentive"; - case GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED: return "display_group_removed"; - case GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF: return "display_groups_turned_off"; - case GO_TO_SLEEP_REASON_DEVICE_FOLD: return "device_folded"; + case GO_TO_SLEEP_REASON_TIMEOUT: return "timeout"; default: return Integer.toString(sleepReason); } } @@ -576,18 +577,20 @@ public final class PowerManager { * @hide */ @IntDef(prefix = { "GO_TO_SLEEP_REASON_" }, value = { + GO_TO_SLEEP_REASON_ACCESSIBILITY, GO_TO_SLEEP_REASON_APPLICATION, GO_TO_SLEEP_REASON_DEVICE_ADMIN, - GO_TO_SLEEP_REASON_TIMEOUT, - GO_TO_SLEEP_REASON_LID_SWITCH, - GO_TO_SLEEP_REASON_POWER_BUTTON, - GO_TO_SLEEP_REASON_HDMI, - GO_TO_SLEEP_REASON_SLEEP_BUTTON, - GO_TO_SLEEP_REASON_ACCESSIBILITY, + GO_TO_SLEEP_REASON_DEVICE_FOLD, + GO_TO_SLEEP_REASON_DISPLAY_GROUP_REMOVED, + GO_TO_SLEEP_REASON_DISPLAY_GROUPS_TURNED_OFF, GO_TO_SLEEP_REASON_FORCE_SUSPEND, + GO_TO_SLEEP_REASON_HDMI, GO_TO_SLEEP_REASON_INATTENTIVE, + GO_TO_SLEEP_REASON_LID_SWITCH, + GO_TO_SLEEP_REASON_POWER_BUTTON, GO_TO_SLEEP_REASON_QUIESCENT, - GO_TO_SLEEP_REASON_DEVICE_FOLD + GO_TO_SLEEP_REASON_SLEEP_BUTTON, + GO_TO_SLEEP_REASON_TIMEOUT, }) @Retention(RetentionPolicy.SOURCE) public @interface GoToSleepReason{} @@ -704,6 +707,8 @@ public final class PowerManager { } /** + * Information related to the device waking up, triggered by {@link #wakeUp}. + * * @hide */ public static class WakeData { @@ -712,9 +717,9 @@ public final class PowerManager { this.wakeReason = wakeReason; this.sleepDuration = sleepDuration; } - public long wakeTime; - public @WakeReason int wakeReason; - public long sleepDuration; + public final long wakeTime; + public final @WakeReason int wakeReason; + public final long sleepDuration; @Override public boolean equals(@Nullable Object o) { @@ -733,6 +738,35 @@ public final class PowerManager { } /** + * Information related to the device going to sleep, triggered by {@link #goToSleep}. + * + * @hide + */ + public static class SleepData { + public SleepData(long goToSleepUptimeMillis, @GoToSleepReason int goToSleepReason) { + this.goToSleepUptimeMillis = goToSleepUptimeMillis; + this.goToSleepReason = goToSleepReason; + } + public final long goToSleepUptimeMillis; + public final @GoToSleepReason int goToSleepReason; + + @Override + public boolean equals(@Nullable Object o) { + if (o instanceof SleepData) { + final SleepData other = (SleepData) o; + return goToSleepUptimeMillis == other.goToSleepUptimeMillis + && goToSleepReason == other.goToSleepReason; + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(goToSleepUptimeMillis, goToSleepReason); + } + } + + /** * The value to pass as the 'reason' argument to reboot() to reboot into * recovery mode for tasks other than applying system updates, such as * doing factory resets. @@ -2644,6 +2678,7 @@ public final class PowerManager { * * @hide */ + @GoToSleepReason public int getLastSleepReason() { try { return mService.getLastSleepReason(); diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java index ec4d3b6a2441..5ca0da2d3f97 100644 --- a/core/java/android/os/PowerManagerInternal.java +++ b/core/java/android/os/PowerManagerInternal.java @@ -330,6 +330,9 @@ public abstract class PowerManagerInternal { /** Returns information about the last wakeup event. */ public abstract PowerManager.WakeData getLastWakeup(); + /** Returns information about the last event to go to sleep. */ + public abstract PowerManager.SleepData getLastGoToSleep(); + /** Allows power button to intercept a power key button press. */ public abstract boolean interceptPowerKeyDown(KeyEvent event); } diff --git a/core/proto/android/server/vibrator/vibratormanagerservice.proto b/core/proto/android/server/vibrator/vibratormanagerservice.proto index 2f2158d4d5a0..2a625b027c17 100644 --- a/core/proto/android/server/vibrator/vibratormanagerservice.proto +++ b/core/proto/android/server/vibrator/vibratormanagerservice.proto @@ -86,7 +86,7 @@ message VibrationAttributesProto { optional int32 flags = 3; } -// Next id: 7 +// Next id: 8 message VibrationProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; optional int64 start_time = 1; @@ -95,6 +95,7 @@ message VibrationProto { optional CombinedVibrationEffectProto original_effect = 4; optional VibrationAttributesProto attributes = 5; optional int32 status = 6; + optional int64 duration_ms = 7; } // Next id: 25 diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 4075cddc302c..3c13abf4403c 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -72,8 +72,8 @@ import android.os.Looper; import android.os.Message; import android.os.ParcelDuration; import android.os.PowerManager; +import android.os.PowerManager.GoToSleepReason; import android.os.PowerManager.ServiceType; -import android.os.PowerManager.WakeData; import android.os.PowerManager.WakeReason; import android.os.PowerManagerInternal; import android.os.PowerSaveState; @@ -352,7 +352,7 @@ public final class PowerManagerService extends SystemService // Last reason the device went to sleep. private @WakeReason int mLastGlobalWakeReason; - private int mLastGlobalSleepReason; + private @GoToSleepReason int mLastGlobalSleepReason; // Timestamp of last time power boost interaction was sent. private long mLastInteractivePowerHintTime; @@ -6350,20 +6350,26 @@ public final class PowerManagerService extends SystemService } } + @GoToSleepReason private int getLastSleepReasonInternal() { synchronized (mLock) { return mLastGlobalSleepReason; } } - @VisibleForTesting private PowerManager.WakeData getLastWakeupInternal() { synchronized (mLock) { - return new WakeData(mLastGlobalWakeTime, mLastGlobalWakeReason, + return new PowerManager.WakeData(mLastGlobalWakeTime, mLastGlobalWakeReason, mLastGlobalWakeTime - mLastGlobalSleepTime); } } + private PowerManager.SleepData getLastGoToSleepInternal() { + synchronized (mLock) { + return new PowerManager.SleepData(mLastGlobalSleepTime, mLastGlobalSleepReason); + } + } + /** * If the user presses power while the proximity sensor is enabled and keeping * the screen off, then turn the screen back on by telling display manager to @@ -6528,11 +6534,16 @@ public final class PowerManagerService extends SystemService } @Override - public WakeData getLastWakeup() { + public PowerManager.WakeData getLastWakeup() { return getLastWakeupInternal(); } @Override + public PowerManager.SleepData getLastGoToSleep() { + return getLastGoToSleepInternal(); + } + + @Override public boolean interceptPowerKeyDown(KeyEvent event) { return interceptPowerKeyDownInternal(event); } diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java index 3e364314e10f..78b1c20ac4b2 100644 --- a/services/core/java/com/android/server/vibrator/Vibration.java +++ b/services/core/java/com/android/server/vibrator/Vibration.java @@ -71,8 +71,8 @@ final class Vibration { IGNORED_SUPERSEDED, } - /** Start time in CLOCK_BOOTTIME base. */ - public final long startTime; + /** Start time using {@link SystemClock#uptimeMillis()}, for calculations. */ + public final long startUptimeMillis; public final VibrationAttributes attrs; public final long id; public final int uid; @@ -94,11 +94,14 @@ final class Vibration { /** * Start/end times in unix epoch time. Only to be used for debugging purposes and to correlate - * with other system events, any duration calculations should be done use {@link #startTime} so - * as not to be affected by discontinuities created by RTC adjustments. + * with other system events, any duration calculations should be done use + * {@link #startUptimeMillis} so as not to be affected by discontinuities created by RTC + * adjustments. */ private final long mStartTimeDebug; private long mEndTimeDebug; + /** End time using {@link SystemClock#uptimeMillis()}, for calculations. */ + private long mEndUptimeMillis; private Status mStatus; /** A {@link CountDownLatch} to enable waiting for completion. */ @@ -109,7 +112,7 @@ final class Vibration { this.token = token; this.mEffect = effect; this.id = id; - this.startTime = SystemClock.elapsedRealtime(); + this.startUptimeMillis = SystemClock.uptimeMillis(); this.attrs = attrs; this.uid = uid; this.opPkg = opPkg; @@ -131,6 +134,7 @@ final class Vibration { return; } mStatus = status; + mEndUptimeMillis = SystemClock.uptimeMillis(); mEndTimeDebug = System.currentTimeMillis(); mCompletionLatch.countDown(); } @@ -225,15 +229,17 @@ final class Vibration { /** Return {@link Vibration.DebugInfo} with read-only debug information about this vibration. */ public Vibration.DebugInfo getDebugInfo() { + long durationMs = hasEnded() ? mEndUptimeMillis - startUptimeMillis : -1; return new Vibration.DebugInfo( - mStartTimeDebug, mEndTimeDebug, mEffect, mOriginalEffect, /* scale= */ 0, attrs, - uid, opPkg, reason, mStatus); + mStartTimeDebug, mEndTimeDebug, durationMs, mEffect, mOriginalEffect, + /* scale= */ 0, attrs, uid, opPkg, reason, mStatus); } /** Debug information about vibrations. */ static final class DebugInfo { private final long mStartTimeDebug; private final long mEndTimeDebug; + private final long mDurationMs; private final CombinedVibration mEffect; private final CombinedVibration mOriginalEffect; private final float mScale; @@ -243,11 +249,12 @@ final class Vibration { private final String mReason; private final Status mStatus; - DebugInfo(long startTimeDebug, long endTimeDebug, CombinedVibration effect, - CombinedVibration originalEffect, float scale, VibrationAttributes attrs, - int uid, String opPkg, String reason, Status status) { + DebugInfo(long startTimeDebug, long endTimeDebug, long durationMs, + CombinedVibration effect, CombinedVibration originalEffect, float scale, + VibrationAttributes attrs, int uid, String opPkg, String reason, Status status) { mStartTimeDebug = startTimeDebug; mEndTimeDebug = endTimeDebug; + mDurationMs = durationMs; mEffect = effect; mOriginalEffect = originalEffect; mScale = scale; @@ -266,6 +273,8 @@ final class Vibration { .append(", endTime: ") .append(mEndTimeDebug == 0 ? null : DEBUG_DATE_FORMAT.format(new Date(mEndTimeDebug))) + .append(", durationMs: ") + .append(mDurationMs) .append(", status: ") .append(mStatus.name().toLowerCase()) .append(", effect: ") @@ -290,6 +299,7 @@ final class Vibration { final long token = proto.start(fieldId); proto.write(VibrationProto.START_TIME, mStartTimeDebug); proto.write(VibrationProto.END_TIME, mEndTimeDebug); + proto.write(VibrationProto.DURATION_MS, mDurationMs); proto.write(VibrationProto.STATUS, mStatus.ordinal()); final long attrsToken = proto.start(VibrationProto.ATTRIBUTES); diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java index ac635a0746c5..f9ffd92a7a1c 100644 --- a/services/core/java/com/android/server/vibrator/VibrationSettings.java +++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java @@ -52,6 +52,7 @@ import android.os.Vibrator; import android.os.Vibrator.VibrationIntensity; import android.os.vibrator.VibrationConfig; import android.provider.Settings; +import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; import android.util.proto.ProtoOutputStream; @@ -121,6 +122,19 @@ final class VibrationSettings { USAGE_PHYSICAL_EMULATION, USAGE_HARDWARE_FEEDBACK)); + /** + * Set of reasons for {@link PowerManager} going to sleep events that allows vibrations to + * continue running. + * + * <p>Some examples are timeout and inattentive, which indicates automatic screen off events. + * When a vibration is playing during one of these screen off events then it will not be + * cancelled by the service. + */ + private static final Set<Integer> POWER_MANAGER_SLEEP_REASON_ALLOWLIST = new HashSet<>( + Arrays.asList( + PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE, + PowerManager.GO_TO_SLEEP_REASON_TIMEOUT)); + private static final IntentFilter USER_SWITCHED_INTENT_FILTER = new IntentFilter(Intent.ACTION_USER_SWITCHED); private static final IntentFilter INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER = @@ -135,7 +149,8 @@ final class VibrationSettings { private final Object mLock = new Object(); private final Context mContext; private final String mSystemUiPackage; - private final SettingsContentObserver mSettingObserver; + @VisibleForTesting + final SettingsContentObserver mSettingObserver; @VisibleForTesting final UidObserver mUidObserver; @VisibleForTesting @@ -150,6 +165,9 @@ final class VibrationSettings { @GuardedBy("mLock") @Nullable private AudioManager mAudioManager; + @GuardedBy("mLock") + @Nullable + private PowerManagerInternal mPowerManagerInternal; @GuardedBy("mLock") private boolean mVibrateInputDevices; @@ -199,10 +217,16 @@ final class VibrationSettings { } public void onSystemReady() { + PowerManagerInternal pm = LocalServices.getService(PowerManagerInternal.class); + AudioManager am = mContext.getSystemService(AudioManager.class); + int ringerMode = am.getRingerModeInternal(); + synchronized (mLock) { - mAudioManager = mContext.getSystemService(AudioManager.class); - mRingerMode = mAudioManager.getRingerModeInternal(); + mPowerManagerInternal = pm; + mAudioManager = am; + mRingerMode = ringerMode; } + try { ActivityManager.getService().registerUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE, @@ -211,7 +235,6 @@ final class VibrationSettings { // ignored; both services live in system_server } - PowerManagerInternal pm = LocalServices.getService(PowerManagerInternal.class); pm.registerLowPowerModeObserver( new PowerManagerInternal.LowPowerModeListener() { @Override @@ -381,7 +404,27 @@ final class VibrationSettings { * @return true if the vibration should be cancelled when the screen goes off, false otherwise. */ public boolean shouldCancelVibrationOnScreenOff(int uid, String opPkg, - @VibrationAttributes.Usage int usage) { + @VibrationAttributes.Usage int usage, long vibrationStartUptimeMillis) { + PowerManagerInternal pm; + synchronized (mLock) { + pm = mPowerManagerInternal; + } + if (pm != null) { + // The SleepData from PowerManager may refer to a more recent sleep than the broadcast + // that triggered this method call. That's ok because only automatic sleeps would be + // ignored here and not cancel a vibration, and those are usually triggered by timeout + // or inactivity, so it's unlikely that it will override a more active goToSleep reason. + PowerManager.SleepData sleepData = pm.getLastGoToSleep(); + if ((sleepData.goToSleepUptimeMillis < vibrationStartUptimeMillis) + || POWER_MANAGER_SLEEP_REASON_ALLOWLIST.contains(sleepData.goToSleepReason)) { + // Ignore screen off events triggered before the vibration started, and all + // automatic "go to sleep" events from allowlist. + Slog.d(TAG, "Ignoring screen off event triggered at uptime " + + sleepData.goToSleepUptimeMillis + " for reason " + + PowerManager.sleepReasonToString(sleepData.goToSleepReason)); + return false; + } + } if (!SYSTEM_VIBRATION_SCREEN_OFF_USAGE_ALLOWLIST.contains(usage)) { // Usages not allowed even for system vibrations should always be cancelled. return true; @@ -628,7 +671,8 @@ final class VibrationSettings { } /** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */ - private final class SettingsContentObserver extends ContentObserver { + @VisibleForTesting + final class SettingsContentObserver extends ContentObserver { SettingsContentObserver(Handler handler) { super(handler); } diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java index d7341cb37685..f0911ca62027 100644 --- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java +++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java @@ -47,6 +47,7 @@ import android.os.ResultReceiver; import android.os.ServiceManager; import android.os.ShellCallback; import android.os.ShellCommand; +import android.os.SystemClock; import android.os.Trace; import android.os.VibrationAttributes; import android.os.VibrationEffect; @@ -405,7 +406,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { if (attrs.isFlagSet(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) { // Force update of user settings before checking if this vibration effect should // be ignored or scaled. - mVibrationSettings.update(); + mVibrationSettings.mSettingObserver.onChange(false); } synchronized (mLock) { @@ -1103,7 +1104,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } Vibration vib = conductor.getVibration(); return mVibrationSettings.shouldCancelVibrationOnScreenOff( - vib.uid, vib.opPkg, vib.attrs.getUsage()); + vib.uid, vib.opPkg, vib.attrs.getUsage(), vib.startUptimeMillis); } @GuardedBy("mLock") @@ -1308,13 +1309,17 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { public final ExternalVibration externalVibration; public int scale; + private final long mStartUptimeMillis; private final long mStartTimeDebug; + + private long mEndUptimeMillis; private long mEndTimeDebug; private Vibration.Status mStatus; private ExternalVibrationHolder(ExternalVibration externalVibration) { this.externalVibration = externalVibration; this.scale = IExternalVibratorService.SCALE_NONE; + mStartUptimeMillis = SystemClock.uptimeMillis(); mStartTimeDebug = System.currentTimeMillis(); mStatus = Vibration.Status.RUNNING; } @@ -1325,6 +1330,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { return; } mStatus = status; + mEndUptimeMillis = SystemClock.uptimeMillis(); mEndTimeDebug = System.currentTimeMillis(); } @@ -1341,11 +1347,12 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } public Vibration.DebugInfo getDebugInfo() { + long durationMs = mEndUptimeMillis == 0 ? -1 : mEndUptimeMillis - mStartUptimeMillis; return new Vibration.DebugInfo( - mStartTimeDebug, mEndTimeDebug, /* effect= */ null, /* originalEffect= */ null, - scale, externalVibration.getVibrationAttributes(), - externalVibration.getUid(), externalVibration.getPackage(), - /* reason= */ null, mStatus); + mStartTimeDebug, mEndTimeDebug, durationMs, + /* effect= */ null, /* originalEffect= */ null, scale, + externalVibration.getVibrationAttributes(), externalVibration.getUid(), + externalVibration.getPackage(), /* reason= */ null, mStatus); } } diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java index b907c62be6fb..64950aa69e3b 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java @@ -277,6 +277,6 @@ public class VibrationScalerTest { Settings.System.putIntForUser( mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT); // FakeSettingsProvider don't support testing triggering ContentObserver yet. - mVibrationSettings.update(); + mVibrationSettings.mSettingObserver.onChange(false); } } 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 0a50e790215f..b214dd0ad2ba 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java @@ -54,6 +54,7 @@ import android.content.Intent; import android.content.pm.PackageManagerInternal; import android.media.AudioManager; import android.os.Handler; +import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.Process; @@ -146,6 +147,8 @@ public class VibrationSettingsTest { mVibrationSettings = new VibrationSettings(mContextSpy, new Handler(mTestLooper.getLooper()), mVibrationConfigMock); + mockGoToSleep(/* goToSleepTime= */ 0, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT); + // Simulate System defaults. setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1); setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0); @@ -164,18 +167,10 @@ public class VibrationSettingsTest { public void addListener_settingsChangeTriggerListener() { mVibrationSettings.addListener(mListenerMock); - setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1); - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); - setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0); - setUserSetting(Settings.System.ALARM_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF); - setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF); - setUserSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF); - setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF); - setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 0); - setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF); - setUserSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF); + mVibrationSettings.mSettingObserver.onChange(false); + mVibrationSettings.mSettingObserver.onChange(false); - verify(mListenerMock, times(10)).onChange(); + verify(mListenerMock, times(2)).onChange(); } @Test @@ -479,50 +474,112 @@ public class VibrationSettingsTest { } @Test - public void shouldCancelVibrationOnScreenOff_withNonSystemPackageAndUid_returnsAlwaysTrue() { + public void shouldCancelVibrationOnScreenOff_withEventBeforeVibration_returnsAlwaysFalse() { + long vibrateStartTime = 100; + mockGoToSleep(vibrateStartTime - 10, PowerManager.GO_TO_SLEEP_REASON_APPLICATION); + + for (int usage : ALL_USAGES) { + // Non-system vibration + assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff( + UID, "some.app", usage, vibrateStartTime)); + // Vibration with UID zero + assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff( + /* uid= */ 0, "", usage, vibrateStartTime)); + // System vibration + assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff( + Process.SYSTEM_UID, "", usage, vibrateStartTime)); + // SysUI vibration + assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff( + UID, SYSUI_PACKAGE_NAME, usage, vibrateStartTime)); + } + } + + @Test + public void shouldCancelVibrationOnScreenOff_withSleepReasonInAllowlist_returnsAlwaysFalse() { + long vibrateStartTime = 100; + int[] allowedSleepReasons = new int[] { + PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, + PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE, + }; + + for (int sleepReason : allowedSleepReasons) { + mockGoToSleep(vibrateStartTime + 10, sleepReason); + + for (int usage : ALL_USAGES) { + // Non-system vibration + assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff( + UID, "some.app", usage, vibrateStartTime)); + // Vibration with UID zero + assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff( + /* uid= */ 0, "", usage, vibrateStartTime)); + // System vibration + assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff( + Process.SYSTEM_UID, "", usage, vibrateStartTime)); + // SysUI vibration + assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff( + UID, SYSUI_PACKAGE_NAME, usage, vibrateStartTime)); + } + } + } + + @Test + public void shouldCancelVibrationOnScreenOff_withNonSystem_returnsTrueIfReasonNotInAllowlist() { + long vibrateStartTime = 100; + mockGoToSleep(vibrateStartTime + 10, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON); + for (int usage : ALL_USAGES) { - assertTrue(mVibrationSettings.shouldCancelVibrationOnScreenOff(UID, "some.app", usage)); + assertTrue(mVibrationSettings.shouldCancelVibrationOnScreenOff( + UID, "some.app", usage, vibrateStartTime)); } } @Test public void shouldCancelVibrationOnScreenOff_withUidZero_returnsFalseForTouchAndHardware() { + long vibrateStartTime = 100; + mockGoToSleep(vibrateStartTime + 10, PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN); + for (int usage : ALL_USAGES) { if (usage == USAGE_TOUCH || usage == USAGE_HARDWARE_FEEDBACK || usage == USAGE_PHYSICAL_EMULATION) { assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff( - /* uid= */ 0, "", usage)); + /* uid= */ 0, "", usage, vibrateStartTime)); } else { assertTrue(mVibrationSettings.shouldCancelVibrationOnScreenOff( - /* uid= */ 0, "", usage)); + /* uid= */ 0, "", usage, vibrateStartTime)); } } } @Test public void shouldCancelVibrationOnScreenOff_withSystemUid_returnsFalseForTouchAndHardware() { + long vibrateStartTime = 100; + mockGoToSleep(vibrateStartTime + 10, PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD); + for (int usage : ALL_USAGES) { if (usage == USAGE_TOUCH || usage == USAGE_HARDWARE_FEEDBACK || usage == USAGE_PHYSICAL_EMULATION) { assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff( - Process.SYSTEM_UID, "", usage)); + Process.SYSTEM_UID, "", usage, vibrateStartTime)); } else { assertTrue(mVibrationSettings.shouldCancelVibrationOnScreenOff( - Process.SYSTEM_UID, "", usage)); + Process.SYSTEM_UID, "", usage, vibrateStartTime)); } } } @Test - public void shouldCancelVibrationOnScreenOff_withSysUi_returnsFalseForTouchAndHardware() { + public void shouldCancelVibrationOnScreenOff_withSysUiPkg_returnsFalseForTouchAndHardware() { + long vibrateStartTime = 100; + mockGoToSleep(vibrateStartTime + 10, PowerManager.GO_TO_SLEEP_REASON_HDMI); + for (int usage : ALL_USAGES) { if (usage == USAGE_TOUCH || usage == USAGE_HARDWARE_FEEDBACK || usage == USAGE_PHYSICAL_EMULATION) { assertFalse(mVibrationSettings.shouldCancelVibrationOnScreenOff( - UID, SYSUI_PACKAGE_NAME, usage)); + UID, SYSUI_PACKAGE_NAME, usage, vibrateStartTime)); } else { assertTrue(mVibrationSettings.shouldCancelVibrationOnScreenOff( - UID, SYSUI_PACKAGE_NAME, usage)); + UID, SYSUI_PACKAGE_NAME, usage, vibrateStartTime)); } } } @@ -581,7 +638,6 @@ public class VibrationSettingsTest { public void getCurrentIntensity_noHardwareFeedbackValueUsesHapticFeedbackValue() { setDefaultIntensity(USAGE_HARDWARE_FEEDBACK, VIBRATION_INTENSITY_MEDIUM); setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF); - mVibrationSettings.update(); assertEquals(VIBRATION_INTENSITY_OFF, mVibrationSettings.getCurrentIntensity(USAGE_TOUCH)); // If haptic feedback is off, fallback to default value. assertEquals(VIBRATION_INTENSITY_MEDIUM, @@ -590,7 +646,6 @@ public class VibrationSettingsTest { mVibrationSettings.getCurrentIntensity(USAGE_PHYSICAL_EMULATION)); setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH); - mVibrationSettings.update(); assertEquals(VIBRATION_INTENSITY_HIGH, mVibrationSettings.getCurrentIntensity(USAGE_TOUCH)); // If haptic feedback is on, fallback to that value. @@ -648,19 +703,25 @@ public class VibrationSettingsTest { Settings.System.putStringForUser( mContextSpy.getContentResolver(), settingName, null, UserHandle.USER_CURRENT); // FakeSettingsProvider doesn't support testing triggering ContentObserver yet. - mVibrationSettings.update(); + mVibrationSettings.mSettingObserver.onChange(false); } private void setUserSetting(String settingName, int value) { Settings.System.putIntForUser( mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT); // FakeSettingsProvider doesn't support testing triggering ContentObserver yet. - mVibrationSettings.update(); + mVibrationSettings.mSettingObserver.onChange(false); } private void setRingerMode(int ringerMode) { mAudioManager.setRingerModeInternal(ringerMode); assertEquals(ringerMode, mAudioManager.getRingerModeInternal()); - mVibrationSettings.update(); + mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy, + new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)); + } + + private void mockGoToSleep(long sleepTime, int reason) { + when(mPowerManagerInternalMock.getLastGoToSleep()).thenReturn( + new PowerManager.SleepData(sleepTime, reason)); } } |