Loading services/core/java/com/android/server/vibrator/ExternalVibrationSession.java 0 → 100644 +146 −0 Original line number Diff line number Diff line /* * 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.annotation.Nullable; import android.content.Context; import android.os.ExternalVibration; import android.os.ExternalVibrationScale; import android.os.IBinder; import android.os.VibrationAttributes; import android.os.vibrator.Flags; import com.android.internal.util.FrameworkStatsLog; /** * A vibration session holding a single {@link ExternalVibration} request. */ final class ExternalVibrationSession extends Vibration implements VibrationSession, IBinder.DeathRecipient { private final ExternalVibration mExternalVibration; private final ExternalVibrationScale mScale = new ExternalVibrationScale(); @Nullable private Runnable mBinderDeathCallback; ExternalVibrationSession(ExternalVibration externalVibration) { super(externalVibration.getToken(), new CallerInfo( externalVibration.getVibrationAttributes(), externalVibration.getUid(), // TODO(b/249785241): Find a way to link ExternalVibration to a VirtualDevice // instead of using DEVICE_ID_INVALID here and relying on the UID checks. Context.DEVICE_ID_INVALID, externalVibration.getPackage(), null)); mExternalVibration = externalVibration; } public ExternalVibrationScale getScale() { return mScale; } @Override public CallerInfo getCallerInfo() { return callerInfo; } @Override public VibrationSession.DebugInfo getDebugInfo() { return new Vibration.DebugInfoImpl(getStatus(), stats, /* playedEffect= */ null, /* originalEffect= */ null, mScale.scaleLevel, mScale.adaptiveHapticsScale, callerInfo); } @Override public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) { return new VibrationStats.StatsInfo( mExternalVibration.getUid(), FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__EXTERNAL, mExternalVibration.getVibrationAttributes().getUsage(), getStatus(), stats, completionUptimeMillis); } @Override public boolean isRepeating() { // We don't currently know if the external vibration is repeating, so we just use a // heuristic based on the usage. Ideally this would be propagated in the ExternalVibration. int usage = mExternalVibration.getVibrationAttributes().getUsage(); return usage == VibrationAttributes.USAGE_RINGTONE || usage == VibrationAttributes.USAGE_ALARM; } @Override public void linkToDeath(Runnable callback) { synchronized (this) { mBinderDeathCallback = callback; } mExternalVibration.linkToDeath(this); } @Override public void unlinkToDeath() { mExternalVibration.unlinkToDeath(this); synchronized (this) { mBinderDeathCallback = null; } } @Override public void binderDied() { synchronized (this) { if (mBinderDeathCallback != null) { mBinderDeathCallback.run(); } } } @Override void end(EndInfo endInfo) { super.end(endInfo); if (stats.hasStarted()) { // External vibration doesn't have feedback from total time the vibrator was playing // with non-zero amplitude, so we use the duration between start and end times of // the vibration as the time the vibrator was ON, since the haptic channels are // open for this duration and can receive vibration waveform data. stats.reportVibratorOn(stats.getEndUptimeMillis() - stats.getStartUptimeMillis()); } } @Override public void notifyEnded() { // Notify external client that this vibration should stop sending data to the vibrator. mExternalVibration.mute(); } boolean isHoldingSameVibration(ExternalVibration vib) { return mExternalVibration.equals(vib); } void muteScale() { mScale.scaleLevel = ExternalVibrationScale.ScaleLevel.SCALE_MUTE; if (Flags.hapticsScaleV2Enabled()) { mScale.scaleFactor = 0; } } void scale(VibrationScaler scaler, int usage) { mScale.scaleLevel = scaler.getScaleLevel(usage); if (Flags.hapticsScaleV2Enabled()) { mScale.scaleFactor = scaler.getScaleFactor(usage); } mScale.adaptiveHapticsScale = scaler.getAdaptiveHapticsScale(usage); stats.reportAdaptiveScale(mScale.adaptiveHapticsScale); } } services/core/java/com/android/server/vibrator/HalVibration.java +9 −29 Original line number Diff line number Diff line Loading @@ -51,37 +51,22 @@ final class HalVibration extends Vibration { @NonNull private volatile CombinedVibration mEffectToPlay; /** 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) { @NonNull VibrationSession.CallerInfo callerInfo) { super(token, callerInfo); mOriginalEffect = effect; mEffectToPlay = effect; mStatus = Vibration.Status.RUNNING; mScaleLevel = VibrationScaler.SCALE_NONE; mAdaptiveScale = VibrationScaler.ADAPTIVE_SCALE_NONE; } /** * Set the {@link Status} of this vibration and reports the current system time as this * vibration end time, for debugging purposes. * * <p>This method will only accept given value if the current status is {@link * Status#RUNNING}. */ public void end(EndInfo info) { if (hasEnded()) { // Vibration already ended, keep first ending status set and ignore this one. return; } mStatus = info.status; stats.reportEnded(info.endedBy); @Override public void end(EndInfo endInfo) { super.end(endInfo); mCompletionLatch.countDown(); } Loading Loading @@ -144,11 +129,6 @@ final class HalVibration extends Vibration { // No need to update fallback effects, they are already configured per device. } /** Return true is current status is different from {@link Status#RUNNING}. */ public boolean hasEnded() { return mStatus != Status.RUNNING; } @Override public boolean isRepeating() { return mOriginalEffect.getDuration() == Long.MAX_VALUE; Loading @@ -159,16 +139,16 @@ final class HalVibration extends Vibration { return mEffectToPlay; } /** Return {@link Vibration.DebugInfo} with read-only debug information about this vibration. */ public Vibration.DebugInfo getDebugInfo() { @Override public VibrationSession.DebugInfo getDebugInfo() { // Clear the original effect if it's the same as the effect that was played, for simplicity CombinedVibration originalEffect = Objects.equals(mOriginalEffect, mEffectToPlay) ? null : mOriginalEffect; return new Vibration.DebugInfo(mStatus, stats, mEffectToPlay, originalEffect, return new Vibration.DebugInfoImpl(getStatus(), stats, mEffectToPlay, originalEffect, mScaleLevel, mAdaptiveScale, callerInfo); } /** Return {@link VibrationStats.StatsInfo} with read-only metrics about this vibration. */ @Override public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) { int vibrationType = mEffectToPlay.hasVendorEffects() ? FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__VENDOR Loading @@ -176,7 +156,7 @@ final class HalVibration extends Vibration { ? FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__REPEATED : FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__SINGLE; return new VibrationStats.StatsInfo( callerInfo.uid, vibrationType, callerInfo.attrs.getUsage(), mStatus, callerInfo.uid, vibrationType, callerInfo.attrs.getUsage(), getStatus(), stats, completionUptimeMillis); } Loading services/core/java/com/android/server/vibrator/InputDeviceDelegate.java +2 −1 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.util.SparseArray; import android.view.InputDevice; import com.android.internal.annotations.GuardedBy; import com.android.server.vibrator.VibrationSession.CallerInfo; /** Delegates vibrations to all connected {@link InputDevice} with one or more vibrators. */ final class InputDeviceDelegate implements InputManager.InputDeviceListener { Loading Loading @@ -93,7 +94,7 @@ final class InputDeviceDelegate implements InputManager.InputDeviceListener { * * @return {@link #isAvailable()} */ public boolean vibrateIfAvailable(Vibration.CallerInfo callerInfo, CombinedVibration effect) { public boolean vibrateIfAvailable(CallerInfo callerInfo, CombinedVibration effect) { synchronized (mLock) { for (int i = 0; i < mInputDeviceVibrators.size(); i++) { mInputDeviceVibrators.valueAt(i).vibrate(callerInfo.uid, callerInfo.opPkg, effect, Loading services/core/java/com/android/server/vibrator/Vibration.java +70 −115 Original line number Diff line number Diff line Loading @@ -31,7 +31,6 @@ import android.os.vibrator.VibrationEffectSegment; import android.util.IndentingPrintWriter; import android.util.proto.ProtoOutputStream; import java.io.PrintWriter; import java.time.Instant; import java.time.ZoneId; import java.time.format.DateTimeFormatter; Loading @@ -52,131 +51,69 @@ abstract class Vibration { private static final AtomicInteger sNextVibrationId = new AtomicInteger(1); // 0 = no callback public final long id; public final CallerInfo callerInfo; public final VibrationSession.CallerInfo callerInfo; public final VibrationStats stats = new VibrationStats(); public final IBinder callerToken; /** Vibration status with reference to values from vibratormanagerservice.proto for logging. */ enum Status { UNKNOWN(VibrationProto.UNKNOWN), RUNNING(VibrationProto.RUNNING), FINISHED(VibrationProto.FINISHED), FINISHED_UNEXPECTED(VibrationProto.FINISHED_UNEXPECTED), FORWARDED_TO_INPUT_DEVICES(VibrationProto.FORWARDED_TO_INPUT_DEVICES), CANCELLED_BINDER_DIED(VibrationProto.CANCELLED_BINDER_DIED), CANCELLED_BY_SCREEN_OFF(VibrationProto.CANCELLED_BY_SCREEN_OFF), CANCELLED_BY_SETTINGS_UPDATE(VibrationProto.CANCELLED_BY_SETTINGS_UPDATE), CANCELLED_BY_USER(VibrationProto.CANCELLED_BY_USER), CANCELLED_BY_FOREGROUND_USER(VibrationProto.CANCELLED_BY_FOREGROUND_USER), CANCELLED_BY_UNKNOWN_REASON(VibrationProto.CANCELLED_BY_UNKNOWN_REASON), CANCELLED_SUPERSEDED(VibrationProto.CANCELLED_SUPERSEDED), CANCELLED_BY_APP_OPS(VibrationProto.CANCELLED_BY_APP_OPS), IGNORED_ERROR_APP_OPS(VibrationProto.IGNORED_ERROR_APP_OPS), IGNORED_ERROR_CANCELLING(VibrationProto.IGNORED_ERROR_CANCELLING), IGNORED_ERROR_SCHEDULING(VibrationProto.IGNORED_ERROR_SCHEDULING), IGNORED_ERROR_TOKEN(VibrationProto.IGNORED_ERROR_TOKEN), IGNORED_APP_OPS(VibrationProto.IGNORED_APP_OPS), IGNORED_BACKGROUND(VibrationProto.IGNORED_BACKGROUND), IGNORED_MISSING_PERMISSION(VibrationProto.IGNORED_MISSING_PERMISSION), IGNORED_UNSUPPORTED(VibrationProto.IGNORED_UNSUPPORTED), IGNORED_FOR_EXTERNAL(VibrationProto.IGNORED_FOR_EXTERNAL), IGNORED_FOR_HIGHER_IMPORTANCE(VibrationProto.IGNORED_FOR_HIGHER_IMPORTANCE), IGNORED_FOR_ONGOING(VibrationProto.IGNORED_FOR_ONGOING), IGNORED_FOR_POWER(VibrationProto.IGNORED_FOR_POWER), IGNORED_FOR_RINGER_MODE(VibrationProto.IGNORED_FOR_RINGER_MODE), IGNORED_FOR_SETTINGS(VibrationProto.IGNORED_FOR_SETTINGS), IGNORED_SUPERSEDED(VibrationProto.IGNORED_SUPERSEDED), IGNORED_FROM_VIRTUAL_DEVICE(VibrationProto.IGNORED_FROM_VIRTUAL_DEVICE), IGNORED_ON_WIRELESS_CHARGER(VibrationProto.IGNORED_ON_WIRELESS_CHARGER); private final int mProtoEnumValue; Status(int value) { mProtoEnumValue = value; } public int getProtoEnumValue() { return mProtoEnumValue; } } Vibration(@NonNull IBinder token, @NonNull CallerInfo callerInfo) { private VibrationSession.Status mStatus; Vibration(@NonNull IBinder token, @NonNull VibrationSession.CallerInfo callerInfo) { Objects.requireNonNull(token); Objects.requireNonNull(callerInfo); mStatus = VibrationSession.Status.RUNNING; this.id = sNextVibrationId.getAndIncrement(); this.callerToken = token; this.callerInfo = callerInfo; } /** Return true if vibration is a repeating vibration. */ abstract boolean isRepeating(); VibrationSession.Status getStatus() { return mStatus; } /** Return true is current status is different from {@link VibrationSession.Status#RUNNING}. */ boolean hasEnded() { return mStatus != VibrationSession.Status.RUNNING; } /** * Holds lightweight immutable info on the process that triggered the vibration. This data * could potentially be kept in memory for a long time for bugreport dumpsys operations. * Set the {@link VibrationSession} of this vibration and reports the current system time as * this vibration end time, for debugging purposes. * * Since CallerInfo can be kept in memory for a long time, it shouldn't hold any references to * potentially expensive or resource-linked objects, such as {@link IBinder}. * <p>This method will only accept given value if the current status is {@link * VibrationSession.Status#RUNNING}. */ static final class CallerInfo { public final VibrationAttributes attrs; public final int uid; public final int deviceId; public final String opPkg; public final String reason; CallerInfo(@NonNull VibrationAttributes attrs, int uid, int deviceId, String opPkg, String reason) { Objects.requireNonNull(attrs); this.attrs = attrs; this.uid = uid; this.deviceId = deviceId; this.opPkg = opPkg; this.reason = reason; void end(Vibration.EndInfo endInfo) { if (hasEnded()) { // Vibration already ended, keep first ending status set and ignore this one. return; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof CallerInfo)) return false; CallerInfo that = (CallerInfo) o; return Objects.equals(attrs, that.attrs) && uid == that.uid && deviceId == that.deviceId && Objects.equals(opPkg, that.opPkg) && Objects.equals(reason, that.reason); mStatus = endInfo.status; stats.reportEnded(endInfo.endedBy); } @Override public int hashCode() { return Objects.hash(attrs, uid, deviceId, opPkg, reason); } /** Return true if vibration is a repeating vibration. */ abstract boolean isRepeating(); @Override public String toString() { return "CallerInfo{" + " uid=" + uid + ", opPkg=" + opPkg + ", deviceId=" + deviceId + ", attrs=" + attrs + ", reason=" + reason + '}'; } } /** Return {@link VibrationSession.DebugInfo} with read-only debug data about this vibration. */ abstract VibrationSession.DebugInfo getDebugInfo(); /** Return {@link VibrationStats.StatsInfo} with read-only metrics about this vibration. */ abstract VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis); /** Immutable info passed as a signal to end a vibration. */ static final class EndInfo { /** The {@link Status} to be set to the vibration when it ends with this info. */ /** The vibration status to be set when it ends with this info. */ @NonNull public final Status status; public final VibrationSession.Status status; /** Info about the process that ended the vibration. */ public final CallerInfo endedBy; public final VibrationSession.CallerInfo endedBy; EndInfo(@NonNull Vibration.Status status) { EndInfo(@NonNull VibrationSession.Status status) { this(status, null); } EndInfo(@NonNull Vibration.Status status, @Nullable CallerInfo endedBy) { EndInfo(@NonNull VibrationSession.Status status, @Nullable VibrationSession.CallerInfo endedBy) { this.status = status; this.endedBy = endedBy; } Loading Loading @@ -211,10 +148,10 @@ abstract class Vibration { * Since DebugInfo can be kept in memory for a long time, it shouldn't hold any references to * potentially expensive or resource-linked objects, such as {@link IBinder}. */ static final class DebugInfo { final Status mStatus; static final class DebugInfoImpl implements VibrationSession.DebugInfo { final VibrationSession.Status mStatus; final long mCreateTime; final CallerInfo mCallerInfo; final VibrationSession.CallerInfo mCallerInfo; @Nullable final CombinedVibration mPlayedEffect; Loading @@ -226,9 +163,10 @@ abstract class Vibration { private final int mScaleLevel; private final float mAdaptiveScale; DebugInfo(Status status, VibrationStats stats, @Nullable CombinedVibration playedEffect, DebugInfoImpl(VibrationSession.Status status, VibrationStats stats, @Nullable CombinedVibration playedEffect, @Nullable CombinedVibration originalEffect, int scaleLevel, float adaptiveScale, @NonNull CallerInfo callerInfo) { float adaptiveScale, @NonNull VibrationSession.CallerInfo callerInfo) { Objects.requireNonNull(callerInfo); mCreateTime = stats.getCreateTimeDebug(); mStartTime = stats.getStartTimeDebug(); Loading @@ -242,6 +180,27 @@ abstract class Vibration { mStatus = status; } @Override public VibrationSession.Status getStatus() { return mStatus; } @Override public long getCreateUptimeMillis() { return mCreateTime; } @Override public VibrationSession.CallerInfo getCallerInfo() { return mCallerInfo; } @Nullable @Override public Object getDumpAggregationKey() { return mPlayedEffect; } @Override public String toString() { return "createTime: " + formatTime(mCreateTime, /*includeDate=*/ true) Loading @@ -257,17 +216,13 @@ abstract class Vibration { + ", callerInfo: " + mCallerInfo; } void logMetrics(VibratorFrameworkStatsLogger statsLogger) { @Override public void logMetrics(VibratorFrameworkStatsLogger statsLogger) { statsLogger.logVibrationAdaptiveHapticScale(mCallerInfo.uid, mAdaptiveScale); } /** * 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) { @Override public void dumpCompact(IndentingPrintWriter pw) { boolean isExternalVibration = mPlayedEffect == null; String timingsStr = String.format(Locale.ROOT, "%s | %8s | %20s | duration: %5dms | start: %12s | end: %12s", Loading Loading @@ -299,8 +254,8 @@ abstract class Vibration { pw.println(timingsStr + paramStr + audioUsageStr + callerStr + effectStr); } /** Write this info into given {@link PrintWriter}. */ void dump(IndentingPrintWriter pw) { @Override public void dump(IndentingPrintWriter pw) { pw.println("Vibration:"); pw.increaseIndent(); pw.println("status = " + mStatus.name().toLowerCase(Locale.ROOT)); Loading @@ -317,8 +272,8 @@ abstract class Vibration { pw.decreaseIndent(); } /** Write this info into given {@code fieldId} on {@link ProtoOutputStream}. */ void dump(ProtoOutputStream proto, long fieldId) { @Override public void dump(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); proto.write(VibrationProto.START_TIME, mStartTime); proto.write(VibrationProto.END_TIME, mEndTime); Loading services/core/java/com/android/server/vibrator/VibrationSession.java 0 → 100644 +208 −0 File added.Preview size limit exceeded, changes collapsed. Show changes Loading
services/core/java/com/android/server/vibrator/ExternalVibrationSession.java 0 → 100644 +146 −0 Original line number Diff line number Diff line /* * 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.annotation.Nullable; import android.content.Context; import android.os.ExternalVibration; import android.os.ExternalVibrationScale; import android.os.IBinder; import android.os.VibrationAttributes; import android.os.vibrator.Flags; import com.android.internal.util.FrameworkStatsLog; /** * A vibration session holding a single {@link ExternalVibration} request. */ final class ExternalVibrationSession extends Vibration implements VibrationSession, IBinder.DeathRecipient { private final ExternalVibration mExternalVibration; private final ExternalVibrationScale mScale = new ExternalVibrationScale(); @Nullable private Runnable mBinderDeathCallback; ExternalVibrationSession(ExternalVibration externalVibration) { super(externalVibration.getToken(), new CallerInfo( externalVibration.getVibrationAttributes(), externalVibration.getUid(), // TODO(b/249785241): Find a way to link ExternalVibration to a VirtualDevice // instead of using DEVICE_ID_INVALID here and relying on the UID checks. Context.DEVICE_ID_INVALID, externalVibration.getPackage(), null)); mExternalVibration = externalVibration; } public ExternalVibrationScale getScale() { return mScale; } @Override public CallerInfo getCallerInfo() { return callerInfo; } @Override public VibrationSession.DebugInfo getDebugInfo() { return new Vibration.DebugInfoImpl(getStatus(), stats, /* playedEffect= */ null, /* originalEffect= */ null, mScale.scaleLevel, mScale.adaptiveHapticsScale, callerInfo); } @Override public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) { return new VibrationStats.StatsInfo( mExternalVibration.getUid(), FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__EXTERNAL, mExternalVibration.getVibrationAttributes().getUsage(), getStatus(), stats, completionUptimeMillis); } @Override public boolean isRepeating() { // We don't currently know if the external vibration is repeating, so we just use a // heuristic based on the usage. Ideally this would be propagated in the ExternalVibration. int usage = mExternalVibration.getVibrationAttributes().getUsage(); return usage == VibrationAttributes.USAGE_RINGTONE || usage == VibrationAttributes.USAGE_ALARM; } @Override public void linkToDeath(Runnable callback) { synchronized (this) { mBinderDeathCallback = callback; } mExternalVibration.linkToDeath(this); } @Override public void unlinkToDeath() { mExternalVibration.unlinkToDeath(this); synchronized (this) { mBinderDeathCallback = null; } } @Override public void binderDied() { synchronized (this) { if (mBinderDeathCallback != null) { mBinderDeathCallback.run(); } } } @Override void end(EndInfo endInfo) { super.end(endInfo); if (stats.hasStarted()) { // External vibration doesn't have feedback from total time the vibrator was playing // with non-zero amplitude, so we use the duration between start and end times of // the vibration as the time the vibrator was ON, since the haptic channels are // open for this duration and can receive vibration waveform data. stats.reportVibratorOn(stats.getEndUptimeMillis() - stats.getStartUptimeMillis()); } } @Override public void notifyEnded() { // Notify external client that this vibration should stop sending data to the vibrator. mExternalVibration.mute(); } boolean isHoldingSameVibration(ExternalVibration vib) { return mExternalVibration.equals(vib); } void muteScale() { mScale.scaleLevel = ExternalVibrationScale.ScaleLevel.SCALE_MUTE; if (Flags.hapticsScaleV2Enabled()) { mScale.scaleFactor = 0; } } void scale(VibrationScaler scaler, int usage) { mScale.scaleLevel = scaler.getScaleLevel(usage); if (Flags.hapticsScaleV2Enabled()) { mScale.scaleFactor = scaler.getScaleFactor(usage); } mScale.adaptiveHapticsScale = scaler.getAdaptiveHapticsScale(usage); stats.reportAdaptiveScale(mScale.adaptiveHapticsScale); } }
services/core/java/com/android/server/vibrator/HalVibration.java +9 −29 Original line number Diff line number Diff line Loading @@ -51,37 +51,22 @@ final class HalVibration extends Vibration { @NonNull private volatile CombinedVibration mEffectToPlay; /** 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) { @NonNull VibrationSession.CallerInfo callerInfo) { super(token, callerInfo); mOriginalEffect = effect; mEffectToPlay = effect; mStatus = Vibration.Status.RUNNING; mScaleLevel = VibrationScaler.SCALE_NONE; mAdaptiveScale = VibrationScaler.ADAPTIVE_SCALE_NONE; } /** * Set the {@link Status} of this vibration and reports the current system time as this * vibration end time, for debugging purposes. * * <p>This method will only accept given value if the current status is {@link * Status#RUNNING}. */ public void end(EndInfo info) { if (hasEnded()) { // Vibration already ended, keep first ending status set and ignore this one. return; } mStatus = info.status; stats.reportEnded(info.endedBy); @Override public void end(EndInfo endInfo) { super.end(endInfo); mCompletionLatch.countDown(); } Loading Loading @@ -144,11 +129,6 @@ final class HalVibration extends Vibration { // No need to update fallback effects, they are already configured per device. } /** Return true is current status is different from {@link Status#RUNNING}. */ public boolean hasEnded() { return mStatus != Status.RUNNING; } @Override public boolean isRepeating() { return mOriginalEffect.getDuration() == Long.MAX_VALUE; Loading @@ -159,16 +139,16 @@ final class HalVibration extends Vibration { return mEffectToPlay; } /** Return {@link Vibration.DebugInfo} with read-only debug information about this vibration. */ public Vibration.DebugInfo getDebugInfo() { @Override public VibrationSession.DebugInfo getDebugInfo() { // Clear the original effect if it's the same as the effect that was played, for simplicity CombinedVibration originalEffect = Objects.equals(mOriginalEffect, mEffectToPlay) ? null : mOriginalEffect; return new Vibration.DebugInfo(mStatus, stats, mEffectToPlay, originalEffect, return new Vibration.DebugInfoImpl(getStatus(), stats, mEffectToPlay, originalEffect, mScaleLevel, mAdaptiveScale, callerInfo); } /** Return {@link VibrationStats.StatsInfo} with read-only metrics about this vibration. */ @Override public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) { int vibrationType = mEffectToPlay.hasVendorEffects() ? FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__VENDOR Loading @@ -176,7 +156,7 @@ final class HalVibration extends Vibration { ? FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__REPEATED : FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__SINGLE; return new VibrationStats.StatsInfo( callerInfo.uid, vibrationType, callerInfo.attrs.getUsage(), mStatus, callerInfo.uid, vibrationType, callerInfo.attrs.getUsage(), getStatus(), stats, completionUptimeMillis); } Loading
services/core/java/com/android/server/vibrator/InputDeviceDelegate.java +2 −1 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.util.SparseArray; import android.view.InputDevice; import com.android.internal.annotations.GuardedBy; import com.android.server.vibrator.VibrationSession.CallerInfo; /** Delegates vibrations to all connected {@link InputDevice} with one or more vibrators. */ final class InputDeviceDelegate implements InputManager.InputDeviceListener { Loading Loading @@ -93,7 +94,7 @@ final class InputDeviceDelegate implements InputManager.InputDeviceListener { * * @return {@link #isAvailable()} */ public boolean vibrateIfAvailable(Vibration.CallerInfo callerInfo, CombinedVibration effect) { public boolean vibrateIfAvailable(CallerInfo callerInfo, CombinedVibration effect) { synchronized (mLock) { for (int i = 0; i < mInputDeviceVibrators.size(); i++) { mInputDeviceVibrators.valueAt(i).vibrate(callerInfo.uid, callerInfo.opPkg, effect, Loading
services/core/java/com/android/server/vibrator/Vibration.java +70 −115 Original line number Diff line number Diff line Loading @@ -31,7 +31,6 @@ import android.os.vibrator.VibrationEffectSegment; import android.util.IndentingPrintWriter; import android.util.proto.ProtoOutputStream; import java.io.PrintWriter; import java.time.Instant; import java.time.ZoneId; import java.time.format.DateTimeFormatter; Loading @@ -52,131 +51,69 @@ abstract class Vibration { private static final AtomicInteger sNextVibrationId = new AtomicInteger(1); // 0 = no callback public final long id; public final CallerInfo callerInfo; public final VibrationSession.CallerInfo callerInfo; public final VibrationStats stats = new VibrationStats(); public final IBinder callerToken; /** Vibration status with reference to values from vibratormanagerservice.proto for logging. */ enum Status { UNKNOWN(VibrationProto.UNKNOWN), RUNNING(VibrationProto.RUNNING), FINISHED(VibrationProto.FINISHED), FINISHED_UNEXPECTED(VibrationProto.FINISHED_UNEXPECTED), FORWARDED_TO_INPUT_DEVICES(VibrationProto.FORWARDED_TO_INPUT_DEVICES), CANCELLED_BINDER_DIED(VibrationProto.CANCELLED_BINDER_DIED), CANCELLED_BY_SCREEN_OFF(VibrationProto.CANCELLED_BY_SCREEN_OFF), CANCELLED_BY_SETTINGS_UPDATE(VibrationProto.CANCELLED_BY_SETTINGS_UPDATE), CANCELLED_BY_USER(VibrationProto.CANCELLED_BY_USER), CANCELLED_BY_FOREGROUND_USER(VibrationProto.CANCELLED_BY_FOREGROUND_USER), CANCELLED_BY_UNKNOWN_REASON(VibrationProto.CANCELLED_BY_UNKNOWN_REASON), CANCELLED_SUPERSEDED(VibrationProto.CANCELLED_SUPERSEDED), CANCELLED_BY_APP_OPS(VibrationProto.CANCELLED_BY_APP_OPS), IGNORED_ERROR_APP_OPS(VibrationProto.IGNORED_ERROR_APP_OPS), IGNORED_ERROR_CANCELLING(VibrationProto.IGNORED_ERROR_CANCELLING), IGNORED_ERROR_SCHEDULING(VibrationProto.IGNORED_ERROR_SCHEDULING), IGNORED_ERROR_TOKEN(VibrationProto.IGNORED_ERROR_TOKEN), IGNORED_APP_OPS(VibrationProto.IGNORED_APP_OPS), IGNORED_BACKGROUND(VibrationProto.IGNORED_BACKGROUND), IGNORED_MISSING_PERMISSION(VibrationProto.IGNORED_MISSING_PERMISSION), IGNORED_UNSUPPORTED(VibrationProto.IGNORED_UNSUPPORTED), IGNORED_FOR_EXTERNAL(VibrationProto.IGNORED_FOR_EXTERNAL), IGNORED_FOR_HIGHER_IMPORTANCE(VibrationProto.IGNORED_FOR_HIGHER_IMPORTANCE), IGNORED_FOR_ONGOING(VibrationProto.IGNORED_FOR_ONGOING), IGNORED_FOR_POWER(VibrationProto.IGNORED_FOR_POWER), IGNORED_FOR_RINGER_MODE(VibrationProto.IGNORED_FOR_RINGER_MODE), IGNORED_FOR_SETTINGS(VibrationProto.IGNORED_FOR_SETTINGS), IGNORED_SUPERSEDED(VibrationProto.IGNORED_SUPERSEDED), IGNORED_FROM_VIRTUAL_DEVICE(VibrationProto.IGNORED_FROM_VIRTUAL_DEVICE), IGNORED_ON_WIRELESS_CHARGER(VibrationProto.IGNORED_ON_WIRELESS_CHARGER); private final int mProtoEnumValue; Status(int value) { mProtoEnumValue = value; } public int getProtoEnumValue() { return mProtoEnumValue; } } Vibration(@NonNull IBinder token, @NonNull CallerInfo callerInfo) { private VibrationSession.Status mStatus; Vibration(@NonNull IBinder token, @NonNull VibrationSession.CallerInfo callerInfo) { Objects.requireNonNull(token); Objects.requireNonNull(callerInfo); mStatus = VibrationSession.Status.RUNNING; this.id = sNextVibrationId.getAndIncrement(); this.callerToken = token; this.callerInfo = callerInfo; } /** Return true if vibration is a repeating vibration. */ abstract boolean isRepeating(); VibrationSession.Status getStatus() { return mStatus; } /** Return true is current status is different from {@link VibrationSession.Status#RUNNING}. */ boolean hasEnded() { return mStatus != VibrationSession.Status.RUNNING; } /** * Holds lightweight immutable info on the process that triggered the vibration. This data * could potentially be kept in memory for a long time for bugreport dumpsys operations. * Set the {@link VibrationSession} of this vibration and reports the current system time as * this vibration end time, for debugging purposes. * * Since CallerInfo can be kept in memory for a long time, it shouldn't hold any references to * potentially expensive or resource-linked objects, such as {@link IBinder}. * <p>This method will only accept given value if the current status is {@link * VibrationSession.Status#RUNNING}. */ static final class CallerInfo { public final VibrationAttributes attrs; public final int uid; public final int deviceId; public final String opPkg; public final String reason; CallerInfo(@NonNull VibrationAttributes attrs, int uid, int deviceId, String opPkg, String reason) { Objects.requireNonNull(attrs); this.attrs = attrs; this.uid = uid; this.deviceId = deviceId; this.opPkg = opPkg; this.reason = reason; void end(Vibration.EndInfo endInfo) { if (hasEnded()) { // Vibration already ended, keep first ending status set and ignore this one. return; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof CallerInfo)) return false; CallerInfo that = (CallerInfo) o; return Objects.equals(attrs, that.attrs) && uid == that.uid && deviceId == that.deviceId && Objects.equals(opPkg, that.opPkg) && Objects.equals(reason, that.reason); mStatus = endInfo.status; stats.reportEnded(endInfo.endedBy); } @Override public int hashCode() { return Objects.hash(attrs, uid, deviceId, opPkg, reason); } /** Return true if vibration is a repeating vibration. */ abstract boolean isRepeating(); @Override public String toString() { return "CallerInfo{" + " uid=" + uid + ", opPkg=" + opPkg + ", deviceId=" + deviceId + ", attrs=" + attrs + ", reason=" + reason + '}'; } } /** Return {@link VibrationSession.DebugInfo} with read-only debug data about this vibration. */ abstract VibrationSession.DebugInfo getDebugInfo(); /** Return {@link VibrationStats.StatsInfo} with read-only metrics about this vibration. */ abstract VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis); /** Immutable info passed as a signal to end a vibration. */ static final class EndInfo { /** The {@link Status} to be set to the vibration when it ends with this info. */ /** The vibration status to be set when it ends with this info. */ @NonNull public final Status status; public final VibrationSession.Status status; /** Info about the process that ended the vibration. */ public final CallerInfo endedBy; public final VibrationSession.CallerInfo endedBy; EndInfo(@NonNull Vibration.Status status) { EndInfo(@NonNull VibrationSession.Status status) { this(status, null); } EndInfo(@NonNull Vibration.Status status, @Nullable CallerInfo endedBy) { EndInfo(@NonNull VibrationSession.Status status, @Nullable VibrationSession.CallerInfo endedBy) { this.status = status; this.endedBy = endedBy; } Loading Loading @@ -211,10 +148,10 @@ abstract class Vibration { * Since DebugInfo can be kept in memory for a long time, it shouldn't hold any references to * potentially expensive or resource-linked objects, such as {@link IBinder}. */ static final class DebugInfo { final Status mStatus; static final class DebugInfoImpl implements VibrationSession.DebugInfo { final VibrationSession.Status mStatus; final long mCreateTime; final CallerInfo mCallerInfo; final VibrationSession.CallerInfo mCallerInfo; @Nullable final CombinedVibration mPlayedEffect; Loading @@ -226,9 +163,10 @@ abstract class Vibration { private final int mScaleLevel; private final float mAdaptiveScale; DebugInfo(Status status, VibrationStats stats, @Nullable CombinedVibration playedEffect, DebugInfoImpl(VibrationSession.Status status, VibrationStats stats, @Nullable CombinedVibration playedEffect, @Nullable CombinedVibration originalEffect, int scaleLevel, float adaptiveScale, @NonNull CallerInfo callerInfo) { float adaptiveScale, @NonNull VibrationSession.CallerInfo callerInfo) { Objects.requireNonNull(callerInfo); mCreateTime = stats.getCreateTimeDebug(); mStartTime = stats.getStartTimeDebug(); Loading @@ -242,6 +180,27 @@ abstract class Vibration { mStatus = status; } @Override public VibrationSession.Status getStatus() { return mStatus; } @Override public long getCreateUptimeMillis() { return mCreateTime; } @Override public VibrationSession.CallerInfo getCallerInfo() { return mCallerInfo; } @Nullable @Override public Object getDumpAggregationKey() { return mPlayedEffect; } @Override public String toString() { return "createTime: " + formatTime(mCreateTime, /*includeDate=*/ true) Loading @@ -257,17 +216,13 @@ abstract class Vibration { + ", callerInfo: " + mCallerInfo; } void logMetrics(VibratorFrameworkStatsLogger statsLogger) { @Override public void logMetrics(VibratorFrameworkStatsLogger statsLogger) { statsLogger.logVibrationAdaptiveHapticScale(mCallerInfo.uid, mAdaptiveScale); } /** * 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) { @Override public void dumpCompact(IndentingPrintWriter pw) { boolean isExternalVibration = mPlayedEffect == null; String timingsStr = String.format(Locale.ROOT, "%s | %8s | %20s | duration: %5dms | start: %12s | end: %12s", Loading Loading @@ -299,8 +254,8 @@ abstract class Vibration { pw.println(timingsStr + paramStr + audioUsageStr + callerStr + effectStr); } /** Write this info into given {@link PrintWriter}. */ void dump(IndentingPrintWriter pw) { @Override public void dump(IndentingPrintWriter pw) { pw.println("Vibration:"); pw.increaseIndent(); pw.println("status = " + mStatus.name().toLowerCase(Locale.ROOT)); Loading @@ -317,8 +272,8 @@ abstract class Vibration { pw.decreaseIndent(); } /** Write this info into given {@code fieldId} on {@link ProtoOutputStream}. */ void dump(ProtoOutputStream proto, long fieldId) { @Override public void dump(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); proto.write(VibrationProto.START_TIME, mStartTime); proto.write(VibrationProto.END_TIME, mEndTime); Loading
services/core/java/com/android/server/vibrator/VibrationSession.java 0 → 100644 +208 −0 File added.Preview size limit exceeded, changes collapsed. Show changes