Merge "Improve VibratorManagerService dumpsys" into main
diff --git a/core/java/android/os/CombinedVibration.java b/core/java/android/os/CombinedVibration.java
index db1a741..f32a1f8 100644
--- a/core/java/android/os/CombinedVibration.java
+++ b/core/java/android/os/CombinedVibration.java
@@ -24,7 +24,9 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
import java.util.Objects;
+import java.util.StringJoiner;
/**
* A CombinedVibration describes a combination of haptic effects to be performed by one or more
@@ -151,6 +153,13 @@
public abstract boolean hasVibrator(int vibratorId);
/**
+ * Returns a compact version of the {@link #toString()} result for debugging purposes.
+ *
+ * @hide
+ */
+ public abstract String toDebugString();
+
+ /**
* Adapts a {@link VibrationEffect} to a specific device vibrator using the ID.
*
* <p>This can be used for adapting effects to the capabilities of the specific device vibrator
@@ -437,6 +446,13 @@
return "Mono{mEffect=" + mEffect + '}';
}
+ /** @hide */
+ @Override
+ public String toDebugString() {
+ // Simplify vibration string, use the single effect to represent it.
+ return mEffect.toDebugString();
+ }
+
@Override
public int describeContents() {
return 0;
@@ -619,6 +635,17 @@
return "Stereo{mEffects=" + mEffects + '}';
}
+ /** @hide */
+ @Override
+ public String toDebugString() {
+ StringJoiner sj = new StringJoiner(",", "Stereo{", "}");
+ for (int i = 0; i < mEffects.size(); i++) {
+ sj.add(String.format(Locale.ROOT, "vibrator(id=%d): %s",
+ mEffects.keyAt(i), mEffects.valueAt(i).toDebugString()));
+ }
+ return sj.toString();
+ }
+
@Override
public int describeContents() {
return 0;
@@ -833,6 +860,17 @@
return "Sequential{mEffects=" + mEffects + ", mDelays=" + mDelays + '}';
}
+ /** @hide */
+ @Override
+ public String toDebugString() {
+ StringJoiner sj = new StringJoiner(",", "Sequential{", "}");
+ for (int i = 0; i < mEffects.size(); i++) {
+ sj.add(String.format(Locale.ROOT, "delayMs=%d, effect=%s",
+ mDelays.get(i), mEffects.get(i).toDebugString()));
+ }
+ return sj.toString();
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index b2d62eb..98f9dff 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -336,10 +336,11 @@
@Override
public String toString() {
- return "VibrationAttributes:"
- + " Usage=" + usageToString()
- + " Audio Usage= " + AudioAttributes.usageToString(mOriginalAudioUsage)
- + " Flags=" + mFlags;
+ return "VibrationAttributes{"
+ + "mUsage=" + usageToString()
+ + ", mAudioUsage= " + AudioAttributes.usageToString(mOriginalAudioUsage)
+ + ", mFlags=" + mFlags
+ + '}';
}
/** @hide */
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index c3d7540..b24b45d 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -44,7 +44,9 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Locale;
import java.util.Objects;
+import java.util.StringJoiner;
/**
* A VibrationEffect describes a haptic effect to be performed by a {@link Vibrator}.
@@ -629,6 +631,13 @@
return MathUtils.constrain(a * fx, 0f, 1f);
}
+ /**
+ * Returns a compact version of the {@link #toString()} result for debugging purposes.
+ *
+ * @hide
+ */
+ public abstract String toDebugString();
+
/** @hide */
public static String effectIdToString(int effectId) {
switch (effectId) {
@@ -925,6 +934,23 @@
+ "}";
}
+ /** @hide */
+ @Override
+ public String toDebugString() {
+ if (mSegments.size() == 1 && mRepeatIndex < 0) {
+ // Simplify effect string, use the single segment to represent it.
+ return mSegments.get(0).toDebugString();
+ }
+ StringJoiner sj = new StringJoiner(",", "[", "]");
+ for (int i = 0; i < mSegments.size(); i++) {
+ sj.add(mSegments.get(i).toDebugString());
+ }
+ if (mRepeatIndex >= 0) {
+ return String.format(Locale.ROOT, "%s, repeat=%d", sj, mRepeatIndex);
+ }
+ return sj.toString();
+ }
+
@Override
public int describeContents() {
return 0;
@@ -1250,23 +1276,23 @@
public static String primitiveToString(@PrimitiveType int id) {
switch (id) {
case PRIMITIVE_NOOP:
- return "PRIMITIVE_NOOP";
+ return "NOOP";
case PRIMITIVE_CLICK:
- return "PRIMITIVE_CLICK";
+ return "CLICK";
case PRIMITIVE_THUD:
- return "PRIMITIVE_THUD";
+ return "THUD";
case PRIMITIVE_SPIN:
- return "PRIMITIVE_SPIN";
+ return "SPIN";
case PRIMITIVE_QUICK_RISE:
- return "PRIMITIVE_QUICK_RISE";
+ return "QUICK_RISE";
case PRIMITIVE_SLOW_RISE:
- return "PRIMITIVE_SLOW_RISE";
+ return "SLOW_RISE";
case PRIMITIVE_QUICK_FALL:
- return "PRIMITIVE_QUICK_FALL";
+ return "QUICK_FALL";
case PRIMITIVE_TICK:
- return "PRIMITIVE_TICK";
+ return "TICK";
case PRIMITIVE_LOW_TICK:
- return "PRIMITIVE_LOW_TICK";
+ return "LOW_TICK";
default:
return Integer.toString(id);
}
diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java
index 71ec096..02e6856 100644
--- a/core/java/android/os/VibratorInfo.java
+++ b/core/java/android/os/VibratorInfo.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.hardware.vibrator.Braking;
import android.hardware.vibrator.IVibrator;
+import android.util.IndentingPrintWriter;
import android.util.MathUtils;
import android.util.Range;
import android.util.SparseBooleanArray;
@@ -207,6 +208,25 @@
+ '}';
}
+ /** @hide */
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("VibratorInfo:");
+ pw.increaseIndent();
+ pw.println("id = " + mId);
+ pw.println("capabilities = " + Arrays.toString(getCapabilitiesNames()));
+ pw.println("capabilitiesFlags = " + Long.toBinaryString(mCapabilities));
+ pw.println("supportedEffects = " + Arrays.toString(getSupportedEffectsNames()));
+ pw.println("supportedPrimitives = " + Arrays.toString(getSupportedPrimitivesNames()));
+ pw.println("supportedBraking = " + Arrays.toString(getSupportedBrakingNames()));
+ pw.println("primitiveDelayMax = " + mPrimitiveDelayMax);
+ pw.println("compositionSizeMax = " + mCompositionSizeMax);
+ pw.println("pwlePrimitiveDurationMax = " + mPwlePrimitiveDurationMax);
+ pw.println("pwleSizeMax = " + mPwleSizeMax);
+ pw.println("q-factor = " + mQFactor);
+ pw.println("frequencyProfile = " + mFrequencyProfile);
+ pw.decreaseIndent();
+ }
+
/** Return the id of this vibrator. */
public int getId() {
return mId;
@@ -370,7 +390,7 @@
* Gets the resonant frequency of the vibrator.
*
* @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown or
- * this vibrator is a composite of multiple physical devices.
+ * this vibrator is a composite of multiple physical devices.
*/
public float getResonantFrequencyHz() {
return mFrequencyProfile.mResonantFrequencyHz;
@@ -380,7 +400,7 @@
* Gets the <a href="https://en.wikipedia.org/wiki/Q_factor">Q factor</a> of the vibrator.
*
* @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown or
- * this vibrator is a composite of multiple physical devices.
+ * this vibrator is a composite of multiple physical devices.
*/
public float getQFactor() {
return mQFactor;
diff --git a/core/java/android/os/vibrator/PrebakedSegment.java b/core/java/android/os/vibrator/PrebakedSegment.java
index 462e5ad..42b6c2da 100644
--- a/core/java/android/os/vibrator/PrebakedSegment.java
+++ b/core/java/android/os/vibrator/PrebakedSegment.java
@@ -203,6 +203,15 @@
+ "}";
}
+ /** @hide */
+ @Override
+ public String toDebugString() {
+ return String.format("Prebaked=%s(%s, %s fallback)",
+ VibrationEffect.effectIdToString(mEffectId),
+ VibrationEffect.effectStrengthToString(mEffectStrength),
+ mFallback ? "with" : "no");
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/os/vibrator/PrimitiveSegment.java b/core/java/android/os/vibrator/PrimitiveSegment.java
index 815a1dc..c52a09c 100644
--- a/core/java/android/os/vibrator/PrimitiveSegment.java
+++ b/core/java/android/os/vibrator/PrimitiveSegment.java
@@ -140,6 +140,13 @@
+ '}';
}
+ /** @hide */
+ @Override
+ public String toDebugString() {
+ return String.format("Primitive=%s(scale=%.2f, delay=%dms)",
+ VibrationEffect.Composition.primitiveToString(mPrimitiveId), mScale, mDelay);
+ }
+
@Override
public boolean equals(@Nullable Object o) {
if (this == o) return true;
diff --git a/core/java/android/os/vibrator/RampSegment.java b/core/java/android/os/vibrator/RampSegment.java
index a5dee6c..e997bcd 100644
--- a/core/java/android/os/vibrator/RampSegment.java
+++ b/core/java/android/os/vibrator/RampSegment.java
@@ -179,6 +179,17 @@
+ "}";
}
+ /** @hide */
+ @Override
+ public String toDebugString() {
+ return String.format("Ramp=%dms(amplitude=%.2f%s to %.2f%s)",
+ mDuration,
+ mStartAmplitude,
+ Float.compare(mStartFrequencyHz, 0) == 0 ? "" : " @ " + mStartFrequencyHz + "Hz",
+ mEndAmplitude,
+ Float.compare(mEndFrequencyHz, 0) == 0 ? "" : " @ " + mEndFrequencyHz + "Hz");
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/os/vibrator/StepSegment.java b/core/java/android/os/vibrator/StepSegment.java
index 54a44a8..a585aa8 100644
--- a/core/java/android/os/vibrator/StepSegment.java
+++ b/core/java/android/os/vibrator/StepSegment.java
@@ -161,6 +161,13 @@
+ "}";
}
+ /** @hide */
+ @Override
+ public String toDebugString() {
+ return String.format("Step=%dms(amplitude=%.2f%s)", mDuration, mAmplitude,
+ Float.compare(mFrequencyHz, 0) == 0 ? "" : " @ " + mFrequencyHz + "Hz");
+ }
+
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/os/vibrator/VibrationConfig.java b/core/java/android/os/vibrator/VibrationConfig.java
index 4790d81..bde334a 100644
--- a/core/java/android/os/vibrator/VibrationConfig.java
+++ b/core/java/android/os/vibrator/VibrationConfig.java
@@ -32,6 +32,9 @@
import android.os.VibrationAttributes;
import android.os.Vibrator;
import android.os.Vibrator.VibrationIntensity;
+import android.util.IndentingPrintWriter;
+
+import java.io.PrintWriter;
/**
* List of device-specific internal vibration configuration loaded from platform config.xml.
@@ -191,4 +194,18 @@
+ ", mDefaultRingIntensity=" + mDefaultRingVibrationIntensity
+ "}";
}
+
+ /**
+ * Write current settings into given {@link PrintWriter}, skipping the default settings.
+ *
+ * @hide
+ */
+ public void dumpWithoutDefaultSettings(IndentingPrintWriter pw) {
+ pw.println("VibrationConfig:");
+ pw.increaseIndent();
+ pw.println("hapticChannelMaxAmplitude = " + mHapticChannelMaxVibrationAmplitude);
+ pw.println("rampStepDurationMs = " + mRampStepDurationMs);
+ pw.println("rampDownDurationMs = " + mRampDownDurationMs);
+ pw.decreaseIndent();
+ }
}
diff --git a/core/java/android/os/vibrator/VibrationEffectSegment.java b/core/java/android/os/vibrator/VibrationEffectSegment.java
index 9a19ed4..3b286a7 100644
--- a/core/java/android/os/vibrator/VibrationEffectSegment.java
+++ b/core/java/android/os/vibrator/VibrationEffectSegment.java
@@ -116,6 +116,13 @@
public abstract <T extends VibrationEffectSegment> T applyEffectStrength(int effectStrength);
/**
+ * Returns a compact version of the {@link #toString()} result for debugging purposes.
+ *
+ * @hide
+ */
+ public abstract String toDebugString();
+
+ /**
* Checks the given frequency argument is valid to represent a vibration effect frequency in
* hertz, i.e. a finite non-negative value.
*
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 54dff08..64be87b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3938,8 +3938,14 @@
<!-- Flag indicating device support for EAP SIM, AKA, AKA' -->
<bool name="config_eap_sim_based_auth_supported">true</bool>
+ <!-- How long history of recent vibrations should be kept for the dumpsys. -->
+ <integer name="config_recentVibrationsDumpSizeLimit">20</integer>
+
<!-- How long history of previous vibrations should be kept for the dumpsys. -->
- <integer name="config_previousVibrationsDumpLimit">50</integer>
+ <integer name="config_previousVibrationsDumpSizeLimit">50</integer>
+
+ <!-- How close vibration request should be when they're aggregated for dumpsys, in ms. -->
+ <integer name="config_previousVibrationsDumpAggregationTimeMillisLimit">1000</integer>
<!-- The default vibration strength, must be between 1 and 255 inclusive. -->
<integer name="config_defaultVibrationAmplitude">255</integer>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9da0f17..3aae3ca 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2038,7 +2038,9 @@
<java-symbol type="integer" name="config_notificationsBatteryNearlyFullLevel" />
<java-symbol type="integer" name="config_notificationServiceArchiveSize" />
<java-symbol type="dimen" name="config_rotaryEncoderAxisScrollTickInterval" />
- <java-symbol type="integer" name="config_previousVibrationsDumpLimit" />
+ <java-symbol type="integer" name="config_recentVibrationsDumpSizeLimit" />
+ <java-symbol type="integer" name="config_previousVibrationsDumpSizeLimit" />
+ <java-symbol type="integer" name="config_previousVibrationsDumpAggregationTimeMillisLimit" />
<java-symbol type="integer" name="config_defaultVibrationAmplitude" />
<java-symbol type="dimen" name="config_hapticChannelMaxVibrationAmplitude" />
<java-symbol type="integer" name="config_vibrationWaveformRampStepDuration" />
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index 4f7f13e..fed6e7e 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.media.AudioAttributes;
import android.os.CombinedVibration;
import android.os.IBinder;
import android.os.VibrationAttributes;
@@ -27,8 +28,10 @@
import android.os.vibrator.RampSegment;
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationEffectSegment;
+import android.util.IndentingPrintWriter;
import android.util.proto.ProtoOutputStream;
+import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
@@ -39,7 +42,9 @@
* The base class for all vibrations.
*/
abstract class Vibration {
- private static final SimpleDateFormat DEBUG_DATE_FORMAT =
+ private static final SimpleDateFormat DEBUG_TIME_FORMAT =
+ new SimpleDateFormat("HH:mm:ss.SSS");
+ private static final SimpleDateFormat DEBUG_DATE_TIME_FORMAT =
new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
// Used to generate globally unique vibration ids.
private static final AtomicInteger sNextVibrationId = new AtomicInteger(1); // 0 = no callback
@@ -146,10 +151,10 @@
@Override
public String toString() {
return "CallerInfo{"
- + " attrs=" + attrs
- + ", uid=" + uid
- + ", displayId=" + displayId
+ + " uid=" + uid
+ ", opPkg=" + opPkg
+ + ", displayId=" + displayId
+ + ", attrs=" + attrs
+ ", reason=" + reason
+ '}';
}
@@ -203,14 +208,17 @@
* potentially expensive or resource-linked objects, such as {@link IBinder}.
*/
static final class DebugInfo {
- private final long mCreateTime;
+ final long mCreateTime;
+ final CallerInfo mCallerInfo;
+ @Nullable
+ final CombinedVibration mPlayedEffect;
+
private final long mStartTime;
private final long mEndTime;
private final long mDurationMs;
- @Nullable private final CombinedVibration mOriginalEffect;
- @Nullable private final CombinedVibration mPlayedEffect;
+ @Nullable
+ private final CombinedVibration mOriginalEffect;
private final float mScale;
- private final CallerInfo mCallerInfo;
private final Status mStatus;
DebugInfo(Status status, VibrationStats stats, @Nullable CombinedVibration playedEffect,
@@ -230,10 +238,10 @@
@Override
public String toString() {
- return "createTime: " + DEBUG_DATE_FORMAT.format(new Date(mCreateTime))
- + ", startTime: " + DEBUG_DATE_FORMAT.format(new Date(mStartTime))
+ return "createTime: " + DEBUG_DATE_TIME_FORMAT.format(new Date(mCreateTime))
+ + ", startTime: " + DEBUG_DATE_TIME_FORMAT.format(new Date(mStartTime))
+ ", endTime: "
- + (mEndTime == 0 ? null : DEBUG_DATE_FORMAT.format(new Date(mEndTime)))
+ + (mEndTime == 0 ? null : DEBUG_DATE_TIME_FORMAT.format(new Date(mEndTime)))
+ ", durationMs: " + mDurationMs
+ ", status: " + mStatus.name().toLowerCase(Locale.ROOT)
+ ", playedEffect: " + mPlayedEffect
@@ -242,8 +250,56 @@
+ ", callerInfo: " + mCallerInfo;
}
+ /**
+ * Write this info in a compact way into given {@link PrintWriter}.
+ *
+ * <p>This is used by dumpsys to log multiple vibration records in single lines that are
+ * easy to skim through by the sorted created time.
+ */
+ void dumpCompact(IndentingPrintWriter pw) {
+ boolean isExternalVibration = mPlayedEffect == null;
+ String timingsStr = String.format(Locale.ROOT,
+ "%s | %8s | %20s | duration: %5dms | start: %12s | end: %10s",
+ DEBUG_DATE_TIME_FORMAT.format(new Date(mCreateTime)),
+ isExternalVibration ? "external" : "effect",
+ mStatus.name().toLowerCase(Locale.ROOT),
+ mDurationMs,
+ mStartTime == 0 ? "" : DEBUG_TIME_FORMAT.format(new Date(mStartTime)),
+ mEndTime == 0 ? "" : DEBUG_TIME_FORMAT.format(new Date(mEndTime)));
+ String callerInfoStr = String.format(Locale.ROOT,
+ " | %s (uid=%d, displayId=%d) | usage: %s (audio=%s) | flags: %s | reason: %s",
+ mCallerInfo.opPkg, mCallerInfo.uid, mCallerInfo.displayId,
+ mCallerInfo.attrs.usageToString(),
+ AudioAttributes.usageToString(mCallerInfo.attrs.getAudioUsage()),
+ Long.toBinaryString(mCallerInfo.attrs.getFlags()),
+ mCallerInfo.reason);
+ String effectStr = String.format(Locale.ROOT,
+ " | played: %s | original: %s | scale: %.2f",
+ mPlayedEffect == null ? null : mPlayedEffect.toDebugString(),
+ mOriginalEffect == null ? null : mOriginalEffect.toDebugString(),
+ mScale);
+ pw.println(timingsStr + callerInfoStr + effectStr);
+ }
+
+ /** Write this info into given {@link PrintWriter}. */
+ void dump(IndentingPrintWriter pw) {
+ pw.println("Vibration:");
+ pw.increaseIndent();
+ pw.println("status = " + mStatus.name().toLowerCase(Locale.ROOT));
+ pw.println("durationMs = " + mDurationMs);
+ pw.println("createTime = " + DEBUG_DATE_TIME_FORMAT.format(new Date(mCreateTime)));
+ pw.println("startTime = " + DEBUG_DATE_TIME_FORMAT.format(new Date(mStartTime)));
+ pw.println("endTime = "
+ + (mEndTime == 0 ? null : DEBUG_DATE_TIME_FORMAT.format(new Date(mEndTime))));
+ pw.println("playedEffect = " + mPlayedEffect);
+ pw.println("originalEffect = " + mOriginalEffect);
+ pw.println("scale = " + String.format(Locale.ROOT, "%.2f", mScale));
+ pw.println("callerInfo = " + mCallerInfo);
+ pw.decreaseIndent();
+ }
+
/** Write this info into given {@code fieldId} on {@link ProtoOutputStream}. */
- public void dumpProto(ProtoOutputStream proto, long fieldId) {
+ void dump(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(VibrationProto.START_TIME, mStartTime);
proto.write(VibrationProto.END_TIME, mEndTime);
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index dbd6bf4..db8a9ae 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -54,6 +54,7 @@
import android.os.Vibrator.VibrationIntensity;
import android.os.vibrator.VibrationConfig;
import android.provider.Settings;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -65,6 +66,7 @@
import com.android.server.LocalServices;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -601,8 +603,32 @@
}
}
+ /** Write current settings into given {@link PrintWriter}. */
+ void dump(IndentingPrintWriter pw) {
+ pw.println("VibrationSettings:");
+ pw.increaseIndent();
+ pw.println("vibrateOn = " + mVibrateOn);
+ pw.println("vibrateInputDevices = " + mVibrateInputDevices);
+ pw.println("batterySaverMode = " + mBatterySaverMode);
+ pw.println("VibrationIntensities:");
+
+ pw.increaseIndent();
+ for (int i = 0; i < mCurrentVibrationIntensities.size(); i++) {
+ int usage = mCurrentVibrationIntensities.keyAt(i);
+ int intensity = mCurrentVibrationIntensities.valueAt(i);
+ pw.println(VibrationAttributes.usageToString(usage) + " = "
+ + intensityToString(intensity)
+ + ", default: " + intensityToString(getDefaultIntensity(usage)));
+ }
+ pw.decreaseIndent();
+
+ mVibrationConfig.dumpWithoutDefaultSettings(pw);
+ pw.println("processStateCache = " + mUidObserver.mProcStatesCache);
+ pw.decreaseIndent();
+ }
+
/** Write current settings into given {@link ProtoOutputStream}. */
- public void dumpProto(ProtoOutputStream proto) {
+ void dump(ProtoOutputStream proto) {
synchronized (mLock) {
proto.write(VibratorManagerServiceDumpProto.VIBRATE_ON, mVibrateOn);
proto.write(VibratorManagerServiceDumpProto.LOW_POWER_MODE, mBatterySaverMode);
diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java
index 47b3e1a..f5d4d1e 100644
--- a/services/core/java/com/android/server/vibrator/VibratorController.java
+++ b/services/core/java/com/android/server/vibrator/VibratorController.java
@@ -17,6 +17,7 @@
package com.android.server.vibrator;
import android.annotation.Nullable;
+import android.annotation.Nullable;
import android.hardware.vibrator.IVibrator;
import android.os.Binder;
import android.os.IVibratorStateListener;
@@ -26,6 +27,7 @@
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.PrimitiveSegment;
import android.os.vibrator.RampSegment;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -351,6 +353,19 @@
+ '}';
}
+ void dump(IndentingPrintWriter pw) {
+ pw.println("VibratorController:");
+ pw.increaseIndent();
+ pw.println("isVibrating = " + mIsVibrating);
+ pw.println("isUnderExternalControl = " + mIsUnderExternalControl);
+ pw.println("currentAmplitude = " + mCurrentAmplitude);
+ pw.println("vibratorInfoLoadSuccessful = " + mVibratorInfoLoadSuccessful);
+ pw.println("vibratorStateListenerCount = "
+ + mVibratorStateListeners.getRegisteredCallbackCount());
+ mVibratorInfo.dump(pw);
+ pw.decreaseIndent();
+ }
+
@GuardedBy("mLock")
private void notifyListenerOnVibrating(boolean isVibrating) {
if (mIsVibrating != isVibrating) {
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 6fdb1db..270f7f0c 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -56,6 +56,7 @@
import android.os.vibrator.VibrationEffectSegment;
import android.os.vibrator.persistence.VibrationXmlParser;
import android.text.TextUtils;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
@@ -80,6 +81,7 @@
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
+import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -204,9 +206,15 @@
mNativeWrapper = injector.getNativeWrapper();
mNativeWrapper.init(listener);
- int dumpLimit = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_previousVibrationsDumpLimit);
- mVibratorManagerRecords = new VibratorManagerRecords(dumpLimit);
+ int recentDumpSizeLimit = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_recentVibrationsDumpSizeLimit);
+ int dumpSizeLimit = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_previousVibrationsDumpSizeLimit);
+ int dumpAggregationTimeLimit = mContext.getResources().getInteger(
+ com.android.internal.R.integer
+ .config_previousVibrationsDumpAggregationTimeMillisLimit);
+ mVibratorManagerRecords = new VibratorManagerRecords(
+ recentDumpSizeLimit, dumpSizeLimit, dumpAggregationTimeLimit);
mBatteryStatsService = injector.getBatteryStatsService();
mFrameworkStatsLogger = injector.getFrameworkStatsLogger(mHandler);
@@ -544,49 +552,74 @@
}
}
- private void dumpText(PrintWriter pw) {
+ private void dumpText(PrintWriter w) {
if (DEBUG) {
Slog.d(TAG, "Dumping vibrator manager service to text...");
}
+ IndentingPrintWriter pw = new IndentingPrintWriter(w, /* singleIndent= */ " ");
synchronized (mLock) {
pw.println("Vibrator Manager Service:");
- pw.println(" mVibrationSettings:");
- pw.println(" " + mVibrationSettings);
+ pw.increaseIndent();
+
+ mVibrationSettings.dump(pw);
pw.println();
- pw.println(" mVibratorControllers:");
+
+ pw.println("VibratorControllers:");
+ pw.increaseIndent();
for (int i = 0; i < mVibrators.size(); i++) {
- pw.println(" " + mVibrators.valueAt(i));
+ mVibrators.valueAt(i).dump(pw);
}
+ pw.decreaseIndent();
pw.println();
- pw.println(" mCurrentVibration:");
- pw.println(" " + (mCurrentVibration == null
- ? null : mCurrentVibration.getVibration().getDebugInfo()));
+
+ pw.println("CurrentVibration:");
+ pw.increaseIndent();
+ if (mCurrentVibration != null) {
+ mCurrentVibration.getVibration().getDebugInfo().dump(pw);
+ } else {
+ pw.println("null");
+ }
+ pw.decreaseIndent();
pw.println();
- pw.println(" mNextVibration:");
- pw.println(" " + (mNextVibration == null
- ? null : mNextVibration.getVibration().getDebugInfo()));
+
+ pw.println("NextVibration:");
+ pw.increaseIndent();
+ if (mNextVibration != null) {
+ mNextVibration.getVibration().getDebugInfo().dump(pw);
+ } else {
+ pw.println("null");
+ }
+ pw.decreaseIndent();
pw.println();
- pw.println(" mCurrentExternalVibration:");
- pw.println(" " + (mCurrentExternalVibration == null
- ? null : mCurrentExternalVibration.getDebugInfo()));
- pw.println();
+
+ pw.println("CurrentExternalVibration:");
+ pw.increaseIndent();
+ if (mCurrentExternalVibration != null) {
+ mCurrentExternalVibration.getDebugInfo().dump(pw);
+ } else {
+ pw.println("null");
+ }
+ pw.decreaseIndent();
}
- mVibratorManagerRecords.dumpText(pw);
+
+ pw.println();
+ pw.println();
+ mVibratorManagerRecords.dump(pw);
}
- synchronized void dumpProto(FileDescriptor fd) {
+ private 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);
+ mVibrationSettings.dump(proto);
if (mCurrentVibration != null) {
- mCurrentVibration.getVibration().getDebugInfo().dumpProto(proto,
+ mCurrentVibration.getVibration().getDebugInfo().dump(proto,
VibratorManagerServiceDumpProto.CURRENT_VIBRATION);
}
if (mCurrentExternalVibration != null) {
- mCurrentExternalVibration.getDebugInfo().dumpProto(proto,
+ mCurrentExternalVibration.getDebugInfo().dump(proto,
VibratorManagerServiceDumpProto.CURRENT_EXTERNAL_VIBRATION);
}
@@ -601,7 +634,7 @@
proto.write(VibratorManagerServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL,
isUnderExternalControl);
}
- mVibratorManagerRecords.dumpProto(proto);
+ mVibratorManagerRecords.dump(proto);
proto.flush();
}
@@ -1581,64 +1614,105 @@
/** Keep records of vibrations played and provide debug information for this service. */
private static final class VibratorManagerRecords {
- private final SparseArray<LinkedList<Vibration.DebugInfo>> mPreviousVibrations =
- new SparseArray<>();
- private final LinkedList<Vibration.DebugInfo> mPreviousExternalVibrations =
- new LinkedList<>();
- private final int mPreviousVibrationsLimit;
+ private final VibrationRecords mAggregatedVibrationHistory;
+ private final VibrationRecords mRecentVibrations;
- VibratorManagerRecords(int limit) {
- mPreviousVibrationsLimit = limit;
+ VibratorManagerRecords(int recentVibrationSizeLimit, int aggregationSizeLimit,
+ int aggregationTimeLimit) {
+ mAggregatedVibrationHistory =
+ new VibrationRecords(aggregationSizeLimit, aggregationTimeLimit);
+ mRecentVibrations = new VibrationRecords(
+ recentVibrationSizeLimit, /* aggregationTimeLimit= */ 0);
}
synchronized void record(HalVibration vib) {
- int usage = vib.callerInfo.attrs.getUsage();
- if (!mPreviousVibrations.contains(usage)) {
- mPreviousVibrations.put(usage, new LinkedList<>());
- }
- record(mPreviousVibrations.get(usage), vib.getDebugInfo());
+ record(vib.getDebugInfo());
}
synchronized void record(ExternalVibrationHolder vib) {
- record(mPreviousExternalVibrations, vib.getDebugInfo());
+ record(vib.getDebugInfo());
}
- synchronized void record(LinkedList<Vibration.DebugInfo> records,
- Vibration.DebugInfo info) {
- if (records.size() > mPreviousVibrationsLimit) {
- records.removeFirst();
+ private synchronized void record(Vibration.DebugInfo info) {
+ AggregatedVibrationRecord removedRecord = mRecentVibrations.record(info);
+ if (removedRecord != null) {
+ mAggregatedVibrationHistory.record(removedRecord.mLatestVibration);
}
- records.addLast(info);
}
- synchronized void dumpText(PrintWriter pw) {
- 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 dump(IndentingPrintWriter pw) {
+ pw.println("Recent vibrations:");
+ pw.increaseIndent();
+ mRecentVibrations.dump(pw);
+ pw.decreaseIndent();
+ pw.println();
+ pw.println();
+
+ pw.println("Aggregated vibration history:");
+ pw.increaseIndent();
+ mAggregatedVibrationHistory.dump(pw);
+ pw.decreaseIndent();
+ }
+
+ synchronized void dump(ProtoOutputStream proto) {
+ mRecentVibrations.dump(proto);
+ }
+ }
+
+ /** Keep records of vibrations played and provide debug information for this service. */
+ private static final class VibrationRecords {
+ private final SparseArray<LinkedList<AggregatedVibrationRecord>> mVibrations =
+ new SparseArray<>();
+ private final int mSizeLimit;
+ private final int mAggregationTimeLimit;
+
+ VibrationRecords(int sizeLimit, int aggregationTimeLimit) {
+ mSizeLimit = sizeLimit;
+ mAggregationTimeLimit = aggregationTimeLimit;
+ }
+
+ synchronized AggregatedVibrationRecord record(Vibration.DebugInfo info) {
+ int usage = info.mCallerInfo.attrs.getUsage();
+ if (!mVibrations.contains(usage)) {
+ mVibrations.put(usage, new LinkedList<>());
+ }
+ LinkedList<AggregatedVibrationRecord> records = mVibrations.get(usage);
+ if (mAggregationTimeLimit > 0 && !records.isEmpty()) {
+ AggregatedVibrationRecord lastRecord = records.getLast();
+ if (lastRecord.mayAggregate(info, mAggregationTimeLimit)) {
+ lastRecord.record(info);
+ return null;
}
}
+ AggregatedVibrationRecord removedRecord = null;
+ if (records.size() > mSizeLimit) {
+ removedRecord = records.removeFirst();
+ }
+ records.addLast(new AggregatedVibrationRecord(info));
+ return removedRecord;
+ }
- pw.println();
- pw.println(" Previous external vibrations:");
- for (Vibration.DebugInfo info : mPreviousExternalVibrations) {
- pw.println(" " + info);
+ synchronized void dump(IndentingPrintWriter pw) {
+ for (int i = 0; i < mVibrations.size(); i++) {
+ pw.println(VibrationAttributes.usageToString(mVibrations.keyAt(i)) + ":");
+ pw.increaseIndent();
+ for (AggregatedVibrationRecord info : mVibrations.valueAt(i)) {
+ info.dump(pw);
+ }
+ pw.decreaseIndent();
+ pw.println();
}
}
- synchronized void dumpProto(ProtoOutputStream proto) {
- for (int i = 0; i < mPreviousVibrations.size(); i++) {
+ synchronized void dump(ProtoOutputStream proto) {
+ for (int i = 0; i < mVibrations.size(); i++) {
long fieldId;
- switch (mPreviousVibrations.keyAt(i)) {
+ switch (mVibrations.keyAt(i)) {
case VibrationAttributes.USAGE_RINGTONE:
fieldId = VibratorManagerServiceDumpProto.PREVIOUS_RING_VIBRATIONS;
break;
case VibrationAttributes.USAGE_NOTIFICATION:
- fieldId = VibratorManagerServiceDumpProto
- .PREVIOUS_NOTIFICATION_VIBRATIONS;
+ fieldId = VibratorManagerServiceDumpProto.PREVIOUS_NOTIFICATION_VIBRATIONS;
break;
case VibrationAttributes.USAGE_ALARM:
fieldId = VibratorManagerServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS;
@@ -1646,18 +1720,70 @@
default:
fieldId = VibratorManagerServiceDumpProto.PREVIOUS_VIBRATIONS;
}
- for (Vibration.DebugInfo info : mPreviousVibrations.valueAt(i)) {
- info.dumpProto(proto, fieldId);
+ for (AggregatedVibrationRecord info : mVibrations.valueAt(i)) {
+ if (info.mLatestVibration.mPlayedEffect == null) {
+ // External vibrations are reported separately in the dump proto
+ info.dump(proto,
+ VibratorManagerServiceDumpProto.PREVIOUS_EXTERNAL_VIBRATIONS);
+ } else {
+ info.dump(proto, fieldId);
+ }
}
}
+ }
- for (Vibration.DebugInfo info : mPreviousExternalVibrations) {
- info.dumpProto(proto,
- VibratorManagerServiceDumpProto.PREVIOUS_EXTERNAL_VIBRATIONS);
+ synchronized void dumpOnSingleField(ProtoOutputStream proto, long fieldId) {
+ for (int i = 0; i < mVibrations.size(); i++) {
+ for (AggregatedVibrationRecord info : mVibrations.valueAt(i)) {
+ info.dump(proto, fieldId);
+ }
}
}
}
+ /**
+ * Record that keeps the last {@link Vibration.DebugInfo} played, aggregating close vibrations
+ * from the same uid that have the same {@link VibrationAttributes} and {@link VibrationEffect}.
+ */
+ private static final class AggregatedVibrationRecord {
+ private final Vibration.DebugInfo mFirstVibration;
+ private Vibration.DebugInfo mLatestVibration;
+ private int mVibrationCount;
+
+ AggregatedVibrationRecord(Vibration.DebugInfo info) {
+ mLatestVibration = mFirstVibration = info;
+ mVibrationCount = 1;
+ }
+
+ synchronized boolean mayAggregate(Vibration.DebugInfo info, long timeLimit) {
+ return Objects.equals(mLatestVibration.mCallerInfo.uid, info.mCallerInfo.uid)
+ && Objects.equals(mLatestVibration.mCallerInfo.attrs, info.mCallerInfo.attrs)
+ && Objects.equals(mLatestVibration.mPlayedEffect, info.mPlayedEffect)
+ && Math.abs(mLatestVibration.mCreateTime - info.mCreateTime) < timeLimit;
+ }
+
+ synchronized void record(Vibration.DebugInfo vib) {
+ mLatestVibration = vib;
+ mVibrationCount++;
+ }
+
+ synchronized void dump(IndentingPrintWriter pw) {
+ mFirstVibration.dumpCompact(pw);
+ if (mVibrationCount == 1) {
+ return;
+ }
+ if (mVibrationCount > 2) {
+ pw.println(
+ "-> Skipping " + (mVibrationCount - 2) + " aggregated vibrations, latest:");
+ }
+ mLatestVibration.dumpCompact(pw);
+ }
+
+ synchronized void dump(ProtoOutputStream proto, long fieldId) {
+ mLatestVibration.dump(proto, fieldId);
+ }
+ }
+
/** Clears mNextVibration if set, ending it cleanly */
@GuardedBy("mLock")
private void clearNextVibrationLocked(Vibration.EndInfo vibrationEndInfo) {
@@ -1676,7 +1802,7 @@
/**
* Ends the external vibration, and clears related service state.
*
- * @param vibrationEndInfo the status and related info to end the associated Vibration with
+ * @param vibrationEndInfo the status and related info to end the associated Vibration
* @param continueExternalControl indicates whether external control will continue. If not, the
* HAL will have external control turned off.
*/