From d9291c0a5daf55121040175dae5bb0a4dbc00a83 Mon Sep 17 00:00:00 2001 From: Lais Andrade <lsandrade@google.com> Date: Tue, 20 Feb 2024 19:50:42 +0000 Subject: [PATCH] Add dumpsys information for VibratorControlService Add debug logs for the VibratorControlService and the cached adaptive haptics scales received from the registered VibratorController. Improvements in dumpsys includes: - Add scale level and adaptive scale values to Vibration.DebugInfo - Add VibrationAttributes category to vibration logs - Add missing fields from VibrationConfig to dumpsys - Add VibrationScaler to dumpsys, including cached adaptive scales - Add keyboard settings and ringer mode to VibrationSettings dumpsys - Simplify processStateCache entry from dumpsys - Rename individual vibrator logs from VibratorController to Vibrator, to avoid confusion with new service name. - Add VibratorControlService logs with pull/push/clear request history Other code changes includes extracting the logic to dump aggregated log entries from VibratorManagerService for reuse in the control service and simplifying the vibration history logs to help debugging. Fix: 305965431 Test: atest GroupedAggregatedLogRecordsTest Change-Id: I745f5b02182b4e730c0eb359cc6a134c8c9299ed --- core/java/android/os/VibrationAttributes.java | 11 +- .../android/os/vibrator/VibrationConfig.java | 14 +- .../vibrator/vibratormanagerservice.proto | 25 +- core/res/res/values/config.xml | 7 + core/res/res/values/symbols.xml | 2 + .../vibrator/GroupedAggregatedLogRecords.java | 182 ++++++++++++ .../android/server/vibrator/HalVibration.java | 28 +- .../vibrator/HapticFeedbackCustomization.java | 8 +- .../HapticFeedbackVibrationProvider.java | 5 - .../android/server/vibrator/Vibration.java | 52 ++-- .../server/vibrator/VibrationScaler.java | 167 ++++++----- .../server/vibrator/VibrationSettings.java | 80 +++-- .../vibrator/VibrationStepConductor.java | 2 +- .../vibrator/VibratorControlService.java | 277 +++++++++++++++--- .../server/vibrator/VibratorController.java | 5 +- .../vibrator/VibratorControllerHolder.java | 2 - .../vibrator/VibratorManagerService.java | 193 +++++------- .../GroupedAggregatedLogRecordsTest.java | 267 +++++++++++++++++ .../server/vibrator/VibrationScalerTest.java | 14 +- .../vibrator/VibratorControlServiceTest.java | 42 +-- .../vibrator/FakeVibratorController.java | 6 +- 21 files changed, 1046 insertions(+), 343 deletions(-) create mode 100644 services/core/java/com/android/server/vibrator/GroupedAggregatedLogRecords.java create mode 100644 services/tests/vibrator/src/com/android/server/vibrator/GroupedAggregatedLogRecordsTest.java diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java index 46705a31f395..9df5b850188f 100644 --- a/core/java/android/os/VibrationAttributes.java +++ b/core/java/android/os/VibrationAttributes.java @@ -288,6 +288,15 @@ public final class VibrationAttributes implements Parcelable { return mUsage; } + /** + * Return the original {@link AudioAttributes} used to create the vibration attributes. + * @hide + */ + @AudioAttributes.AttributeUsage + public int getOriginalAudioUsage() { + return mOriginalAudioUsage; + } + /** * Return the flags. * @return a combined mask of all flags @@ -405,8 +414,8 @@ public final class VibrationAttributes implements Parcelable { return "VibrationAttributes{" + "mUsage=" + usageToString() + ", mAudioUsage= " + AudioAttributes.usageToString(mOriginalAudioUsage) - + ", mFlags=" + mFlags + ", mCategory=" + categoryToString() + + ", mFlags=" + mFlags + '}'; } diff --git a/core/java/android/os/vibrator/VibrationConfig.java b/core/java/android/os/vibrator/VibrationConfig.java index bcdb98282679..a14a2c7e54ca 100644 --- a/core/java/android/os/vibrator/VibrationConfig.java +++ b/core/java/android/os/vibrator/VibrationConfig.java @@ -224,17 +224,19 @@ public class VibrationConfig { @Override public String toString() { return "VibrationConfig{" - + "mHapticChannelMaxVibrationAmplitude=" + mHapticChannelMaxVibrationAmplitude + + "mIgnoreVibrationsOnWirelessCharger=" + mIgnoreVibrationsOnWirelessCharger + + ", mHapticChannelMaxVibrationAmplitude=" + mHapticChannelMaxVibrationAmplitude + ", mRampStepDurationMs=" + mRampStepDurationMs + ", mRampDownDurationMs=" + mRampDownDurationMs + + ", mRequestVibrationParamsForUsages=" + + Arrays.toString(getRequestVibrationParamsForUsagesNames()) + + ", mRequestVibrationParamsTimeoutMs=" + mRequestVibrationParamsTimeoutMs + ", mDefaultAlarmIntensity=" + mDefaultAlarmVibrationIntensity + ", mDefaultHapticFeedbackIntensity=" + mDefaultHapticFeedbackIntensity + ", mDefaultMediaIntensity=" + mDefaultMediaVibrationIntensity + ", mDefaultNotificationIntensity=" + mDefaultNotificationVibrationIntensity + ", mDefaultRingIntensity=" + mDefaultRingVibrationIntensity - + ", mRequestVibrationParamsTimeoutMs=" + mRequestVibrationParamsTimeoutMs - + ", mRequestVibrationParamsForUsages=" + Arrays.toString( - getRequestVibrationParamsForUsagesNames()) + + ", mDefaultKeyboardVibrationEnabled=" + mDefaultKeyboardVibrationEnabled + "}"; } @@ -246,9 +248,13 @@ public class VibrationConfig { public void dumpWithoutDefaultSettings(IndentingPrintWriter pw) { pw.println("VibrationConfig:"); pw.increaseIndent(); + pw.println("ignoreVibrationsOnWirelessCharger = " + mIgnoreVibrationsOnWirelessCharger); pw.println("hapticChannelMaxAmplitude = " + mHapticChannelMaxVibrationAmplitude); pw.println("rampStepDurationMs = " + mRampStepDurationMs); pw.println("rampDownDurationMs = " + mRampDownDurationMs); + pw.println("requestVibrationParamsForUsages = " + + Arrays.toString(getRequestVibrationParamsForUsagesNames())); + pw.println("requestVibrationParamsTimeoutMs = " + mRequestVibrationParamsTimeoutMs); pw.decreaseIndent(); } diff --git a/core/proto/android/server/vibrator/vibratormanagerservice.proto b/core/proto/android/server/vibrator/vibratormanagerservice.proto index db99e5b53875..9151958e2b94 100644 --- a/core/proto/android/server/vibrator/vibratormanagerservice.proto +++ b/core/proto/android/server/vibrator/vibratormanagerservice.proto @@ -79,11 +79,28 @@ message CombinedVibrationEffectProto { repeated int32 delays = 2; } +// Next Tag: 5 message VibrationAttributesProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; optional int32 usage = 1; optional int32 audio_usage = 2; optional int32 flags = 3; + optional int32 category = 4; +} + +// Next Tag: 4 +message VibrationParamProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional VibrationScaleParamProto scale = 1; + optional int64 create_time = 2; + optional bool is_from_request = 3; +} + +// Next Tag: 3 +message VibrationScaleParamProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + optional int32 types_mask = 1; + optional float scale = 2; } // Next Tag: 9 @@ -132,16 +149,19 @@ message VibrationProto { } } -// Next Tag: 25 +// Next Tag: 29 message VibratorManagerServiceDumpProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; repeated int32 vibrator_ids = 1; optional VibrationProto current_vibration = 2; optional bool is_vibrating = 3; + optional int32 is_vibrator_controller_registered = 27; optional VibrationProto current_external_vibration = 4; optional bool vibrator_under_external_control = 5; optional bool low_power_mode = 6; optional bool vibrate_on = 24; + optional bool keyboard_vibration_on = 25; + optional int32 default_vibration_amplitude = 26; optional int32 alarm_intensity = 18; optional int32 alarm_default_intensity = 19; optional int32 haptic_feedback_intensity = 7; @@ -158,5 +178,6 @@ message VibratorManagerServiceDumpProto { repeated VibrationProto previous_notification_vibrations = 14; repeated VibrationProto previous_alarm_vibrations = 15; repeated VibrationProto previous_vibrations = 16; - repeated VibrationProto previous_external_vibrations = 17; + repeated VibrationParamProto previous_vibration_params = 28; + reserved 17; // prev previous_external_vibrations } \ No newline at end of file diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 6134e788be82..967edde83cc9 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4101,6 +4101,13 @@ <!-- How close vibration request should be when they're aggregated for dumpsys, in ms. --> <integer name="config_previousVibrationsDumpAggregationTimeMillisLimit">1000</integer> + <!-- How long history of vibration control service should be kept for the dumpsys. --> + <integer name="config_vibratorControlServiceDumpSizeLimit">50</integer> + + <!-- How close requests to vibration control service should be when they're aggregated for + dumpsys, in ms. --> + <integer name="config_vibratorControlServiceDumpAggregationTimeMillisLimit">60000</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 2f5183fc1455..ee51ed020be6 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2083,6 +2083,8 @@ <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_vibratorControlServiceDumpSizeLimit" /> + <java-symbol type="integer" name="config_vibratorControlServiceDumpAggregationTimeMillisLimit" /> <java-symbol type="integer" name="config_defaultVibrationAmplitude" /> <java-symbol type="dimen" name="config_hapticChannelMaxVibrationAmplitude" /> <java-symbol type="dimen" name="config_keyboardHapticFeedbackFixedAmplitude" /> diff --git a/services/core/java/com/android/server/vibrator/GroupedAggregatedLogRecords.java b/services/core/java/com/android/server/vibrator/GroupedAggregatedLogRecords.java new file mode 100644 index 000000000000..7ee29016a58b --- /dev/null +++ b/services/core/java/com/android/server/vibrator/GroupedAggregatedLogRecords.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vibrator; + +import android.os.SystemClock; +import android.util.IndentingPrintWriter; +import android.util.SparseArray; +import android.util.proto.ProtoOutputStream; + +import java.util.ArrayDeque; + +/** + * A generic grouped list of aggregated log records to be printed in dumpsys. + * + * <p>This can be used to dump history of operations or requests to the vibrator services, e.g. + * vibration requests grouped by usage or vibration parameters sent to the vibrator control service. + * + * @param <T> The type of log entries aggregated in this record. + */ +abstract class GroupedAggregatedLogRecords<T extends GroupedAggregatedLogRecords.SingleLogRecord> { + private final SparseArray<ArrayDeque<AggregatedLogRecord<T>>> mGroupedRecords; + private final int mSizeLimit; + private final int mAggregationTimeLimitMs; + + GroupedAggregatedLogRecords(int sizeLimit, int aggregationTimeLimitMs) { + mGroupedRecords = new SparseArray<>(); + mSizeLimit = sizeLimit; + mAggregationTimeLimitMs = aggregationTimeLimitMs; + } + + /** Prints a header to identify the group to be logged. */ + abstract void dumpGroupHeader(IndentingPrintWriter pw, int groupKey); + + /** Returns the {@link ProtoOutputStream} repeated field id to log records of this group. */ + abstract long findGroupKeyProtoFieldId(int groupKey); + + /** + * Adds given entry to this record list, dropping the oldest record if size limit was reached + * for its group. + * + * @param record The new {@link SingleLogRecord} to be recorded. + * @return The oldest {@link AggregatedLogRecord} entry being dropped from the group list if + * it's full, null otherwise. + */ + final synchronized AggregatedLogRecord<T> add(T record) { + int groupKey = record.getGroupKey(); + if (!mGroupedRecords.contains(groupKey)) { + mGroupedRecords.put(groupKey, new ArrayDeque<>(mSizeLimit)); + } + ArrayDeque<AggregatedLogRecord<T>> records = mGroupedRecords.get(groupKey); + if (mAggregationTimeLimitMs > 0 && !records.isEmpty()) { + AggregatedLogRecord<T> lastAggregatedRecord = records.getLast(); + if (lastAggregatedRecord.mayAggregate(record, mAggregationTimeLimitMs)) { + lastAggregatedRecord.record(record); + return null; + } + } + AggregatedLogRecord<T> removedRecord = null; + if (records.size() >= mSizeLimit) { + removedRecord = records.removeFirst(); + } + records.addLast(new AggregatedLogRecord<>(record)); + return removedRecord; + } + + final synchronized void dump(IndentingPrintWriter pw) { + for (int i = 0; i < mGroupedRecords.size(); i++) { + dumpGroupHeader(pw, mGroupedRecords.keyAt(i)); + pw.increaseIndent(); + for (AggregatedLogRecord<T> records : mGroupedRecords.valueAt(i)) { + records.dump(pw); + } + pw.decreaseIndent(); + pw.println(); + } + } + + final synchronized void dump(ProtoOutputStream proto) { + for (int i = 0; i < mGroupedRecords.size(); i++) { + long fieldId = findGroupKeyProtoFieldId(mGroupedRecords.keyAt(i)); + for (AggregatedLogRecord<T> records : mGroupedRecords.valueAt(i)) { + records.dump(proto, fieldId); + } + } + } + + /** + * Represents an aggregation of log record entries that can be printed in a compact manner. + * + * <p>The aggregation is controlled by a time limit on the difference between the creation time + * of two consecutive entries that {@link SingleLogRecord#mayAggregate}. + * + * @param <T> The type of log entries aggregated in this record. + */ + static final class AggregatedLogRecord<T extends SingleLogRecord> { + private final T mFirst; + private T mLatest; + private int mCount; + + AggregatedLogRecord(T record) { + mLatest = mFirst = record; + mCount = 1; + } + + T getLatest() { + return mLatest; + } + + synchronized boolean mayAggregate(T record, long timeLimitMs) { + long timeDeltaMs = Math.abs(mLatest.getCreateUptimeMs() - record.getCreateUptimeMs()); + return mLatest.mayAggregate(record) && timeDeltaMs < timeLimitMs; + } + + synchronized void record(T record) { + mLatest = record; + mCount++; + } + + synchronized void dump(IndentingPrintWriter pw) { + mFirst.dump(pw); + if (mCount == 1) { + return; + } + if (mCount > 2) { + pw.println("-> Skipping " + (mCount - 2) + " aggregated entries, latest:"); + } + mLatest.dump(pw); + } + + synchronized void dump(ProtoOutputStream proto, long fieldId) { + mFirst.dump(proto, fieldId); + if (mCount > 1) { + mLatest.dump(proto, fieldId); + } + } + } + + /** + * Represents a single log entry that can be grouped and aggregated for compact logging. + * + * <p>Entries are first grouped by an integer group key, and then aggregated with consecutive + * entries of same group within a limited timespan. + */ + interface SingleLogRecord { + + /** The group identifier for this record (e.g. vibration usage). */ + int getGroupKey(); + + /** + * The timestamp in millis that should be used for aggregation of close entries. + * + * <p>Should be {@link SystemClock#uptimeMillis()} to be used for calculations. + */ + long getCreateUptimeMs(); + + /** + * Returns true if this record can be aggregated with the given one (e.g. the represent the + * same vibration request from the same process client). + */ + boolean mayAggregate(SingleLogRecord record); + + /** Writes this record into given {@link IndentingPrintWriter}. */ + void dump(IndentingPrintWriter pw); + + /** Writes this record into given {@link ProtoOutputStream} field. */ + void dump(ProtoOutputStream proto, long fieldId); + } +} diff --git a/services/core/java/com/android/server/vibrator/HalVibration.java b/services/core/java/com/android/server/vibrator/HalVibration.java index 70e2e27a3bae..8f755f4ecec8 100644 --- a/services/core/java/com/android/server/vibrator/HalVibration.java +++ b/services/core/java/com/android/server/vibrator/HalVibration.java @@ -54,12 +54,18 @@ final class HalVibration extends Vibration { /** Vibration status. */ private Vibration.Status mStatus; + /** Reported scale values applied to the vibration effects. */ + private int mScaleLevel; + private float mAdaptiveScale; + HalVibration(@NonNull IBinder token, @NonNull CombinedVibration effect, @NonNull CallerInfo callerInfo) { super(token, callerInfo); mOriginalEffect = effect; mEffectToPlay = effect; mStatus = Vibration.Status.RUNNING; + mScaleLevel = VibrationScaler.SCALE_NONE; + mAdaptiveScale = VibrationScaler.ADAPTIVE_SCALE_NONE; } /** @@ -119,20 +125,24 @@ final class HalVibration extends Vibration { } /** - * Scales the {@link #getEffectToPlay()} and each fallback effect with a scaling transformation. - * - * @param scaler A {@link VibrationEffect.Transformation<Integer>} that takes one of the - * {@code VibrationAttributes.USAGE_*} as the modifier to scale the effect - * based on the user settings. + * Scales the {@link #getEffectToPlay()} and each fallback effect based on the vibration usage. */ - public void scaleEffects(VibrationEffect.Transformation<Integer> scaler) { + public void scaleEffects(VibrationScaler scaler) { int vibrationUsage = callerInfo.attrs.getUsage(); - CombinedVibration newEffect = mEffectToPlay.transform(scaler, vibrationUsage); + + // Save scale values for debugging purposes. + mScaleLevel = scaler.getScaleLevel(vibrationUsage); + mAdaptiveScale = scaler.getAdaptiveHapticsScale(vibrationUsage); + + // Scale all VibrationEffect instances in given CombinedVibration. + CombinedVibration newEffect = mEffectToPlay.transform(scaler::scale, vibrationUsage); if (!Objects.equals(mEffectToPlay, newEffect)) { mEffectToPlay = newEffect; } + + // Scale all fallback VibrationEffect instances that can be used by VibrationThread. for (int i = 0; i < mFallbacks.size(); i++) { - mFallbacks.setValueAt(i, scaler.transform(mFallbacks.valueAt(i), vibrationUsage)); + mFallbacks.setValueAt(i, scaler.scale(mFallbacks.valueAt(i), vibrationUsage)); } } @@ -171,7 +181,7 @@ final class HalVibration extends Vibration { CombinedVibration originalEffect = Objects.equals(mOriginalEffect, mEffectToPlay) ? null : mOriginalEffect; return new Vibration.DebugInfo(mStatus, stats, mEffectToPlay, originalEffect, - /* scale= */ 0, callerInfo); + mScaleLevel, mAdaptiveScale, callerInfo); } /** Return {@link VibrationStats.StatsInfo} with read-only metrics about this vibration. */ diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java b/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java index a34621642bcd..9756094e5af0 100644 --- a/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java +++ b/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java @@ -19,8 +19,8 @@ package com.android.server.vibrator; import android.annotation.Nullable; import android.content.res.Resources; import android.os.VibrationEffect; -import android.os.vibrator.Flags; import android.os.VibratorInfo; +import android.os.vibrator.Flags; import android.os.vibrator.persistence.ParsedVibration; import android.os.vibrator.persistence.VibrationXmlParser; import android.text.TextUtils; @@ -73,8 +73,6 @@ import java.io.IOException; * * <p>After a successful parsing of the customization XML file, it returns a {@link SparseArray} * that maps each customized haptic feedback effect ID to its respective {@link VibrationEffect}. - * - * @hide */ final class HapticFeedbackCustomization { private static final String TAG = "HapticFeedbackCustomization"; @@ -104,8 +102,6 @@ final class HapticFeedbackCustomization { * @throws {@link IOException} if an IO error occurs while parsing the customization XML. * @throws {@link CustomizationParserException} for any non-IO error that occurs when parsing * the XML, like an invalid XML content or an invalid haptic feedback constant. - * - * @hide */ @Nullable static SparseArray<VibrationEffect> loadVibrations(Resources res, VibratorInfo vibratorInfo) @@ -202,8 +198,6 @@ final class HapticFeedbackCustomization { /** * Represents an error while parsing a haptic feedback customization XML. - * - * @hide */ static final class CustomizationParserException extends Exception { private CustomizationParserException(String message) { diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java index 519acec2f7b4..21c290abb1fe 100644 --- a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java +++ b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java @@ -34,8 +34,6 @@ import java.io.PrintWriter; /** * Provides the {@link VibrationEffect} and {@link VibrationAttributes} for haptic feedback. - * - * @hide */ public final class HapticFeedbackVibrationProvider { private static final String TAG = "HapticFeedbackVibrationProvider"; @@ -58,17 +56,14 @@ public final class HapticFeedbackVibrationProvider { private float mKeyboardVibrationFixedAmplitude; - /** @hide */ public HapticFeedbackVibrationProvider(Resources res, Vibrator vibrator) { this(res, vibrator.getInfo()); } - /** @hide */ public HapticFeedbackVibrationProvider(Resources res, VibratorInfo vibratorInfo) { this(res, vibratorInfo, loadHapticCustomizations(res, vibratorInfo)); } - /** @hide */ @VisibleForTesting HapticFeedbackVibrationProvider( Resources res, VibratorInfo vibratorInfo, diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java index b2e808ac8e95..b490f57a936e 100644 --- a/services/core/java/com/android/server/vibrator/Vibration.java +++ b/services/core/java/com/android/server/vibrator/Vibration.java @@ -218,12 +218,13 @@ abstract class Vibration { private final long mDurationMs; @Nullable private final CombinedVibration mOriginalEffect; - private final float mScale; + private final int mScaleLevel; + private final float mAdaptiveScale; private final Status mStatus; DebugInfo(Status status, VibrationStats stats, @Nullable CombinedVibration playedEffect, - @Nullable CombinedVibration originalEffect, float scale, - @NonNull CallerInfo callerInfo) { + @Nullable CombinedVibration originalEffect, int scaleLevel, + float adaptiveScale, @NonNull CallerInfo callerInfo) { Objects.requireNonNull(callerInfo); mCreateTime = stats.getCreateTimeDebug(); mStartTime = stats.getStartTimeDebug(); @@ -231,7 +232,8 @@ abstract class Vibration { mDurationMs = stats.getDurationDebug(); mPlayedEffect = playedEffect; mOriginalEffect = originalEffect; - mScale = scale; + mScaleLevel = scaleLevel; + mAdaptiveScale = adaptiveScale; mCallerInfo = callerInfo; mStatus = status; } @@ -246,7 +248,8 @@ abstract class Vibration { + ", status: " + mStatus.name().toLowerCase(Locale.ROOT) + ", playedEffect: " + mPlayedEffect + ", originalEffect: " + mOriginalEffect - + ", scale: " + String.format(Locale.ROOT, "%.2f", mScale) + + ", scaleLevel: " + VibrationScaler.scaleLevelToString(mScaleLevel) + + ", adaptiveScale: " + String.format(Locale.ROOT, "%.2f", mAdaptiveScale) + ", callerInfo: " + mCallerInfo; } @@ -259,26 +262,39 @@ abstract class Vibration { void dumpCompact(IndentingPrintWriter pw) { boolean isExternalVibration = mPlayedEffect == null; String timingsStr = String.format(Locale.ROOT, - "%s | %8s | %20s | duration: %5dms | start: %12s | end: %10s", + "%s | %8s | %20s | duration: %5dms | start: %12s | end: %12s", 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, deviceId=%d) | usage: %s (audio=%s) | flags: %s | reason: %s", - mCallerInfo.opPkg, mCallerInfo.uid, mCallerInfo.deviceId, - mCallerInfo.attrs.usageToString(), - AudioAttributes.usageToString(mCallerInfo.attrs.getAudioUsage()), + String paramStr = String.format(Locale.ROOT, + " | scale: %8s (adaptive=%.2f) | flags: %4s | usage: %s", + VibrationScaler.scaleLevelToString(mScaleLevel), mAdaptiveScale, Long.toBinaryString(mCallerInfo.attrs.getFlags()), - mCallerInfo.reason); + mCallerInfo.attrs.usageToString()); + // Optional, most vibrations have category unknown so skip them to simplify the logs + String categoryStr = + mCallerInfo.attrs.getCategory() != VibrationAttributes.CATEGORY_UNKNOWN + ? " | category=" + VibrationAttributes.categoryToString( + mCallerInfo.attrs.getCategory()) + : ""; + // Optional, most vibrations should not be defined via AudioAttributes + // so skip them to simplify the logs + String audioUsageStr = + mCallerInfo.attrs.getOriginalAudioUsage() != AudioAttributes.USAGE_UNKNOWN + ? " | audioUsage=" + AudioAttributes.usageToString( + mCallerInfo.attrs.getOriginalAudioUsage()) + : ""; + String callerStr = String.format(Locale.ROOT, + " | %s (uid=%d, deviceId=%d) | reason: %s", + mCallerInfo.opPkg, mCallerInfo.uid, mCallerInfo.deviceId, mCallerInfo.reason); String effectStr = String.format(Locale.ROOT, - " | played: %s | original: %s | scale: %.2f", + " | played: %s | original: %s", mPlayedEffect == null ? null : mPlayedEffect.toDebugString(), - mOriginalEffect == null ? null : mOriginalEffect.toDebugString(), - mScale); - pw.println(timingsStr + callerInfoStr + effectStr); + mOriginalEffect == null ? null : mOriginalEffect.toDebugString()); + pw.println(timingsStr + paramStr + categoryStr + audioUsageStr + callerStr + effectStr); } /** Write this info into given {@link PrintWriter}. */ @@ -293,7 +309,8 @@ abstract class Vibration { + (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("scale = " + VibrationScaler.scaleLevelToString(mScaleLevel)); + pw.println("adaptiveScale = " + String.format(Locale.ROOT, "%.2f", mAdaptiveScale)); pw.println("callerInfo = " + mCallerInfo); pw.decreaseIndent(); } @@ -310,6 +327,7 @@ abstract class Vibration { final VibrationAttributes attrs = mCallerInfo.attrs; proto.write(VibrationAttributesProto.USAGE, attrs.getUsage()); proto.write(VibrationAttributesProto.AUDIO_USAGE, attrs.getAudioUsage()); + proto.write(VibrationAttributesProto.CATEGORY, attrs.getCategory()); proto.write(VibrationAttributesProto.FLAGS, attrs.getFlags()); proto.end(attrsToken); diff --git a/services/core/java/com/android/server/vibrator/VibrationScaler.java b/services/core/java/com/android/server/vibrator/VibrationScaler.java index 5d17884c769b..d9ca71003aae 100644 --- a/services/core/java/com/android/server/vibrator/VibrationScaler.java +++ b/services/core/java/com/android/server/vibrator/VibrationScaler.java @@ -26,10 +26,14 @@ import android.os.Vibrator; import android.os.vibrator.Flags; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.VibrationEffectSegment; +import android.util.IndentingPrintWriter; import android.util.Slog; import android.util.SparseArray; +import android.util.proto.ProtoOutputStream; +import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Locale; /** Controls vibration scaling. */ final class VibrationScaler { @@ -37,13 +41,12 @@ final class VibrationScaler { // Scale levels. Each level, except MUTE, is defined as the delta between the current setting // and the default intensity for that type of vibration (i.e. current - default). - private static final int SCALE_VERY_LOW = - ExternalVibrationScale.ScaleLevel.SCALE_VERY_LOW; // -2 - private static final int SCALE_LOW = ExternalVibrationScale.ScaleLevel.SCALE_LOW; // -1 - private static final int SCALE_NONE = ExternalVibrationScale.ScaleLevel.SCALE_NONE; // 0 - private static final int SCALE_HIGH = ExternalVibrationScale.ScaleLevel.SCALE_HIGH; // 1 - private static final int SCALE_VERY_HIGH = - ExternalVibrationScale.ScaleLevel.SCALE_VERY_HIGH; // 2 + static final int SCALE_VERY_LOW = ExternalVibrationScale.ScaleLevel.SCALE_VERY_LOW; // -2 + static final int SCALE_LOW = ExternalVibrationScale.ScaleLevel.SCALE_LOW; // -1 + static final int SCALE_NONE = ExternalVibrationScale.ScaleLevel.SCALE_NONE; // 0 + static final int SCALE_HIGH = ExternalVibrationScale.ScaleLevel.SCALE_HIGH; // 1 + static final int SCALE_VERY_HIGH = ExternalVibrationScale.ScaleLevel.SCALE_VERY_HIGH; // 2 + static final float ADAPTIVE_SCALE_NONE = 1f; // Scale factors for each level. private static final float SCALE_FACTOR_VERY_LOW = 0.6f; @@ -52,6 +55,8 @@ final class VibrationScaler { private static final float SCALE_FACTOR_HIGH = 1.2f; private static final float SCALE_FACTOR_VERY_HIGH = 1.4f; + private static final ScaleLevel SCALE_LEVEL_NONE = new ScaleLevel(SCALE_FACTOR_NONE); + // A mapping from the intensity adjustment to the scaling to apply, where the intensity // adjustment is defined as the delta between the default intensity level and the user selected // intensity level. It's important that we apply the scaling on the delta between the two so @@ -69,7 +74,7 @@ final class VibrationScaler { mScaleLevels = new SparseArray<>(); mScaleLevels.put(SCALE_VERY_LOW, new ScaleLevel(SCALE_FACTOR_VERY_LOW)); mScaleLevels.put(SCALE_LOW, new ScaleLevel(SCALE_FACTOR_LOW)); - mScaleLevels.put(SCALE_NONE, new ScaleLevel(SCALE_FACTOR_NONE)); + mScaleLevels.put(SCALE_NONE, SCALE_LEVEL_NONE); mScaleLevels.put(SCALE_HIGH, new ScaleLevel(SCALE_FACTOR_HIGH)); mScaleLevels.put(SCALE_VERY_HIGH, new ScaleLevel(SCALE_FACTOR_VERY_HIGH)); } @@ -87,25 +92,24 @@ final class VibrationScaler { * @param usageHint one of VibrationAttributes.USAGE_* * @return one of ExternalVibrationScale.ScaleLevel.SCALE_* */ - public int getExternalVibrationScaleLevel(int usageHint) { + public int getScaleLevel(int usageHint) { int defaultIntensity = mSettingsController.getDefaultIntensity(usageHint); int currentIntensity = mSettingsController.getCurrentIntensity(usageHint); - if (currentIntensity == Vibrator.VIBRATION_INTENSITY_OFF) { // Bypassing user settings, or it has changed between checking and scaling. Use default. return SCALE_NONE; } int scaleLevel = currentIntensity - defaultIntensity; - if (scaleLevel >= SCALE_VERY_LOW && scaleLevel <= SCALE_VERY_HIGH) { return scaleLevel; - } else { - // Something about our scaling has gone wrong, so just play with no scaling. - Slog.w(TAG, "Error in scaling calculations, ended up with invalid scale level " - + scaleLevel + " for vibration with usage " + usageHint); - return SCALE_NONE; } + + // Something about our scaling has gone wrong, so just play with no scaling. + Slog.wtf(TAG, "Error in scaling calculations, ended up with invalid scale level " + + scaleLevel + " for vibration with usage " + usageHint); + + return SCALE_NONE; } /** @@ -117,11 +121,9 @@ final class VibrationScaler { * @return The adaptive haptics scale. */ public float getAdaptiveHapticsScale(int usageHint) { - if (shouldApplyAdaptiveHapticsScale(usageHint)) { - return mAdaptiveHapticsScales.get(usageHint); - } - - return 1f; // no scaling + return Flags.adaptiveHapticsEnabled() + ? mAdaptiveHapticsScales.get(usageHint, ADAPTIVE_SCALE_NONE) + : ADAPTIVE_SCALE_NONE; } /** @@ -140,21 +142,16 @@ final class VibrationScaler { return effect; } - int defaultIntensity = mSettingsController.getDefaultIntensity(usageHint); - int currentIntensity = mSettingsController.getCurrentIntensity(usageHint); - - if (currentIntensity == Vibrator.VIBRATION_INTENSITY_OFF) { - // Bypassing user settings, or it has changed between checking and scaling. Use default. - currentIntensity = defaultIntensity; - } - - int newEffectStrength = intensityToEffectStrength(currentIntensity); - ScaleLevel scaleLevel = mScaleLevels.get(currentIntensity - defaultIntensity); + int newEffectStrength = getEffectStrength(usageHint); + ScaleLevel scaleLevel = mScaleLevels.get(getScaleLevel(usageHint)); + float adaptiveScale = getAdaptiveHapticsScale(usageHint); if (scaleLevel == null) { // Something about our scaling has gone wrong, so just play with no scaling. - Slog.e(TAG, "No configured scaling level!" - + " (current=" + currentIntensity + ", default= " + defaultIntensity + ")"); + Slog.e(TAG, "No configured scaling level found! (current=" + + mSettingsController.getCurrentIntensity(usageHint) + ", default= " + + mSettingsController.getDefaultIntensity(usageHint) + ")"); + scaleLevel = SCALE_LEVEL_NONE; } VibrationEffect.Composed composedEffect = (VibrationEffect.Composed) effect; @@ -162,20 +159,11 @@ final class VibrationScaler { new ArrayList<>(composedEffect.getSegments()); int segmentCount = segments.size(); for (int i = 0; i < segmentCount; i++) { - VibrationEffectSegment segment = segments.get(i); - segment = segment.resolve(mDefaultVibrationAmplitude) - .applyEffectStrength(newEffectStrength); - if (scaleLevel != null) { - segment = segment.scale(scaleLevel.factor); - } - - // If adaptive haptics scaling is available for this usage, apply it to the segment. - if (shouldApplyAdaptiveHapticsScale(usageHint)) { - float adaptiveScale = mAdaptiveHapticsScales.get(usageHint); - segment = segment.scaleLinearly(adaptiveScale); - } - - segments.set(i, segment); + segments.set(i, + segments.get(i).resolve(mDefaultVibrationAmplitude) + .applyEffectStrength(newEffectStrength) + .scale(scaleLevel.factor) + .scaleLinearly(adaptiveScale)); } if (segments.equals(composedEffect.getSegments())) { // No segment was updated, return original effect. @@ -197,15 +185,7 @@ final class VibrationScaler { * updated effect strength */ public PrebakedSegment scale(PrebakedSegment prebaked, int usageHint) { - int currentIntensity = mSettingsController.getCurrentIntensity(usageHint); - - if (currentIntensity == Vibrator.VIBRATION_INTENSITY_OFF) { - // Bypassing user settings, or it has changed between checking and scaling. Use default. - currentIntensity = mSettingsController.getDefaultIntensity(usageHint); - } - - int newEffectStrength = intensityToEffectStrength(currentIntensity); - return prebaked.applyEffectStrength(newEffectStrength); + return prebaked.applyEffectStrength(getEffectStrength(usageHint)); } /** @@ -213,8 +193,6 @@ final class VibrationScaler { * * @param usageHint one of VibrationAttributes.USAGE_*. * @param scale The scaling factor that should be applied to vibrations of this usage. - * - * @hide */ public void updateAdaptiveHapticsScale(@VibrationAttributes.Usage int usageHint, float scale) { mAdaptiveHapticsScales.put(usageHint, scale); @@ -224,24 +202,68 @@ final class VibrationScaler { * Removes the usage from the cached adaptive haptics scales list. * * @param usageHint one of VibrationAttributes.USAGE_*. - * - * @hide */ public void removeAdaptiveHapticsScale(@VibrationAttributes.Usage int usageHint) { mAdaptiveHapticsScales.remove(usageHint); } - /** - * Removes all cached adaptive haptics scales. - * - * @hide - */ + /** Removes all cached adaptive haptics scales. */ public void clearAdaptiveHapticsScales() { mAdaptiveHapticsScales.clear(); } - private boolean shouldApplyAdaptiveHapticsScale(int usageHint) { - return Flags.adaptiveHapticsEnabled() && mAdaptiveHapticsScales.contains(usageHint); + /** Write current settings into given {@link PrintWriter}. */ + void dump(IndentingPrintWriter pw) { + pw.println("VibrationScaler:"); + pw.increaseIndent(); + pw.println("defaultVibrationAmplitude = " + mDefaultVibrationAmplitude); + + pw.println("ScaleLevels:"); + pw.increaseIndent(); + for (int i = 0; i < mScaleLevels.size(); i++) { + int scaleLevelKey = mScaleLevels.keyAt(i); + ScaleLevel scaleLevel = mScaleLevels.valueAt(i); + pw.println(scaleLevelToString(scaleLevelKey) + " = " + scaleLevel); + } + pw.decreaseIndent(); + + pw.println("AdaptiveHapticsScales:"); + pw.increaseIndent(); + for (int i = 0; i < mAdaptiveHapticsScales.size(); i++) { + int usage = mAdaptiveHapticsScales.keyAt(i); + float scale = mAdaptiveHapticsScales.valueAt(i); + pw.println(VibrationAttributes.usageToString(usage) + + " = " + String.format(Locale.ROOT, "%.2f", scale)); + } + pw.decreaseIndent(); + + pw.decreaseIndent(); + } + + /** Write current settings into given {@link ProtoOutputStream}. */ + void dump(ProtoOutputStream proto) { + proto.write(VibratorManagerServiceDumpProto.DEFAULT_VIBRATION_AMPLITUDE, + mDefaultVibrationAmplitude); + } + + @Override + public String toString() { + return "VibrationScaler{" + + "mScaleLevels=" + mScaleLevels + + ", mDefaultVibrationAmplitude=" + mDefaultVibrationAmplitude + + ", mAdaptiveHapticsScales=" + mAdaptiveHapticsScales + + '}'; + } + + private int getEffectStrength(int usageHint) { + int currentIntensity = mSettingsController.getCurrentIntensity(usageHint); + + if (currentIntensity == Vibrator.VIBRATION_INTENSITY_OFF) { + // Bypassing user settings, or it has changed between checking and scaling. Use default. + currentIntensity = mSettingsController.getDefaultIntensity(usageHint); + } + + return intensityToEffectStrength(currentIntensity); } /** Mapping of Vibrator.VIBRATION_INTENSITY_* values to {@link EffectStrength}. */ @@ -259,6 +281,17 @@ final class VibrationScaler { } } + static String scaleLevelToString(int scaleLevel) { + return switch (scaleLevel) { + case SCALE_VERY_LOW -> "VERY_LOW"; + case SCALE_LOW -> "LOW"; + case SCALE_NONE -> "NONE"; + case SCALE_HIGH -> "HIGH"; + case SCALE_VERY_HIGH -> "VERY_HIGH"; + default -> String.valueOf(scaleLevel); + }; + } + /** Represents the scale that must be applied to a vibration effect intensity. */ private static final class ScaleLevel { public final float factor; diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java index 99ce3e2fb740..5b77433fa6d9 100644 --- a/services/core/java/com/android/server/vibrator/VibrationSettings.java +++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java @@ -386,7 +386,6 @@ final class VibrationSettings { * Returns the duration, in milliseconds, that the vibrator control service will wait for new * vibration params. * @return The request vibration params timeout in milliseconds. - * @hide */ public int getRequestVibrationParamsTimeoutMs() { return mVibrationConfig.getRequestVibrationParamsTimeoutMs(); @@ -645,11 +644,16 @@ final class VibrationSettings { .append("), "); } vibrationIntensitiesString.append('}'); + String keyboardVibrationOnString = mKeyboardVibrationOn + + " (default: " + mVibrationConfig.isDefaultKeyboardVibrationEnabled() + ")"; return "VibrationSettings{" + "mVibratorConfig=" + mVibrationConfig + + ", mVibrateOn=" + mVibrateOn + + ", mKeyboardVibrationOn=" + keyboardVibrationOnString + ", mVibrateInputDevices=" + mVibrateInputDevices + ", mBatterySaverMode=" + mBatterySaverMode - + ", mVibrateOn=" + mVibrateOn + + ", mRingerMode=" + ringerModeToString(mRingerMode) + + ", mOnWirelessCharger=" + mOnWirelessCharger + ", mVibrationIntensities=" + vibrationIntensitiesString + ", mProcStatesCache=" + mUidObserver.mProcStatesCache + '}'; @@ -658,32 +662,40 @@ final class VibrationSettings { /** 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:"); + synchronized (mLock) { + pw.println("VibrationSettings:"); + pw.increaseIndent(); + pw.println("vibrateOn = " + mVibrateOn); + pw.println("keyboardVibrationOn = " + mKeyboardVibrationOn + + ", default: " + mVibrationConfig.isDefaultKeyboardVibrationEnabled()); + pw.println("vibrateInputDevices = " + mVibrateInputDevices); + pw.println("batterySaverMode = " + mBatterySaverMode); + pw.println("ringerMode = " + ringerModeToString(mRingerMode)); + pw.println("onWirelessCharger = " + mOnWirelessCharger); + pw.println("processStateCache size = " + mUidObserver.mProcStatesCache.size()); + + 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(); - 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))); + mVibrationConfig.dumpWithoutDefaultSettings(pw); + pw.decreaseIndent(); } - pw.decreaseIndent(); - - mVibrationConfig.dumpWithoutDefaultSettings(pw); - pw.println("processStateCache = " + mUidObserver.mProcStatesCache); - pw.decreaseIndent(); } /** Write current settings into given {@link ProtoOutputStream}. */ void dump(ProtoOutputStream proto) { synchronized (mLock) { proto.write(VibratorManagerServiceDumpProto.VIBRATE_ON, mVibrateOn); + proto.write(VibratorManagerServiceDumpProto.KEYBOARD_VIBRATION_ON, + mKeyboardVibrationOn); proto.write(VibratorManagerServiceDumpProto.LOW_POWER_MODE, mBatterySaverMode); proto.write(VibratorManagerServiceDumpProto.ALARM_INTENSITY, getCurrentIntensity(USAGE_ALARM)); @@ -723,18 +735,22 @@ final class VibrationSettings { } private static String intensityToString(int intensity) { - switch (intensity) { - case Vibrator.VIBRATION_INTENSITY_OFF: - return "OFF"; - case Vibrator.VIBRATION_INTENSITY_LOW: - return "LOW"; - case Vibrator.VIBRATION_INTENSITY_MEDIUM: - return "MEDIUM"; - case Vibrator.VIBRATION_INTENSITY_HIGH: - return "HIGH"; - default: - return "UNKNOWN INTENSITY " + intensity; - } + return switch (intensity) { + case Vibrator.VIBRATION_INTENSITY_OFF -> "OFF"; + case Vibrator.VIBRATION_INTENSITY_LOW -> "LOW"; + case Vibrator.VIBRATION_INTENSITY_MEDIUM -> "MEDIUM"; + case Vibrator.VIBRATION_INTENSITY_HIGH -> "HIGH"; + default -> "UNKNOWN INTENSITY " + intensity; + }; + } + + private static String ringerModeToString(int ringerMode) { + return switch (ringerMode) { + case AudioManager.RINGER_MODE_SILENT -> "silent"; + case AudioManager.RINGER_MODE_VIBRATE -> "vibrate"; + case AudioManager.RINGER_MODE_NORMAL -> "normal"; + default -> String.valueOf(ringerMode); + }; } @VibrationIntensity diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java index f6af9ad991ff..f510b4e8ab30 100644 --- a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java +++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java @@ -161,7 +161,7 @@ final class VibrationStepConductor implements IBinder.DeathRecipient { waitForVibrationParamsIfRequired(); } // Scale resolves the default amplitudes from the effect before scaling them. - mVibration.scaleEffects(mVibrationScaler::scale); + mVibration.scaleEffects(mVibrationScaler); } else { mVibration.resolveEffects(mVibrationScaler.getDefaultVibrationAmplitude()); } diff --git a/services/core/java/com/android/server/vibrator/VibratorControlService.java b/services/core/java/com/android/server/vibrator/VibratorControlService.java index 17a9e3330375..ec3d99b24656 100644 --- a/services/core/java/com/android/server/vibrator/VibratorControlService.java +++ b/services/core/java/com/android/server/vibrator/VibratorControlService.java @@ -30,6 +30,7 @@ import static android.os.VibrationAttributes.USAGE_UNKNOWN; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; +import android.content.Context; import android.frameworks.vibrator.IVibratorControlService; import android.frameworks.vibrator.IVibratorController; import android.frameworks.vibrator.ScaleParam; @@ -37,27 +38,38 @@ import android.frameworks.vibrator.VibrationParam; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.os.SystemClock; import android.os.VibrationAttributes; +import android.os.VibrationEffect; +import android.util.IndentingPrintWriter; +import android.util.IntArray; import android.util.Slog; +import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; import java.util.Objects; import java.util.concurrent.CompletableFuture; /** * Implementation of {@link IVibratorControlService} which allows the registration of * {@link IVibratorController} to set and receive vibration params. - * - * @hide */ -public final class VibratorControlService extends IVibratorControlService.Stub { +final class VibratorControlService extends IVibratorControlService.Stub { private static final String TAG = "VibratorControlService"; private static final int UNRECOGNIZED_VIBRATION_TYPE = -1; private static final int NO_SCALE = -1; + private static final SimpleDateFormat DEBUG_DATE_TIME_FORMAT = + new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); + + private final VibrationParamsRecords mVibrationParamsRecords; private final VibratorControllerHolder mVibratorControllerHolder; private final VibrationScaler mVibrationScaler; private final Object mLock; @@ -68,25 +80,32 @@ public final class VibratorControlService extends IVibratorControlService.Stub { @GuardedBy("mLock") private IBinder mRequestVibrationParamsToken; - public VibratorControlService(VibratorControllerHolder vibratorControllerHolder, - VibrationScaler vibrationScaler, VibrationSettings vibrationSettings, Object lock) { + VibratorControlService(Context context, + VibratorControllerHolder vibratorControllerHolder, VibrationScaler vibrationScaler, + VibrationSettings vibrationSettings, Object lock) { mVibratorControllerHolder = vibratorControllerHolder; mVibrationScaler = vibrationScaler; mLock = lock; mRequestVibrationParamsForUsages = vibrationSettings.getRequestVibrationParamsForUsages(); + + int dumpSizeLimit = context.getResources().getInteger( + com.android.internal.R.integer.config_previousVibrationsDumpSizeLimit); + int dumpAggregationTimeLimit = context.getResources().getInteger( + com.android.internal.R.integer + .config_previousVibrationsDumpAggregationTimeMillisLimit); + mVibrationParamsRecords = + new VibrationParamsRecords(dumpSizeLimit, dumpAggregationTimeLimit); } @Override - public void registerVibratorController(IVibratorController controller) - throws RemoteException { + public void registerVibratorController(IVibratorController controller) { synchronized (mLock) { mVibratorControllerHolder.setVibratorController(controller); } } @Override - public void unregisterVibratorController(@NonNull IVibratorController controller) - throws RemoteException { + public void unregisterVibratorController(@NonNull IVibratorController controller) { Objects.requireNonNull(controller); synchronized (mLock) { @@ -110,7 +129,7 @@ public final class VibratorControlService extends IVibratorControlService.Stub { @Override public void setVibrationParams(@SuppressLint("ArrayReturn") VibrationParam[] params, - @NonNull IVibratorController token) throws RemoteException { + @NonNull IVibratorController token) { Objects.requireNonNull(token); synchronized (mLock) { @@ -128,12 +147,12 @@ public final class VibratorControlService extends IVibratorControlService.Stub { } updateAdaptiveHapticsScales(params); + recordUpdateVibrationParams(params, /* fromRequest= */ false); } } @Override - public void clearVibrationParams(int types, @NonNull IVibratorController token) - throws RemoteException { + public void clearVibrationParams(int types, @NonNull IVibratorController token) { Objects.requireNonNull(token); synchronized (mLock) { @@ -151,13 +170,13 @@ public final class VibratorControlService extends IVibratorControlService.Stub { } updateAdaptiveHapticsScales(types, NO_SCALE); + recordClearVibrationParams(types); } } @Override public void onRequestVibrationParamsComplete( - @NonNull IBinder requestToken, @SuppressLint("ArrayReturn") VibrationParam[] result) - throws RemoteException { + @NonNull IBinder requestToken, @SuppressLint("ArrayReturn") VibrationParam[] result) { Objects.requireNonNull(requestToken); synchronized (mLock) { @@ -177,16 +196,17 @@ public final class VibratorControlService extends IVibratorControlService.Stub { updateAdaptiveHapticsScales(result); endOngoingRequestVibrationParamsLocked(/* wasCancelled= */ false); + recordUpdateVibrationParams(result, /* fromRequest= */ true); } } @Override - public int getInterfaceVersion() throws RemoteException { + public int getInterfaceVersion() { return this.VERSION; } @Override - public String getInterfaceHash() throws RemoteException { + public String getInterfaceHash() { return this.HASH; } @@ -266,6 +286,42 @@ public final class VibratorControlService extends IVibratorControlService.Stub { } } + /** Write current settings into given {@link PrintWriter}. */ + void dump(IndentingPrintWriter pw) { + boolean isVibratorControllerRegistered; + boolean hasPendingVibrationParamsRequest; + synchronized (mLock) { + isVibratorControllerRegistered = + mVibratorControllerHolder.getVibratorController() != null; + hasPendingVibrationParamsRequest = mRequestVibrationParamsFuture != null; + } + + pw.println("VibratorControlService:"); + pw.increaseIndent(); + pw.println("isVibratorControllerRegistered = " + isVibratorControllerRegistered); + pw.println("hasPendingVibrationParamsRequest = " + hasPendingVibrationParamsRequest); + + pw.println(); + pw.println("Vibration parameters update history:"); + pw.increaseIndent(); + mVibrationParamsRecords.dump(pw); + pw.decreaseIndent(); + + pw.decreaseIndent(); + } + + /** Write current settings into given {@link ProtoOutputStream}. */ + void dump(ProtoOutputStream proto) { + boolean isVibratorControllerRegistered; + synchronized (mLock) { + isVibratorControllerRegistered = + mVibratorControllerHolder.getVibratorController() != null; + } + proto.write(VibratorManagerServiceDumpProto.IS_VIBRATOR_CONTROLLER_REGISTERED, + isVibratorControllerRegistered); + mVibrationParamsRecords.dump(proto); + } + /** * Completes or cancels the vibration params request future and resets the future and token * to null. @@ -312,6 +368,33 @@ public final class VibratorControlService extends IVibratorControlService.Stub { } } + private static int[] mapFromAdaptiveVibrationTypeToVibrationUsages(int types) { + IntArray usages = new IntArray(15); + if ((ScaleParam.TYPE_ALARM & types) != 0) { + usages.add(USAGE_ALARM); + } + + if ((ScaleParam.TYPE_NOTIFICATION & types) != 0) { + usages.add(USAGE_NOTIFICATION); + usages.add(USAGE_COMMUNICATION_REQUEST); + } + + if ((ScaleParam.TYPE_RINGTONE & types) != 0) { + usages.add(USAGE_RINGTONE); + } + + if ((ScaleParam.TYPE_MEDIA & types) != 0) { + usages.add(USAGE_MEDIA); + usages.add(USAGE_UNKNOWN); + } + + if ((ScaleParam.TYPE_INTERACTIVE & types) != 0) { + usages.add(USAGE_TOUCH); + usages.add(USAGE_HARDWARE_FEEDBACK); + } + return usages.toArray(); + } + /** * Updates the adaptive haptics scales cached in {@link VibrationScaler} with the * provided params. @@ -319,7 +402,14 @@ public final class VibratorControlService extends IVibratorControlService.Stub { * @param params the new vibration params. */ private void updateAdaptiveHapticsScales(@Nullable VibrationParam[] params) { + if (params == null) { + return; + } for (VibrationParam param : params) { + if (param.getTag() != VibrationParam.scale) { + Slog.e(TAG, "Unsupported vibration param: " + param); + continue; + } ScaleParam scaleParam = param.getScale(); updateAdaptiveHapticsScales(scaleParam.typesMask, scaleParam.scale); } @@ -333,27 +423,8 @@ public final class VibratorControlService extends IVibratorControlService.Stub { * @param scale The scaling factor that should be applied to the vibrations. */ private void updateAdaptiveHapticsScales(int types, float scale) { - if ((ScaleParam.TYPE_ALARM & types) != 0) { - updateOrRemoveAdaptiveHapticsScale(USAGE_ALARM, scale); - } - - if ((ScaleParam.TYPE_NOTIFICATION & types) != 0) { - updateOrRemoveAdaptiveHapticsScale(USAGE_NOTIFICATION, scale); - updateOrRemoveAdaptiveHapticsScale(USAGE_COMMUNICATION_REQUEST, scale); - } - - if ((ScaleParam.TYPE_RINGTONE & types) != 0) { - updateOrRemoveAdaptiveHapticsScale(USAGE_RINGTONE, scale); - } - - if ((ScaleParam.TYPE_MEDIA & types) != 0) { - updateOrRemoveAdaptiveHapticsScale(USAGE_MEDIA, scale); - updateOrRemoveAdaptiveHapticsScale(USAGE_UNKNOWN, scale); - } - - if ((ScaleParam.TYPE_INTERACTIVE & types) != 0) { - updateOrRemoveAdaptiveHapticsScale(USAGE_TOUCH, scale); - updateOrRemoveAdaptiveHapticsScale(USAGE_HARDWARE_FEEDBACK, scale); + for (int usage : mapFromAdaptiveVibrationTypeToVibrationUsages(types)) { + updateOrRemoveAdaptiveHapticsScale(usage, scale); } } @@ -375,4 +446,136 @@ public final class VibratorControlService extends IVibratorControlService.Stub { mVibrationScaler.updateAdaptiveHapticsScale(usageHint, scale); } + + private void recordUpdateVibrationParams(@Nullable VibrationParam[] params, + boolean fromRequest) { + if (params == null) { + return; + } + VibrationParamsRecords.Operation operation = + fromRequest ? VibrationParamsRecords.Operation.PULL + : VibrationParamsRecords.Operation.PUSH; + long createTime = SystemClock.uptimeMillis(); + for (VibrationParam param : params) { + if (param.getTag() != VibrationParam.scale) { + Slog.w(TAG, "Unsupported vibration param ignored from dumpsys records: " + param); + continue; + } + ScaleParam scaleParam = param.getScale(); + mVibrationParamsRecords.add(new VibrationScaleParamRecord(operation, createTime, + scaleParam.typesMask, scaleParam.scale)); + } + } + + private void recordClearVibrationParams(int typesMask) { + long createTime = SystemClock.uptimeMillis(); + mVibrationParamsRecords.add(new VibrationScaleParamRecord( + VibrationParamsRecords.Operation.CLEAR, createTime, typesMask, NO_SCALE)); + } + + /** + * Keep records of {@link VibrationParam} values received by this service from a registered + * {@link VibratorController} and provide debug information for this service. + */ + private static final class VibrationParamsRecords + extends GroupedAggregatedLogRecords<VibrationScaleParamRecord> { + + /** The type of operations on vibration parameters that the service is recording. */ + enum Operation { + PULL, PUSH, CLEAR + }; + + VibrationParamsRecords(int sizeLimit, int aggregationTimeLimit) { + super(sizeLimit, aggregationTimeLimit); + } + + @Override + synchronized void dumpGroupHeader(IndentingPrintWriter pw, int paramType) { + if (paramType == VibrationParam.scale) { + pw.println("SCALE:"); + } else { + pw.println("UNKNOWN:"); + } + } + + @Override + synchronized long findGroupKeyProtoFieldId(int usage) { + return VibratorManagerServiceDumpProto.PREVIOUS_VIBRATION_PARAMS; + } + } + + /** + * Record for a single {@link Vibration.DebugInfo}, that can be grouped by usage and aggregated + * by UID, {@link VibrationAttributes} and {@link VibrationEffect}. + */ + private static final class VibrationScaleParamRecord + implements GroupedAggregatedLogRecords.SingleLogRecord { + + private final VibrationParamsRecords.Operation mOperation; + private final long mCreateTime; + private final int mTypesMask; + private final float mScale; + + VibrationScaleParamRecord(VibrationParamsRecords.Operation operation, long createTime, + int typesMask, float scale) { + mOperation = operation; + mCreateTime = createTime; + mTypesMask = typesMask; + mScale = scale; + } + + @Override + public int getGroupKey() { + return VibrationParam.scale; + } + + @Override + public long getCreateUptimeMs() { + return mCreateTime; + } + + @Override + public boolean mayAggregate(GroupedAggregatedLogRecords.SingleLogRecord record) { + if (!(record instanceof VibrationScaleParamRecord param)) { + return false; + } + return mTypesMask == param.mTypesMask && mOperation == param.mOperation; + } + + @Override + public void dump(IndentingPrintWriter pw) { + String line = String.format(Locale.ROOT, + "%s | %6s | scale: %5s | typesMask: %6s | usages: %s", + DEBUG_DATE_TIME_FORMAT.format(new Date(mCreateTime)), + mOperation.name().toLowerCase(Locale.ROOT), + (mScale == NO_SCALE) ? "" : String.format(Locale.ROOT, "%.2f", mScale), + Long.toBinaryString(mTypesMask), createVibrationUsagesString()); + pw.println(line); + } + + @Override + public void dump(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + proto.write(VibrationParamProto.CREATE_TIME, mCreateTime); + proto.write(VibrationParamProto.IS_FROM_REQUEST, + mOperation == VibrationParamsRecords.Operation.PULL); + + final long scaleToken = proto.start(VibrationParamProto.SCALE); + proto.write(VibrationScaleParamProto.TYPES_MASK, mTypesMask); + proto.write(VibrationScaleParamProto.SCALE, mScale); + proto.end(scaleToken); + + proto.end(token); + } + + private String createVibrationUsagesString() { + StringBuilder sb = new StringBuilder(); + int[] usages = mapFromAdaptiveVibrationTypeToVibrationUsages(mTypesMask); + for (int i = 0; i < usages.length; i++) { + if (i > 0) sb.append(", "); + sb.append(VibrationAttributes.usageToString(usages[i])); + } + return sb.toString(); + } + } } diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java index f5d4d1e3926b..6710d02bee90 100644 --- a/services/core/java/com/android/server/vibrator/VibratorController.java +++ b/services/core/java/com/android/server/vibrator/VibratorController.java @@ -16,7 +16,6 @@ package com.android.server.vibrator; -import android.annotation.Nullable; import android.annotation.Nullable; import android.hardware.vibrator.IVibrator; import android.os.Binder; @@ -354,13 +353,13 @@ final class VibratorController { } void dump(IndentingPrintWriter pw) { - pw.println("VibratorController:"); + pw.println("Vibrator (id=" + mVibratorInfo.getId() + "):"); pw.increaseIndent(); pw.println("isVibrating = " + mIsVibrating); pw.println("isUnderExternalControl = " + mIsUnderExternalControl); pw.println("currentAmplitude = " + mCurrentAmplitude); pw.println("vibratorInfoLoadSuccessful = " + mVibratorInfoLoadSuccessful); - pw.println("vibratorStateListenerCount = " + pw.println("vibratorStateListener size = " + mVibratorStateListeners.getRegisteredCallbackCount()); mVibratorInfo.dump(pw); pw.decreaseIndent(); diff --git a/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java b/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java index 79a99b3ee2ff..b49fb85ecf3f 100644 --- a/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java +++ b/services/core/java/com/android/server/vibrator/VibratorControllerHolder.java @@ -24,8 +24,6 @@ import android.util.Slog; /** * Holder class for {@link IVibratorController}. - * - * @hide */ public final class VibratorControllerHolder implements IBinder.DeathRecipient { private static final String TAG = "VibratorControllerHolder"; diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java index 78e0ebbb53fa..18b7ef43022e 100644 --- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java +++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java @@ -16,7 +16,6 @@ package com.android.server.vibrator; -import static android.os.ExternalVibrationScale.ScaleLevel.SCALE_MUTE; import static android.os.VibrationEffect.VibrationParameter.targetAmplitude; import static android.os.VibrationEffect.VibrationParameter.targetFrequency; @@ -84,7 +83,6 @@ import java.lang.ref.WeakReference; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; -import java.util.LinkedList; import java.util.List; import java.util.Objects; import java.util.concurrent.CompletableFuture; @@ -217,7 +215,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { mVibrationSettings = new VibrationSettings(mContext, mHandler); mVibrationScaler = new VibrationScaler(mContext, mVibrationSettings); - mVibratorControlService = new VibratorControlService( + mVibratorControlService = new VibratorControlService(mContext, injector.createVibratorControllerHolder(), mVibrationScaler, mVibrationSettings, mLock); mInputDeviceDelegate = new InputDeviceDelegate(mContext, mHandler); @@ -639,13 +637,16 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } IndentingPrintWriter pw = new IndentingPrintWriter(w, /* singleIndent= */ " "); synchronized (mLock) { - pw.println("Vibrator Manager Service:"); + pw.println("VibratorManagerService:"); pw.increaseIndent(); mVibrationSettings.dump(pw); pw.println(); - pw.println("VibratorControllers:"); + mVibrationScaler.dump(pw); + pw.println(); + + pw.println("Vibrators:"); pw.increaseIndent(); for (int i = 0; i < mVibrators.size(); i++) { mVibrators.valueAt(i).dump(pw); @@ -686,6 +687,10 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { pw.println(); pw.println(); mVibratorManagerRecords.dump(pw); + + pw.println(); + pw.println(); + mVibratorControlService.dump(pw); } private void dumpProto(FileDescriptor fd) { @@ -695,6 +700,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } synchronized (mLock) { mVibrationSettings.dump(proto); + mVibrationScaler.dump(proto); if (mCurrentVibration != null) { mCurrentVibration.getVibration().getDebugInfo().dump(proto, VibratorManagerServiceDumpProto.CURRENT_VIBRATION); @@ -716,6 +722,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { isUnderExternalControl); } mVibratorManagerRecords.dump(proto); + mVibratorControlService.dump(proto); proto.flush(); } @@ -887,7 +894,7 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { if (!vib.callerInfo.attrs.isFlagSet( VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_SCALE)) { // Scale resolves the default amplitudes from the effect before scaling them. - vib.scaleEffects(mVibrationScaler::scale); + vib.scaleEffects(mVibrationScaler); } else { vib.resolveEffects(mVibrationScaler.getDefaultVibrationAmplitude()); } @@ -1663,7 +1670,8 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { public Vibration.DebugInfo getDebugInfo() { return new Vibration.DebugInfo(mStatus, stats, /* playedEffect= */ null, - /* originalEffect= */ null, scale.scaleLevel, callerInfo); + /* originalEffect= */ null, scale.scaleLevel, scale.adaptiveHapticsScale, + callerInfo); } public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) { @@ -1739,8 +1747,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { int aggregationTimeLimit) { mAggregatedVibrationHistory = new VibrationRecords(aggregationSizeLimit, aggregationTimeLimit); - mRecentVibrations = new VibrationRecords( - recentVibrationSizeLimit, /* aggregationTimeLimit= */ 0); + // Recent vibrations are not aggregated, to help debugging issues that just happened. + mRecentVibrations = + new VibrationRecords(recentVibrationSizeLimit, /* aggregationTimeLimit= */ 0); } synchronized void record(HalVibration vib) { @@ -1752,9 +1761,11 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } private synchronized void record(Vibration.DebugInfo info) { - AggregatedVibrationRecord removedRecord = mRecentVibrations.record(info); - if (removedRecord != null) { - mAggregatedVibrationHistory.record(removedRecord.mLatestVibration); + GroupedAggregatedLogRecords.AggregatedLogRecord<VibrationRecord> droppedRecord = + mRecentVibrations.add(new VibrationRecord(info)); + if (droppedRecord != null) { + // Move dropped record from recent list to aggregated history list. + mAggregatedVibrationHistory.add(droppedRecord.getLatest()); } } @@ -1763,9 +1774,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { pw.increaseIndent(); mRecentVibrations.dump(pw); pw.decreaseIndent(); + pw.println(); pw.println(); - pw.println("Aggregated vibration history:"); pw.increaseIndent(); mAggregatedVibrationHistory.dump(pw); @@ -1778,127 +1789,75 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } /** 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; + private static final class VibrationRecords + extends GroupedAggregatedLogRecords<VibrationRecord> { VibrationRecords(int sizeLimit, int aggregationTimeLimit) { - mSizeLimit = sizeLimit; - mAggregationTimeLimit = aggregationTimeLimit; + super(sizeLimit, 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; - } - - 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 dump(ProtoOutputStream proto) { - for (int i = 0; i < mVibrations.size(); i++) { - long fieldId; - switch (mVibrations.keyAt(i)) { - case VibrationAttributes.USAGE_RINGTONE: - fieldId = VibratorManagerServiceDumpProto.PREVIOUS_RING_VIBRATIONS; - break; - case VibrationAttributes.USAGE_NOTIFICATION: - fieldId = VibratorManagerServiceDumpProto.PREVIOUS_NOTIFICATION_VIBRATIONS; - break; - case VibrationAttributes.USAGE_ALARM: - fieldId = VibratorManagerServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS; - break; - default: - fieldId = VibratorManagerServiceDumpProto.PREVIOUS_VIBRATIONS; - } - for (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); - } - } - } + @Override + void dumpGroupHeader(IndentingPrintWriter pw, int usage) { + pw.println(VibrationAttributes.usageToString(usage) + ":"); } - 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); - } - } + @Override + long findGroupKeyProtoFieldId(int usage) { + return switch (usage) { + case VibrationAttributes.USAGE_RINGTONE -> + VibratorManagerServiceDumpProto.PREVIOUS_RING_VIBRATIONS; + case VibrationAttributes.USAGE_NOTIFICATION -> + VibratorManagerServiceDumpProto.PREVIOUS_NOTIFICATION_VIBRATIONS; + case VibrationAttributes.USAGE_ALARM -> + VibratorManagerServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS; + default -> + VibratorManagerServiceDumpProto.PREVIOUS_VIBRATIONS; + }; } } /** - * 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}. + * Record for a single {@link Vibration.DebugInfo}, that can be grouped by usage and aggregated + * by UID, {@link VibrationAttributes} and {@link VibrationEffect}. */ - private static final class AggregatedVibrationRecord { - private final Vibration.DebugInfo mFirstVibration; - private Vibration.DebugInfo mLatestVibration; - private int mVibrationCount; + private static final class VibrationRecord + implements GroupedAggregatedLogRecords.SingleLogRecord { + private final Vibration.DebugInfo mInfo; - AggregatedVibrationRecord(Vibration.DebugInfo info) { - mLatestVibration = mFirstVibration = info; - mVibrationCount = 1; + VibrationRecord(Vibration.DebugInfo info) { + mInfo = info; } - 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; + @Override + public int getGroupKey() { + return mInfo.mCallerInfo.attrs.getUsage(); } - synchronized void record(Vibration.DebugInfo vib) { - mLatestVibration = vib; - mVibrationCount++; + @Override + public long getCreateUptimeMs() { + return mInfo.mCreateTime; } - synchronized void dump(IndentingPrintWriter pw) { - mFirstVibration.dumpCompact(pw); - if (mVibrationCount == 1) { - return; - } - if (mVibrationCount > 2) { - pw.println( - "-> Skipping " + (mVibrationCount - 2) + " aggregated vibrations, latest:"); + @Override + public boolean mayAggregate(GroupedAggregatedLogRecords.SingleLogRecord record) { + if (!(record instanceof VibrationRecord)) { + return false; } - mLatestVibration.dumpCompact(pw); + Vibration.DebugInfo info = ((VibrationRecord) record).mInfo; + return mInfo.mCallerInfo.uid == info.mCallerInfo.uid + && Objects.equals(mInfo.mCallerInfo.attrs, info.mCallerInfo.attrs) + && Objects.equals(mInfo.mPlayedEffect, info.mPlayedEffect); } - synchronized void dump(ProtoOutputStream proto, long fieldId) { - mLatestVibration.dump(proto, fieldId); + @Override + public void dump(IndentingPrintWriter pw) { + // Prints a compact version of each vibration request for dumpsys. + mInfo.dumpCompact(pw); + } + + @Override + public void dump(ProtoOutputStream proto, long fieldId) { + mInfo.dump(proto, fieldId); } } @@ -2001,7 +1960,6 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { @Override public ExternalVibrationScale onExternalVibrationStart(ExternalVibration vib) { - if (!hasExternalControlCapability()) { return SCALE_MUTE; } @@ -2085,10 +2043,9 @@ public class VibratorManagerService extends IVibratorManagerService.Stub { } mCurrentExternalVibration = vibHolder; vibHolder.linkToDeath(); - vibHolder.scale.scaleLevel = mVibrationScaler.getExternalVibrationScaleLevel( - attrs.getUsage()); - vibHolder.scale.adaptiveHapticsScale = mVibrationScaler.getAdaptiveHapticsScale( - attrs.getUsage()); + vibHolder.scale.scaleLevel = mVibrationScaler.getScaleLevel(attrs.getUsage()); + vibHolder.scale.adaptiveHapticsScale = + mVibrationScaler.getAdaptiveHapticsScale(attrs.getUsage()); } if (waitForCompletion) { diff --git a/services/tests/vibrator/src/com/android/server/vibrator/GroupedAggregatedLogRecordsTest.java b/services/tests/vibrator/src/com/android/server/vibrator/GroupedAggregatedLogRecordsTest.java new file mode 100644 index 000000000000..038f1db32d18 --- /dev/null +++ b/services/tests/vibrator/src/com/android/server/vibrator/GroupedAggregatedLogRecordsTest.java @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.vibrator; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import android.util.IndentingPrintWriter; +import android.util.proto.ProtoOutputStream; + +import com.android.server.vibrator.GroupedAggregatedLogRecords.AggregatedLogRecord; +import com.android.server.vibrator.GroupedAggregatedLogRecords.SingleLogRecord; + +import org.junit.Test; + +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class GroupedAggregatedLogRecordsTest { + + private static final int AGGREGATION_TIME_LIMIT = 1000; + private static final int NO_AGGREGATION_TIME_LIMIT = 0; + private static final long PROTO_FIELD_ID = 1; + private static final int GROUP_1 = 1; + private static final int GROUP_2 = 2; + private static final int KEY_1 = 1; + private static final int KEY_2 = 2; + + private static final IndentingPrintWriter WRITER = new IndentingPrintWriter(new StringWriter()); + private static final ProtoOutputStream PROTO_OUTPUT_STREAM = new ProtoOutputStream(); + + private final List<TestSingleLogRecord> mTestRecords = new ArrayList<>(); + + @Test + public void record_noAggregation_keepsIndividualRecords() { + int sizeLimit = 10; + long createTime = 100; + TestGroupedAggregatedLogRecords records = new TestGroupedAggregatedLogRecords( + sizeLimit, NO_AGGREGATION_TIME_LIMIT, PROTO_FIELD_ID); + + for (int i = 0; i < sizeLimit; i++) { + assertThat(records.add(createRecord(GROUP_1, KEY_1, createTime++))).isNull(); + } + + dumpRecords(records); + assertGroupHeadersWrittenOnce(records, GROUP_1); + assertRecordsInRangeWrittenOnce(0, sizeLimit); + } + + @Test + public void record_sizeLimit_dropsOldestEntriesForNewOnes() { + long createTime = 100; + TestGroupedAggregatedLogRecords records = new TestGroupedAggregatedLogRecords( + /* sizeLimit= */ 2, NO_AGGREGATION_TIME_LIMIT, PROTO_FIELD_ID); + + TestSingleLogRecord firstRecord = createRecord(GROUP_1, KEY_1, createTime++); + assertThat(records.add(firstRecord)).isNull(); + assertThat(records.add(createRecord(GROUP_1, KEY_1, createTime++))).isNull(); + + // Adding third record drops first record + AggregatedLogRecord<TestSingleLogRecord> droppedRecord = + records.add(createRecord(GROUP_1, KEY_1, createTime++)); + assertThat(droppedRecord).isNotNull(); + assertThat(droppedRecord.getLatest()).isEqualTo(firstRecord); + + dumpRecords(records); + assertGroupHeadersWrittenOnce(records, GROUP_1); + assertRecordsInRangeNotWritten(0, 1); // First record not written + assertRecordsInRangeWrittenOnce(1, 3); // All newest records written + } + + @Test + public void record_timeAggregation_aggregatesCloseRecordAndPrintsOnlyFirstAndLast() { + long createTime = 100; + TestGroupedAggregatedLogRecords records = new TestGroupedAggregatedLogRecords( + /* sizeLimit= */ 1, AGGREGATION_TIME_LIMIT, PROTO_FIELD_ID); + + // No record dropped, all aggregated in a single entry + assertThat(records.add(createRecord(GROUP_1, KEY_1, createTime))).isNull(); + assertThat(records.add(createRecord(GROUP_1, KEY_1, createTime + 1))).isNull(); + assertThat(records.add(createRecord(GROUP_1, KEY_1, + createTime + AGGREGATION_TIME_LIMIT - 2))).isNull(); + assertThat(records.add(createRecord(GROUP_1, KEY_1, + createTime + AGGREGATION_TIME_LIMIT - 1))).isNull(); + + dumpRecords(records); + assertGroupHeadersWrittenOnce(records, GROUP_1); + assertRecordsInRangeWrittenOnce(0, 1); // Writes first record + assertRecordsInRangeNotWritten(1, 3); // Skips aggregated records in between + assertRecordsInRangeWrittenOnce(3, 4); // Writes last record + } + + @Test + public void record_differentGroups_recordsKeptSeparate() { + long createTime = 100; + TestGroupedAggregatedLogRecords records = new TestGroupedAggregatedLogRecords( + /* sizeLimit= */ 1, AGGREGATION_TIME_LIMIT, PROTO_FIELD_ID); + + // No record dropped, all kept in separate aggregated lists + assertThat(records.add(createRecord(GROUP_1, KEY_1, createTime++))).isNull(); + assertThat(records.add(createRecord(GROUP_1, KEY_1, createTime++))).isNull(); + assertThat(records.add(createRecord(GROUP_2, KEY_2, createTime++))).isNull(); + assertThat(records.add(createRecord(GROUP_2, KEY_2, createTime++))).isNull(); + + dumpRecords(records); + assertGroupHeadersWrittenOnce(records, GROUP_1, GROUP_2); + assertRecordsInRangeWrittenOnce(0, 4); + } + + @Test + public void record_sameGroupDifferentAggregationKeys_recordsNotAggregated() { + long createTime = 100; + TestGroupedAggregatedLogRecords records = new TestGroupedAggregatedLogRecords( + /* sizeLimit= */ 1, AGGREGATION_TIME_LIMIT, PROTO_FIELD_ID); + + assertThat(records.add(createRecord(GROUP_1, KEY_1, createTime++))).isNull(); + + // Second record on same group with different key not aggregated, drops first record + AggregatedLogRecord<TestSingleLogRecord> droppedRecord = + records.add(createRecord(GROUP_1, KEY_2, createTime++)); + assertThat(droppedRecord).isNotNull(); + assertThat(droppedRecord.getLatest()).isEqualTo(mTestRecords.getFirst()); + + dumpRecords(records); + assertGroupHeadersWrittenOnce(records, GROUP_1); + assertRecordsInRangeNotWritten(0, 1); // Skips first record that was dropped + assertRecordsInRangeWrittenOnce(1, 2); // Writes last record + } + + @Test + public void record_sameGroupAndAggregationKeysDistantTimes_recordsNotAggregated() { + long createTime = 100; + TestGroupedAggregatedLogRecords records = new TestGroupedAggregatedLogRecords( + /* sizeLimit= */ 1, AGGREGATION_TIME_LIMIT, PROTO_FIELD_ID); + + assertThat(records.add(createRecord(GROUP_1, KEY_1, createTime))).isNull(); + + // Second record after aggregation time limit not aggregated, drops first record + AggregatedLogRecord<TestSingleLogRecord> droppedRecord = + records.add(createRecord(GROUP_1, KEY_1, createTime + AGGREGATION_TIME_LIMIT)); + assertThat(droppedRecord).isNotNull(); + assertThat(droppedRecord.getLatest()).isEqualTo(mTestRecords.getFirst()); + + dumpRecords(records); + assertGroupHeadersWrittenOnce(records, GROUP_1); + assertRecordsInRangeNotWritten(0, 1); // Skips first record that was dropped + assertRecordsInRangeWrittenOnce(1, 2); // Writes last record + } + + private TestSingleLogRecord createRecord(int groupKey, int aggregateKey, long createTime) { + TestSingleLogRecord record = new TestSingleLogRecord(groupKey, aggregateKey, createTime); + mTestRecords.add(record); + return record; + } + + private void dumpRecords(TestGroupedAggregatedLogRecords records) { + records.dump(WRITER); + records.dump(PROTO_OUTPUT_STREAM); + } + + private void assertGroupHeadersWrittenOnce(TestGroupedAggregatedLogRecords records, + int... groupKeys) { + assertThat(records.dumpGroupKeys).containsExactlyElementsIn( + Arrays.stream(groupKeys).boxed().toList()); + } + + private void assertRecordsInRangeWrittenOnce(int startIndexInclusive, int endIndexExclusive) { + for (int i = startIndexInclusive; i < endIndexExclusive; i++) { + assertWithMessage("record index=" + i).that(mTestRecords.get(i).dumpTextCount) + .isEqualTo(1); + assertWithMessage("record index=" + i).that(mTestRecords.get(i).dumpProtoFieldIds) + .containsExactly(PROTO_FIELD_ID); + } + } + + private void assertRecordsInRangeNotWritten(int startIndexInclusive, int endIndexExclusive) { + for (int i = startIndexInclusive; i < endIndexExclusive; i++) { + assertWithMessage("record index=" + i).that(mTestRecords.get(i).dumpTextCount) + .isEqualTo(0); + assertWithMessage("record index=" + i).that(mTestRecords.get(i).dumpProtoFieldIds) + .isEmpty(); + } + } + + private static final class TestGroupedAggregatedLogRecords + extends GroupedAggregatedLogRecords<TestSingleLogRecord> { + + public final List<Integer> dumpGroupKeys = new ArrayList<>(); + + private final long mProtoFieldId; + + TestGroupedAggregatedLogRecords(int sizeLimit, int aggregationTimeLimitMs, + long protoFieldId) { + super(sizeLimit, aggregationTimeLimitMs); + mProtoFieldId = protoFieldId; + } + + @Override + void dumpGroupHeader(IndentingPrintWriter pw, int groupKey) { + dumpGroupKeys.add(groupKey); + } + + @Override + long findGroupKeyProtoFieldId(int groupKey) { + return mProtoFieldId; + } + } + + private static final class TestSingleLogRecord implements SingleLogRecord { + public final List<Long> dumpProtoFieldIds = new ArrayList<>(); + public int dumpTextCount = 0; + + private final int mGroupKey; + private final int mAggregateKey; + private final long mCreateTime; + + TestSingleLogRecord(int groupKey, int aggregateKey, long createTime) { + mGroupKey = groupKey; + mAggregateKey = aggregateKey; + mCreateTime = createTime; + } + + @Override + public int getGroupKey() { + return mGroupKey; + } + + @Override + public long getCreateUptimeMs() { + return mCreateTime; + } + + @Override + public boolean mayAggregate(SingleLogRecord record) { + if (record instanceof TestSingleLogRecord param) { + return mAggregateKey == param.mAggregateKey; + } + return false; + } + + @Override + public void dump(IndentingPrintWriter pw) { + dumpTextCount++; + } + + @Override + public void dump(ProtoOutputStream proto, long fieldId) { + dumpProtoFieldIds.add(fieldId); + } + } +} diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java index 3e59878f9e1e..b2644350dfdd 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationScalerTest.java @@ -117,32 +117,32 @@ public class VibrationScalerTest { } @Test - public void testGetExternalVibrationScale() { + public void testGetScaleLevel() { setDefaultIntensity(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_LOW); setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_HIGH); assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_VERY_HIGH, - mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH)); + mVibrationScaler.getScaleLevel(USAGE_TOUCH)); setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_MEDIUM); assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_HIGH, - mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH)); + mVibrationScaler.getScaleLevel(USAGE_TOUCH)); setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_LOW); assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_NONE, - mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH)); + mVibrationScaler.getScaleLevel(USAGE_TOUCH)); setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_MEDIUM); assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_LOW, - mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH)); + mVibrationScaler.getScaleLevel(USAGE_TOUCH)); setDefaultIntensity(USAGE_TOUCH, VIBRATION_INTENSITY_HIGH); assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_VERY_LOW, - mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH)); + mVibrationScaler.getScaleLevel(USAGE_TOUCH)); setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF); // Vibration setting being bypassed will use default setting and not scale. assertEquals(ExternalVibrationScale.ScaleLevel.SCALE_NONE, - mVibrationScaler.getExternalVibrationScaleLevel(USAGE_TOUCH)); + mVibrationScaler.getScaleLevel(USAGE_TOUCH)); } @Test diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java index 0d5bf95d959d..3799abc100c9 100644 --- a/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java +++ b/services/tests/vibrator/src/com/android/server/vibrator/VibratorControlServiceTest.java @@ -38,10 +38,10 @@ import android.frameworks.vibrator.ScaleParam; import android.os.Binder; import android.os.Handler; import android.os.IBinder; -import android.os.RemoteException; import android.os.test.TestLooper; import android.util.SparseArray; +import androidx.test.InstrumentationRegistry; import androidx.test.core.app.ApplicationProvider; import com.android.internal.util.ArrayUtils; @@ -86,20 +86,20 @@ public class VibratorControlServiceTest { ApplicationProvider.getApplicationContext(), new Handler(testLooper.getLooper())); mFakeVibratorController = new FakeVibratorController(mTestLooper.getLooper()); - mVibratorControlService = new VibratorControlService(new VibratorControllerHolder(), + mVibratorControlService = new VibratorControlService( + InstrumentationRegistry.getContext(), new VibratorControllerHolder(), mMockVibrationScaler, mVibrationSettings, mLock); } @Test - public void testRegisterVibratorController() throws RemoteException { + public void testRegisterVibratorController() { mVibratorControlService.registerVibratorController(mFakeVibratorController); assertThat(mFakeVibratorController.isLinkedToDeath).isTrue(); } @Test - public void testUnregisterVibratorController_providingTheRegisteredController_performsRequest() - throws RemoteException { + public void testUnregisterVibratorController_providingRegisteredController_performsRequest() { mVibratorControlService.registerVibratorController(mFakeVibratorController); mVibratorControlService.unregisterVibratorController(mFakeVibratorController); @@ -108,8 +108,7 @@ public class VibratorControlServiceTest { } @Test - public void testUnregisterVibratorController_providingAnInvalidController_ignoresRequest() - throws RemoteException { + public void testUnregisterVibratorController_providingAnInvalidController_ignoresRequest() { FakeVibratorController controller1 = new FakeVibratorController(mTestLooper.getLooper()); FakeVibratorController controller2 = new FakeVibratorController(mTestLooper.getLooper()); mVibratorControlService.registerVibratorController(controller1); @@ -120,8 +119,7 @@ public class VibratorControlServiceTest { } @Test - public void testOnRequestVibrationParamsComplete_cachesAdaptiveHapticsScalesCorrectly() - throws RemoteException { + public void testOnRequestVibrationParamsComplete_cachesAdaptiveHapticsScalesCorrectly() { mVibratorControlService.registerVibratorController(mFakeVibratorController); int timeoutInMillis = 10; CompletableFuture<Void> future = @@ -148,8 +146,7 @@ public class VibratorControlServiceTest { } @Test - public void testOnRequestVibrationParamsComplete_withIncorrectToken_ignoresRequest() - throws RemoteException, InterruptedException { + public void testOnRequestVibrationParamsComplete_withIncorrectToken_ignoresRequest() { mVibratorControlService.registerVibratorController(mFakeVibratorController); int timeoutInMillis = 10; CompletableFuture<Void> unusedFuture = @@ -167,8 +164,7 @@ public class VibratorControlServiceTest { } @Test - public void testSetVibrationParams_cachesAdaptiveHapticsScalesCorrectly() - throws RemoteException { + public void testSetVibrationParams_cachesAdaptiveHapticsScalesCorrectly() { mVibratorControlService.registerVibratorController(mFakeVibratorController); SparseArray<Float> vibrationScales = new SparseArray<>(); vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f); @@ -187,8 +183,7 @@ public class VibratorControlServiceTest { } @Test - public void testSetVibrationParams_withUnregisteredController_ignoresRequest() - throws RemoteException { + public void testSetVibrationParams_withUnregisteredController_ignoresRequest() { SparseArray<Float> vibrationScales = new SparseArray<>(); vibrationScales.put(ScaleParam.TYPE_ALARM, 0.7f); vibrationScales.put(ScaleParam.TYPE_NOTIFICATION, 0.4f); @@ -201,8 +196,7 @@ public class VibratorControlServiceTest { } @Test - public void testClearVibrationParams_clearsCachedAdaptiveHapticsScales() - throws RemoteException { + public void testClearVibrationParams_clearsCachedAdaptiveHapticsScales() { mVibratorControlService.registerVibratorController(mFakeVibratorController); int types = buildVibrationTypesMask(ScaleParam.TYPE_ALARM, ScaleParam.TYPE_NOTIFICATION); @@ -216,8 +210,7 @@ public class VibratorControlServiceTest { } @Test - public void testClearVibrationParams_withUnregisteredController_ignoresRequest() - throws RemoteException { + public void testClearVibrationParams_withUnregisteredController_ignoresRequest() { mVibratorControlService.clearVibrationParams(ScaleParam.TYPE_ALARM, mFakeVibratorController); @@ -225,8 +218,7 @@ public class VibratorControlServiceTest { } @Test - public void testRequestVibrationParams_createsFutureRequestProperly() - throws RemoteException { + public void testRequestVibrationParams_createsFutureRequestProperly() { int timeoutInMillis = 10; mVibratorControlService.registerVibratorController(mFakeVibratorController); CompletableFuture<Void> future = @@ -243,8 +235,7 @@ public class VibratorControlServiceTest { } @Test - public void testShouldRequestVibrationParams_returnsTrueForVibrationsThatShouldRequestParams() - throws RemoteException { + public void testShouldRequestVibrationParams_returnsTrueForVibrationsThatShouldRequestParams() { int[] vibrations = new int[]{USAGE_ALARM, USAGE_RINGTONE, USAGE_MEDIA, USAGE_TOUCH, USAGE_NOTIFICATION, USAGE_HARDWARE_FEEDBACK, USAGE_UNKNOWN, USAGE_COMMUNICATION_REQUEST}; @@ -258,8 +249,7 @@ public class VibratorControlServiceTest { } @Test - public void testShouldRequestVibrationParams_unregisteredVibratorController_returnsFalse() - throws RemoteException { + public void testShouldRequestVibrationParams_unregisteredVibratorController_returnsFalse() { int[] vibrations = new int[]{USAGE_ALARM, USAGE_RINGTONE, USAGE_MEDIA, USAGE_TOUCH, USAGE_NOTIFICATION, USAGE_HARDWARE_FEEDBACK, USAGE_UNKNOWN, USAGE_COMMUNICATION_REQUEST}; @@ -269,7 +259,7 @@ public class VibratorControlServiceTest { } } - private int buildVibrationTypesMask(int... types) { + private static int buildVibrationTypesMask(int... types) { int typesMask = 0; for (int type : types) { typesMask |= type; diff --git a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java index 2a010f0a82a9..0cd88ef7a4b4 100644 --- a/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java +++ b/services/tests/vibrator/utils/com/android/server/vibrator/FakeVibratorController.java @@ -60,11 +60,7 @@ public final class FakeVibratorController extends IVibratorController.Stub { requestTimeoutInMillis = timeoutInMillis; mHandler.post(() -> { if (mVibratorControlService != null) { - try { - mVibratorControlService.onRequestVibrationParamsComplete(token, mRequestResult); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + mVibratorControlService.onRequestVibrationParamsComplete(token, mRequestResult); } }); } -- GitLab