summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Lais Andrade <lsandrade@google.com> 2022-05-10 15:10:51 +0100
committer Lais Andrade <lsandrade@google.com> 2022-05-13 19:53:43 +0100
commitc558857fc7fda905b56fbc6c57b80c0336ad3dcf (patch)
treeb00bc8cded2f6628d21959747939feac1485e917
parentc9daa03269935ea5b36f7abf4b7bbda0a287afcf (diff)
Check sleep reason and time before cancelling vibrations
Add a method PowerManagerInternal.getLastGoToSleep(), similar to existing method getLastWakeup(), to retrieve global values for last go to sleep events, including system uptime and reason. Retrieve this data in VibratorManagerService before cancelling ongoing vibrations by the broadcast of ACTION_SCREEN_OFF intents to indicate that the screen was turned of when the vibration was still playing. This new logic allows vibrations to continue in the following cases: - the broadcasted event was triggered before the vibration started; - there is already a wakeup event triggered by the time the screen off broadcast is being processed by the vibrator service; - the screen off reason is in allowlist, indicating it's an automatically triggered event (screen timeout or user inattentive); This should handle the following scenarios: - delayed broadcasts of screen off events, that should not cancel vibrations that started when the screen was already off (e.g. notification or ringtone that are allowed to vibrate in that state); - race conditions when the screen automatically turns off right after a ringtone/notification vibration starts, before the notification acquires a screen lock; Fix: 219849350 Test: VibrationSettingsTest Change-Id: I358327192196989a7d4fc49a96b2ab92ec677302
-rw-r--r--core/java/android/os/PowerManager.java73
-rw-r--r--core/java/android/os/PowerManagerInternal.java3
-rw-r--r--core/proto/android/server/vibrator/vibratormanagerservice.proto3
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java21
-rw-r--r--services/core/java/com/android/server/vibrator/Vibration.java30
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationSettings.java56
-rw-r--r--services/core/java/com/android/server/vibrator/VibratorManagerService.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationScalerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java111
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));
}
}