Merge "Add more information to vibrator dumpsys"

This commit is contained in:
TreeHugger Robot
2020-10-13 14:54:05 +00:00
committed by Android (Google) Code Review
2 changed files with 392 additions and 142 deletions

View File

@@ -21,6 +21,12 @@ option java_multiple_files = true;
import "frameworks/base/core/proto/android/privacy.proto";
message OneShotProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
repeated int32 duration = 1;
repeated int32 amplitude = 2;
}
message WaveformProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
repeated int32 timings = 1;
@@ -35,20 +41,41 @@ message PrebakedProto {
optional int32 fallback = 3;
}
message ComposedProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
repeated int32 effect_ids = 1;
repeated float effect_scales = 2;
repeated int32 delays = 3;
}
// A com.android.os.VibrationEffect object.
message VibrationEffectProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional OneShotProto oneshot = 3;
optional WaveformProto waveform = 1;
optional PrebakedProto prebaked = 2;
optional ComposedProto composed = 4;
}
message VibrationAttributesProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional int32 usage = 1;
optional int32 audio_usage = 2;
optional int32 flags = 3;
}
// Next id: 7
message VibrationProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional int64 start_time = 1;
optional int64 end_time = 4;
optional VibrationEffectProto effect = 2;
optional VibrationEffectProto origin_effect = 3;
optional VibrationEffectProto original_effect = 3;
optional VibrationAttributesProto attributes = 5;
optional int32 status = 6;
}
// Next id: 17
message VibratorServiceDumpProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional VibrationProto current_vibration = 1;
@@ -57,10 +84,14 @@ message VibratorServiceDumpProto {
optional bool vibrator_under_external_control = 4;
optional bool low_power_mode = 5;
optional int32 haptic_feedback_intensity = 6;
optional int32 haptic_feedback_default_intensity = 14;
optional int32 notification_intensity = 7;
optional int32 notification_default_intensity = 15;
optional int32 ring_intensity = 8;
optional int32 ring_default_intensity = 16;
repeated VibrationProto previous_ring_vibrations = 9;
repeated VibrationProto previous_notification_vibrations = 10;
repeated VibrationProto previous_alarm_vibrations = 11;
repeated VibrationProto previous_vibrations = 12;
repeated VibrationProto previous_external_vibrations = 13;
}

View File

@@ -33,7 +33,6 @@ import android.database.ContentObserver;
import android.hardware.input.InputManager;
import android.hardware.vibrator.IVibrator;
import android.hardware.vibrator.V1_0.EffectStrength;
import android.icu.text.DateFormat;
import android.media.AudioManager;
import android.os.BatteryStats;
import android.os.Binder;
@@ -80,6 +79,7 @@ import libcore.util.NativeAllocationRegistry;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
@@ -90,6 +90,8 @@ import java.util.concurrent.atomic.AtomicInteger;
public class VibratorService extends IVibratorService.Stub
implements InputManager.InputDeviceListener {
private static final String TAG = "VibratorService";
private static final SimpleDateFormat DEBUG_DATE_FORMAT =
new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
private static final boolean DEBUG = false;
private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service";
@@ -126,7 +128,7 @@ public class VibratorService extends IVibratorService.Stub
private final LinkedList<VibrationInfo> mPreviousRingVibrations;
private final LinkedList<VibrationInfo> mPreviousNotificationVibrations;
private final LinkedList<VibrationInfo> mPreviousAlarmVibrations;
private final LinkedList<ExternalVibration> mPreviousExternalVibrations;
private final LinkedList<VibrationInfo> mPreviousExternalVibrations;
private final LinkedList<VibrationInfo> mPreviousVibrations;
private final int mPreviousVibrationsLimit;
private final boolean mAllowPriorityVibrationsInLowPowerMode;
@@ -162,7 +164,7 @@ public class VibratorService extends IVibratorService.Stub
@GuardedBy("mLock")
private Vibration mCurrentVibration;
private int mCurVibUid = -1;
private ExternalVibration mCurrentExternalVibration;
private ExternalVibrationHolder mCurrentExternalVibration;
private boolean mVibratorUnderExternalControl;
private boolean mLowPowerMode;
@GuardedBy("mLock")
@@ -231,19 +233,12 @@ public class VibratorService extends IVibratorService.Stub
void onComplete(long vibrationId);
}
/**
* Holder for a vibration to be played. This class can be shared with native methods for
* hardware callback support.
*/
/** Holder for a {@link VibrationEffect}. */
private final class Vibration implements IBinder.DeathRecipient {
public final IBinder token;
// Start time in CLOCK_BOOTTIME base.
public final long startTime;
// Start time in unix epoch time. Only to be used for debugging purposes and to correlate
// with other system events, any duration calculations should be done use startTime so as
// not to be affected by discontinuities created by RTC adjustments.
public final long startTimeDebug;
public final VibrationAttributes attrs;
public final long id;
public final int uid;
@@ -255,18 +250,28 @@ public class VibratorService extends IVibratorService.Stub
// The original effect that was requested. Typically these two things differ because
// the effect was scaled based on the users vibration intensity settings.
public VibrationEffect originalEffect;
// The scale applied to the original effect.
public float scale;
// Start/end times in unix epoch time. Only to be used for debugging purposes and to
// correlate with other system events, any duration calculations should be done use
// startTime so as not to be affected by discontinuities created by RTC adjustments.
private final long mStartTimeDebug;
private long mEndTimeDebug;
private VibrationInfo.Status mStatus;
private Vibration(IBinder token, VibrationEffect effect,
VibrationAttributes attrs, int uid, String opPkg, String reason) {
this.token = token;
this.id = mNextVibrationId.getAndIncrement();
this.effect = effect;
this.id = mNextVibrationId.getAndIncrement();
this.startTime = SystemClock.elapsedRealtime();
this.startTimeDebug = System.currentTimeMillis();
this.attrs = attrs;
this.uid = uid;
this.opPkg = opPkg;
this.reason = reason;
mStartTimeDebug = System.currentTimeMillis();
mStatus = VibrationInfo.Status.RUNNING;
}
@Override
@@ -276,11 +281,24 @@ public class VibratorService extends IVibratorService.Stub
if (DEBUG) {
Slog.d(TAG, "Vibration finished because binder died, cleaning up");
}
doCancelVibrateLocked();
doCancelVibrateLocked(VibrationInfo.Status.CANCELLED);
}
}
}
public void end(VibrationInfo.Status status) {
if (hasEnded()) {
// Vibration already ended, keep first ending status set and ignore this one.
return;
}
mStatus = status;
mEndTimeDebug = System.currentTimeMillis();
}
public boolean hasEnded() {
return mStatus != VibrationInfo.Status.RUNNING;
}
public boolean hasTimeoutLongerThan(long millis) {
final long duration = effect.getDuration();
return duration >= 0 && duration > millis;
@@ -308,40 +326,109 @@ public class VibratorService extends IVibratorService.Stub
public VibrationInfo toInfo() {
return new VibrationInfo(
startTimeDebug, effect, originalEffect, attrs, uid, opPkg, reason);
mStartTimeDebug, mEndTimeDebug, effect, originalEffect, scale, attrs,
uid, opPkg, reason, mStatus);
}
}
private static class VibrationInfo {
/** Holder for a {@link ExternalVibration}. */
private final class ExternalVibrationHolder {
public final ExternalVibration externalVibration;
public int scale;
private final long mStartTimeDebug;
private long mEndTimeDebug;
private VibrationInfo.Status mStatus;
private ExternalVibrationHolder(ExternalVibration externalVibration) {
this.externalVibration = externalVibration;
this.scale = SCALE_NONE;
mStartTimeDebug = System.currentTimeMillis();
mStatus = VibrationInfo.Status.RUNNING;
}
public void end(VibrationInfo.Status status) {
if (mStatus != VibrationInfo.Status.RUNNING) {
// Vibration already ended, keep first ending status set and ignore this one.
return;
}
mStatus = status;
mEndTimeDebug = System.currentTimeMillis();
}
public VibrationInfo toInfo() {
return new VibrationInfo(
mStartTimeDebug, mEndTimeDebug, /* effect= */ null, /* originalEffect= */ null,
scale, externalVibration.getVibrationAttributes(),
externalVibration.getUid(), externalVibration.getPackage(),
/* reason= */ null, mStatus);
}
}
/** Debug information about vibrations. */
private static class VibrationInfo {
public enum Status {
RUNNING,
FINISHED,
CANCELLED,
ERROR_APP_OPS,
IGNORED,
IGNORED_APP_OPS,
IGNORED_BACKGROUND,
IGNORED_RINGTONE,
IGNORED_UNKNOWN_VIBRATION,
IGNORED_UNSUPPORTED,
IGNORED_FOR_ALARM,
IGNORED_FOR_EXTERNAL,
IGNORED_FOR_ONGOING,
IGNORED_FOR_POWER,
IGNORED_FOR_SETTINGS,
}
private final long mStartTimeDebug;
private final long mEndTimeDebug;
private final VibrationEffect mEffect;
private final VibrationEffect mOriginalEffect;
private final float mScale;
private final VibrationAttributes mAttrs;
private final int mUid;
private final String mOpPkg;
private final String mReason;
private final VibrationInfo.Status mStatus;
VibrationInfo(long startTimeDebug, VibrationEffect effect,
VibrationEffect originalEffect, VibrationAttributes attrs, int uid,
String opPkg, String reason) {
VibrationInfo(long startTimeDebug, long endTimeDebug, VibrationEffect effect,
VibrationEffect originalEffect, float scale, VibrationAttributes attrs,
int uid, String opPkg, String reason, VibrationInfo.Status status) {
mStartTimeDebug = startTimeDebug;
mEndTimeDebug = endTimeDebug;
mEffect = effect;
mOriginalEffect = originalEffect;
mScale = scale;
mAttrs = attrs;
mUid = uid;
mOpPkg = opPkg;
mReason = reason;
mStatus = status;
}
@Override
public String toString() {
return new StringBuilder()
.append("startTime: ")
.append(DateFormat.getDateTimeInstance().format(new Date(mStartTimeDebug)))
.append(DEBUG_DATE_FORMAT.format(new Date(mStartTimeDebug)))
.append(", endTime: ")
.append(mEndTimeDebug == 0 ? null
: DEBUG_DATE_FORMAT.format(new Date(mEndTimeDebug)))
.append(", status: ")
.append(mStatus.name().toLowerCase())
.append(", effect: ")
.append(mEffect)
.append(", originalEffect: ")
.append(mOriginalEffect)
.append(", scale: ")
.append(String.format("%.2f", mScale))
.append(", attrs: ")
.append(mAttrs)
.append(", uid: ")
@@ -354,11 +441,80 @@ public class VibratorService extends IVibratorService.Stub
}
void dumpProto(ProtoOutputStream proto, long fieldId) {
synchronized (this) {
final long token = proto.start(fieldId);
proto.write(VibrationProto.START_TIME, mStartTimeDebug);
proto.end(token);
final long token = proto.start(fieldId);
proto.write(VibrationProto.START_TIME, mStartTimeDebug);
proto.write(VibrationProto.END_TIME, mEndTimeDebug);
proto.write(VibrationProto.STATUS, mStatus.ordinal());
final long attrsToken = proto.start(VibrationProto.ATTRIBUTES);
proto.write(VibrationAttributesProto.USAGE, mAttrs.getUsage());
proto.write(VibrationAttributesProto.AUDIO_USAGE, mAttrs.getAudioUsage());
proto.write(VibrationAttributesProto.FLAGS, mAttrs.getFlags());
proto.end(attrsToken);
if (mEffect != null) {
dumpEffect(proto, VibrationProto.EFFECT, mEffect);
}
if (mOriginalEffect != null) {
dumpEffect(proto, VibrationProto.ORIGINAL_EFFECT, mOriginalEffect);
}
proto.end(token);
}
private void dumpEffect(ProtoOutputStream proto, long fieldId, VibrationEffect effect) {
final long token = proto.start(fieldId);
if (effect instanceof VibrationEffect.OneShot) {
dumpEffect(proto, VibrationEffectProto.ONESHOT, (VibrationEffect.OneShot) effect);
} else if (effect instanceof VibrationEffect.Waveform) {
dumpEffect(proto, VibrationEffectProto.WAVEFORM, (VibrationEffect.Waveform) effect);
} else if (effect instanceof VibrationEffect.Prebaked) {
dumpEffect(proto, VibrationEffectProto.PREBAKED, (VibrationEffect.Prebaked) effect);
} else if (effect instanceof VibrationEffect.Composed) {
dumpEffect(proto, VibrationEffectProto.COMPOSED, (VibrationEffect.Composed) effect);
}
proto.end(token);
}
private void dumpEffect(ProtoOutputStream proto, long fieldId,
VibrationEffect.OneShot effect) {
final long token = proto.start(fieldId);
proto.write(OneShotProto.DURATION, (int) effect.getDuration());
proto.write(OneShotProto.AMPLITUDE, effect.getAmplitude());
proto.end(token);
}
private void dumpEffect(ProtoOutputStream proto, long fieldId,
VibrationEffect.Waveform effect) {
final long token = proto.start(fieldId);
for (long timing : effect.getTimings()) {
proto.write(WaveformProto.TIMINGS, (int) timing);
}
for (int amplitude : effect.getAmplitudes()) {
proto.write(WaveformProto.AMPLITUDES, amplitude);
}
proto.write(WaveformProto.REPEAT, effect.getRepeatIndex() >= 0);
proto.end(token);
}
private void dumpEffect(ProtoOutputStream proto, long fieldId,
VibrationEffect.Prebaked effect) {
final long token = proto.start(fieldId);
proto.write(PrebakedProto.EFFECT_ID, effect.getId());
proto.write(PrebakedProto.EFFECT_STRENGTH, effect.getEffectStrength());
proto.write(PrebakedProto.FALLBACK, effect.shouldFallback());
proto.end(token);
}
private void dumpEffect(ProtoOutputStream proto, long fieldId,
VibrationEffect.Composed effect) {
final long token = proto.start(fieldId);
for (PrimitiveEffect primitive : effect.getPrimitiveEffects()) {
proto.write(ComposedProto.EFFECT_IDS, primitive.id);
proto.write(ComposedProto.EFFECT_SCALES, primitive.scale);
proto.write(ComposedProto.DELAYS, primitive.delay);
}
proto.end(token);
}
}
@@ -549,7 +705,7 @@ public class VibratorService extends IVibratorService.Stub
if (DEBUG) {
Slog.d(TAG, "Vibration finished by callback, cleaning up");
}
doCancelVibrateLocked();
doCancelVibrateLocked(VibrationInfo.Status.FINISHED);
}
}
}
@@ -792,6 +948,7 @@ public class VibratorService extends IVibratorService.Stub
}
attrs = fixupVibrationAttributes(attrs);
Vibration vib = new Vibration(token, effect, attrs, uid, opPkg, reason);
// If our current vibration is longer than the new vibration and is the same amplitude,
// then just let the current one finish.
@@ -808,6 +965,7 @@ public class VibratorService extends IVibratorService.Stub
Slog.d(TAG,
"Ignoring incoming vibration in favor of current vibration");
}
endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_ONGOING);
return;
}
}
@@ -819,6 +977,7 @@ public class VibratorService extends IVibratorService.Stub
if (DEBUG) {
Slog.d(TAG, "Ignoring incoming vibration for current external vibration");
}
endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_EXTERNAL);
return;
}
@@ -832,24 +991,29 @@ public class VibratorService extends IVibratorService.Stub
if (DEBUG) {
Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
}
endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_ALARM);
return;
}
Vibration vib = new Vibration(token, effect, attrs, uid, opPkg, reason);
if (mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)
> ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
&& !vib.isNotification() && !vib.isRingtone() && !vib.isAlarm()) {
Slog.e(TAG, "Ignoring incoming vibration as process with"
+ " uid= " + uid + " is background,"
+ " attrs= " + vib.attrs);
endVibrationLocked(vib, VibrationInfo.Status.IGNORED_BACKGROUND);
return;
}
linkVibration(vib);
final long ident = Binder.clearCallingIdentity();
try {
doCancelVibrateLocked();
doCancelVibrateLocked(VibrationInfo.Status.CANCELLED);
startVibrationLocked(vib);
addToPreviousVibrationsLocked(vib);
if (!vib.hasEnded() && mCurrentVibration.id != vib.id) {
// Vibration was unexpectedly ignored: add to list for debugging
endVibrationLocked(vib, VibrationInfo.Status.IGNORED);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -868,7 +1032,7 @@ public class VibratorService extends IVibratorService.Stub
return effect.getDuration() == Long.MAX_VALUE;
}
private void addToPreviousVibrationsLocked(Vibration vib) {
private void endVibrationLocked(Vibration vib, VibrationInfo.Status status) {
final LinkedList<VibrationInfo> previousVibrations;
if (vib.isRingtone()) {
previousVibrations = mPreviousRingVibrations;
@@ -883,9 +1047,18 @@ public class VibratorService extends IVibratorService.Stub
if (previousVibrations.size() > mPreviousVibrationsLimit) {
previousVibrations.removeFirst();
}
vib.end(status);
previousVibrations.addLast(vib.toInfo());
}
private void endVibrationLocked(ExternalVibrationHolder vib, VibrationInfo.Status status) {
if (mPreviousExternalVibrations.size() > mPreviousVibrationsLimit) {
mPreviousExternalVibrations.removeFirst();
}
vib.end(status);
mPreviousExternalVibrations.addLast(vib.toInfo());
}
@Override // Binder call
public void cancelVibrate(IBinder token) {
mContext.enforceCallingOrSelfPermission(
@@ -899,7 +1072,7 @@ public class VibratorService extends IVibratorService.Stub
}
final long ident = Binder.clearCallingIdentity();
try {
doCancelVibrateLocked();
doCancelVibrateLocked(VibrationInfo.Status.CANCELLED);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -908,7 +1081,7 @@ public class VibratorService extends IVibratorService.Stub
}
@GuardedBy("mLock")
private void doCancelVibrateLocked() {
private void doCancelVibrateLocked(VibrationInfo.Status status) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doCancelVibrateLocked");
try {
@@ -917,12 +1090,13 @@ public class VibratorService extends IVibratorService.Stub
mThread = null;
}
if (mCurrentExternalVibration != null) {
mCurrentExternalVibration.mute();
endVibrationLocked(mCurrentExternalVibration, status);
mCurrentExternalVibration.externalVibration.mute();
mCurrentExternalVibration = null;
setVibratorUnderExternalControl(false);
}
doVibratorOff();
reportFinishVibrationLocked();
reportFinishVibrationLocked(status);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
@@ -936,7 +1110,7 @@ public class VibratorService extends IVibratorService.Stub
synchronized (mLock) {
// Make sure the vibration is really done. This also reports that the vibration is
// finished.
doCancelVibrateLocked();
doCancelVibrateLocked(VibrationInfo.Status.FINISHED);
}
}
@@ -944,7 +1118,7 @@ public class VibratorService extends IVibratorService.Stub
private void startVibrationLocked(final Vibration vib) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked");
try {
final int intensity = getCurrentIntensityLocked(vib);
final int intensity = getCurrentIntensityLocked(vib.attrs.getUsage());
if (!shouldVibrate(vib, intensity)) {
return;
}
@@ -959,6 +1133,7 @@ public class VibratorService extends IVibratorService.Stub
private void startVibrationInnerLocked(Vibration vib) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationInnerLocked");
try {
// Set current vibration before starting it, so callback will work.
mCurrentVibration = vib;
if (vib.effect instanceof VibrationEffect.OneShot) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
@@ -971,18 +1146,21 @@ public class VibratorService extends IVibratorService.Stub
} else if (vib.effect instanceof VibrationEffect.Prebaked) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
doVibratorPrebakedEffectLocked(vib);
} else if (vib.effect instanceof VibrationEffect.Composed) {
} else if (vib.effect instanceof VibrationEffect.Composed) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
doVibratorComposedEffectLocked(vib);
} else {
Slog.e(TAG, "Unknown vibration type, ignoring");
endVibrationLocked(vib, VibrationInfo.Status.IGNORED_UNKNOWN_VIBRATION);
// The set current vibration is not actually playing, so drop it.
mCurrentVibration = null;
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
private boolean isAllowedToVibrateLocked(Vibration vib) {
private boolean shouldVibrateForPowerModeLocked(Vibration vib) {
if (!mLowPowerMode) {
return true;
}
@@ -993,14 +1171,28 @@ public class VibratorService extends IVibratorService.Stub
|| usage == VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
}
private int getCurrentIntensityLocked(Vibration vib) {
if (vib.isRingtone()) {
private int getCurrentIntensityLocked(int usageHint) {
if (isRingtone(usageHint)) {
return mRingIntensity;
} else if (vib.isNotification()) {
} else if (isNotification(usageHint)) {
return mNotificationIntensity;
} else if (vib.isHapticFeedback()) {
} else if (isHapticFeedback(usageHint)) {
return mHapticFeedbackIntensity;
} else if (vib.isAlarm()) {
} else if (isAlarm(usageHint)) {
return Vibrator.VIBRATION_INTENSITY_HIGH;
} else {
return Vibrator.VIBRATION_INTENSITY_MEDIUM;
}
}
private int getDefaultIntensity(int usageHint) {
if (isRingtone(usageHint)) {
return mVibrator.getDefaultRingVibrationIntensity();
} else if (isNotification(usageHint)) {
return mVibrator.getDefaultNotificationVibrationIntensity();
} else if (isHapticFeedback(usageHint)) {
return mVibrator.getDefaultHapticFeedbackIntensity();
} else if (isAlarm(usageHint)) {
return Vibrator.VIBRATION_INTENSITY_HIGH;
} else {
return Vibrator.VIBRATION_INTENSITY_MEDIUM;
@@ -1019,21 +1211,7 @@ public class VibratorService extends IVibratorService.Stub
return;
}
final int defaultIntensity;
if (vib.isRingtone()) {
defaultIntensity = mVibrator.getDefaultRingVibrationIntensity();
} else if (vib.isNotification()) {
defaultIntensity = mVibrator.getDefaultNotificationVibrationIntensity();
} else if (vib.isHapticFeedback()) {
defaultIntensity = mVibrator.getDefaultHapticFeedbackIntensity();
} else if (vib.isAlarm()) {
defaultIntensity = Vibrator.VIBRATION_INTENSITY_HIGH;
} else {
// If we don't know what kind of vibration we're playing then just skip scaling for
// now.
return;
}
final int defaultIntensity = getDefaultIntensity(vib.attrs.getUsage());
final ScaleLevel scale = mScaleLevels.get(intensity - defaultIntensity);
if (scale == null) {
// We should have scaling levels for all cases, so not being able to scale because of a
@@ -1045,6 +1223,7 @@ public class VibratorService extends IVibratorService.Stub
vib.originalEffect = vib.effect;
vib.effect = vib.effect.resolve(mDefaultVibrationAmplitude).scale(scale.factor);
vib.scale = scale.factor;
}
private boolean shouldVibrateForRingtone() {
@@ -1084,11 +1263,13 @@ public class VibratorService extends IVibratorService.Stub
}
private boolean shouldVibrate(Vibration vib, int intensity) {
if (!isAllowedToVibrateLocked(vib)) {
if (!shouldVibrateForPowerModeLocked(vib)) {
endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_POWER);
return false;
}
if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
endVibrationLocked(vib, VibrationInfo.Status.IGNORED_FOR_SETTINGS);
return false;
}
@@ -1096,6 +1277,7 @@ public class VibratorService extends IVibratorService.Stub
if (DEBUG) {
Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones");
}
endVibrationLocked(vib, VibrationInfo.Status.IGNORED_RINGTONE);
return false;
}
@@ -1105,6 +1287,9 @@ public class VibratorService extends IVibratorService.Stub
// We might be getting calls from within system_server, so we don't actually
// want to throw a SecurityException here.
Slog.w(TAG, "Would be an error: vibrate from uid " + vib.uid);
endVibrationLocked(vib, VibrationInfo.Status.ERROR_APP_OPS);
} else {
endVibrationLocked(vib, VibrationInfo.Status.IGNORED_APP_OPS);
}
return false;
}
@@ -1113,10 +1298,11 @@ public class VibratorService extends IVibratorService.Stub
}
@GuardedBy("mLock")
private void reportFinishVibrationLocked() {
private void reportFinishVibrationLocked(VibrationInfo.Status status) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
try {
if (mCurrentVibration != null) {
endVibrationLocked(mCurrentVibration, status);
mAppOps.finishOp(AppOpsManager.OP_VIBRATE, mCurrentVibration.uid,
mCurrentVibration.opPkg);
unlinkVibration(mCurrentVibration);
@@ -1153,7 +1339,7 @@ public class VibratorService extends IVibratorService.Stub
if (devicesUpdated || lowPowerModeUpdated) {
// If the state changes out from under us then just reset.
doCancelVibrateLocked();
doCancelVibrateLocked(VibrationInfo.Status.CANCELLED);
}
updateAlwaysOnLocked();
@@ -1224,7 +1410,7 @@ public class VibratorService extends IVibratorService.Stub
}
private void updateAlwaysOnLocked(int id, Vibration vib) {
final int intensity = getCurrentIntensityLocked(vib);
final int intensity = getCurrentIntensityLocked(vib.attrs.getUsage());
if (!shouldVibrate(vib, intensity)) {
mNativeWrapper.vibratorAlwaysOnDisable(id);
} else {
@@ -1345,6 +1531,10 @@ public class VibratorService extends IVibratorService.Stub
return;
}
}
endVibrationLocked(vib, VibrationInfo.Status.IGNORED_UNSUPPORTED);
// The set current vibration is not actually playing, so drop it.
mCurrentVibration = null;
if (!prebaked.shouldFallback()) {
return;
}
@@ -1355,11 +1545,12 @@ public class VibratorService extends IVibratorService.Stub
}
Vibration fallbackVib = new Vibration(vib.token, effect, vib.attrs, vib.uid,
vib.opPkg, vib.reason + " (fallback)");
final int intensity = getCurrentIntensityLocked(fallbackVib);
// Set current vibration before starting it, so callback will work.
mCurrentVibration = fallbackVib;
final int intensity = getCurrentIntensityLocked(fallbackVib.attrs.getUsage());
linkVibration(fallbackVib);
applyVibrationIntensityScalingLocked(fallbackVib, intensity);
startVibrationInnerLocked(fallbackVib);
return;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
@@ -1376,11 +1567,10 @@ public class VibratorService extends IVibratorService.Stub
usingInputDeviceVibrators = !mInputDeviceVibrators.isEmpty();
}
// Input devices don't support composed effect, so skip trying it with them.
if (usingInputDeviceVibrators) {
return;
}
if (!hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
if (usingInputDeviceVibrators || !hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
endVibrationLocked(vib, VibrationInfo.Status.IGNORED_UNSUPPORTED);
// The set current vibration is not actually playing, so drop it.
mCurrentVibration = null;
return;
}
@@ -1489,15 +1679,25 @@ public class VibratorService extends IVibratorService.Stub
} else {
pw.println("null");
}
pw.print(" mCurrentExternalVibration=" + mCurrentExternalVibration);
pw.print(" mCurrentExternalVibration=");
if (mCurrentExternalVibration != null) {
pw.println(mCurrentExternalVibration.toInfo().toString());
} else {
pw.println("null");
}
pw.println(" mVibratorUnderExternalControl=" + mVibratorUnderExternalControl);
pw.println(" mIsVibrating=" + mIsVibrating);
pw.println(" mVibratorStateListeners Count=" +
mVibratorStateListeners.getRegisteredCallbackCount());
pw.println(" mVibratorStateListeners Count="
+ mVibratorStateListeners.getRegisteredCallbackCount());
pw.println(" mLowPowerMode=" + mLowPowerMode);
pw.println(" mHapticFeedbackIntensity=" + mHapticFeedbackIntensity);
pw.println(" mHapticFeedbackDefaultIntensity="
+ mVibrator.getDefaultHapticFeedbackIntensity());
pw.println(" mNotificationIntensity=" + mNotificationIntensity);
pw.println(" mNotificationDefaultIntensity="
+ mVibrator.getDefaultNotificationVibrationIntensity());
pw.println(" mRingIntensity=" + mRingIntensity);
pw.println(" mRingDefaultIntensity=" + mVibrator.getDefaultRingVibrationIntensity());
pw.println(" mSupportedEffects=" + mSupportedEffects);
pw.println(" mSupportedPrimitives=" + mSupportedPrimitives);
pw.println();
@@ -1523,8 +1723,8 @@ public class VibratorService extends IVibratorService.Stub
}
pw.println(" Previous external vibrations:");
for (ExternalVibration vib : mPreviousExternalVibrations) {
pw.println(" " + vib);
for (VibrationInfo info : mPreviousExternalVibrations) {
pw.println(" " + info);
}
}
}
@@ -1535,36 +1735,45 @@ public class VibratorService extends IVibratorService.Stub
synchronized (mLock) {
if (mCurrentVibration != null) {
mCurrentVibration.toInfo().dumpProto(proto,
VibratorServiceDumpProto.CURRENT_VIBRATION);
VibratorServiceDumpProto.CURRENT_VIBRATION);
}
if (mCurrentExternalVibration != null) {
mCurrentExternalVibration.toInfo().dumpProto(proto,
VibratorServiceDumpProto.CURRENT_EXTERNAL_VIBRATION);
}
proto.write(VibratorServiceDumpProto.IS_VIBRATING, mIsVibrating);
proto.write(VibratorServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL,
mVibratorUnderExternalControl);
mVibratorUnderExternalControl);
proto.write(VibratorServiceDumpProto.LOW_POWER_MODE, mLowPowerMode);
proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_INTENSITY,
mHapticFeedbackIntensity);
proto.write(VibratorServiceDumpProto.NOTIFICATION_INTENSITY,
mNotificationIntensity);
mHapticFeedbackIntensity);
proto.write(VibratorServiceDumpProto.HAPTIC_FEEDBACK_DEFAULT_INTENSITY,
mVibrator.getDefaultHapticFeedbackIntensity());
proto.write(VibratorServiceDumpProto.NOTIFICATION_INTENSITY, mNotificationIntensity);
proto.write(VibratorServiceDumpProto.NOTIFICATION_DEFAULT_INTENSITY,
mVibrator.getDefaultNotificationVibrationIntensity());
proto.write(VibratorServiceDumpProto.RING_INTENSITY, mRingIntensity);
proto.write(VibratorServiceDumpProto.RING_DEFAULT_INTENSITY,
mVibrator.getDefaultRingVibrationIntensity());
for (VibrationInfo info : mPreviousRingVibrations) {
info.dumpProto(proto,
VibratorServiceDumpProto.PREVIOUS_RING_VIBRATIONS);
info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_RING_VIBRATIONS);
}
for (VibrationInfo info : mPreviousNotificationVibrations) {
info.dumpProto(proto,
VibratorServiceDumpProto.PREVIOUS_NOTIFICATION_VIBRATIONS);
info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_NOTIFICATION_VIBRATIONS);
}
for (VibrationInfo info : mPreviousAlarmVibrations) {
info.dumpProto(proto,
VibratorServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS);
info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS);
}
for (VibrationInfo info : mPreviousVibrations) {
info.dumpProto(proto,
VibratorServiceDumpProto.PREVIOUS_VIBRATIONS);
info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_VIBRATIONS);
}
for (VibrationInfo info : mPreviousExternalVibrations) {
info.dumpProto(proto, VibratorServiceDumpProto.PREVIOUS_EXTERNAL_VIBRATIONS);
}
}
proto.flush();
@@ -1579,8 +1788,8 @@ public class VibratorService extends IVibratorService.Stub
VibrateWaveformThread(Vibration vib) {
mWaveform = (VibrationEffect.Waveform) vib.effect;
mVibration = new Vibration(vib.token, /* effect= */ null, vib.attrs, vib.uid, vib.opPkg,
vib.reason);
mVibration = new Vibration(vib.token, /* effect= */ null, vib.attrs, vib.uid,
vib.opPkg, vib.reason);
mTmpWorkSource.set(vib.uid);
mWakeLock.setWorkSource(mTmpWorkSource);
}
@@ -1655,8 +1864,8 @@ public class VibratorService extends IVibratorService.Stub
// appropriate intervals.
onDuration = getTotalOnDuration(timings, amplitudes, index - 1,
repeat);
mVibration.effect =
VibrationEffect.createOneShot(onDuration, amplitude);
mVibration.effect = VibrationEffect.createOneShot(
onDuration, amplitude);
doVibratorOn(mVibration);
} else {
doVibratorSetAmplitude(amplitude);
@@ -1827,7 +2036,7 @@ public class VibratorService extends IVibratorService.Stub
if (mCurrentVibration != null
&& !(mCurrentVibration.isHapticFeedback()
&& mCurrentVibration.isFromSystem())) {
doCancelVibrateLocked();
doCancelVibrateLocked(VibrationInfo.Status.CANCELLED);
}
}
}
@@ -1882,63 +2091,54 @@ public class VibratorService extends IVibratorService.Stub
int mode = getAppOpMode(vib.getUid(), vib.getPackage(), vib.getVibrationAttributes());
if (mode != AppOpsManager.MODE_ALLOWED) {
ExternalVibrationHolder vibHolder = new ExternalVibrationHolder(vib);
vibHolder.scale = SCALE_MUTE;
if (mode == AppOpsManager.MODE_ERRORED) {
Slog.w(TAG, "Would be an error: external vibrate from uid " + vib.getUid());
endVibrationLocked(vibHolder, VibrationInfo.Status.ERROR_APP_OPS);
} else {
endVibrationLocked(vibHolder, VibrationInfo.Status.IGNORED_APP_OPS);
}
return SCALE_MUTE;
}
final int scaleLevel;
synchronized (mLock) {
if (!vib.equals(mCurrentExternalVibration)) {
if (mCurrentExternalVibration == null) {
// If we're not under external control right now, then cancel any normal
// vibration that may be playing and ready the vibrator for external
// control.
doCancelVibrateLocked();
setVibratorUnderExternalControl(true);
}
// At this point we either have an externally controlled vibration playing, or
// no vibration playing. Since the interface defines that only one externally
// controlled vibration can play at a time, by returning something other than
// SCALE_MUTE from this function we can be assured that if we are currently
// playing vibration, it will be muted in favor of the new vibration.
//
// Note that this doesn't support multiple concurrent external controls, as we
// would need to mute the old one still if it came from a different controller.
mCurrentExternalVibration = vib;
mCurrentExternalDeathRecipient = new ExternalVibrationDeathRecipient();
mCurrentExternalVibration.linkToDeath(mCurrentExternalDeathRecipient);
if (mPreviousExternalVibrations.size() > mPreviousVibrationsLimit) {
mPreviousExternalVibrations.removeFirst();
}
mPreviousExternalVibrations.addLast(vib);
if (DEBUG) {
Slog.e(TAG, "Playing external vibration: " + vib);
}
if (mCurrentExternalVibration != null
&& mCurrentExternalVibration.externalVibration.equals(vib)) {
// We are already playing this external vibration, so we can return the same
// scale calculated in the previous call to this method.
return mCurrentExternalVibration.scale;
}
final int usage = vib.getVibrationAttributes().getUsage();
final int defaultIntensity;
final int currentIntensity;
if (isRingtone(usage)) {
defaultIntensity = mVibrator.getDefaultRingVibrationIntensity();
currentIntensity = mRingIntensity;
} else if (isNotification(usage)) {
defaultIntensity = mVibrator.getDefaultNotificationVibrationIntensity();
currentIntensity = mNotificationIntensity;
} else if (isHapticFeedback(usage)) {
defaultIntensity = mVibrator.getDefaultHapticFeedbackIntensity();
currentIntensity = mHapticFeedbackIntensity;
} else if (isAlarm(usage)) {
defaultIntensity = Vibrator.VIBRATION_INTENSITY_HIGH;
currentIntensity = Vibrator.VIBRATION_INTENSITY_HIGH;
if (mCurrentExternalVibration == null) {
// If we're not under external control right now, then cancel any normal
// vibration that may be playing and ready the vibrator for external control.
doCancelVibrateLocked(VibrationInfo.Status.CANCELLED);
setVibratorUnderExternalControl(true);
} else {
defaultIntensity = 0;
currentIntensity = 0;
endVibrationLocked(mCurrentExternalVibration, VibrationInfo.Status.CANCELLED);
}
// At this point we either have an externally controlled vibration playing, or
// no vibration playing. Since the interface defines that only one externally
// controlled vibration can play at a time, by returning something other than
// SCALE_MUTE from this function we can be assured that if we are currently
// playing vibration, it will be muted in favor of the new vibration.
//
// Note that this doesn't support multiple concurrent external controls, as we
// would need to mute the old one still if it came from a different controller.
mCurrentExternalVibration = new ExternalVibrationHolder(vib);
mCurrentExternalDeathRecipient = new ExternalVibrationDeathRecipient();
vib.linkToDeath(mCurrentExternalDeathRecipient);
if (DEBUG) {
Slog.e(TAG, "Playing external vibration: " + vib);
}
int usage = vib.getVibrationAttributes().getUsage();
int defaultIntensity = getDefaultIntensity(usage);
int currentIntensity = getCurrentIntensityLocked(usage);
scaleLevel = currentIntensity - defaultIntensity;
}
if (scaleLevel >= SCALE_VERY_LOW && scaleLevel <= SCALE_VERY_HIGH) {
mCurrentExternalVibration.scale = scaleLevel;
return scaleLevel;
} else {
// Presumably we want to play this but something about our scaling has gone
@@ -1952,22 +2152,42 @@ public class VibratorService extends IVibratorService.Stub
@Override
public void onExternalVibrationStop(ExternalVibration vib) {
synchronized (mLock) {
if (vib.equals(mCurrentExternalVibration)) {
mCurrentExternalVibration.unlinkToDeath(mCurrentExternalDeathRecipient);
mCurrentExternalDeathRecipient = null;
mCurrentExternalVibration = null;
setVibratorUnderExternalControl(false);
if (mCurrentExternalVibration != null
&& mCurrentExternalVibration.externalVibration.equals(vib)) {
if (DEBUG) {
Slog.e(TAG, "Stopping external vibration" + vib);
}
doCancelExternalVibrateLocked(VibrationInfo.Status.FINISHED);
}
}
}
private void doCancelExternalVibrateLocked(VibrationInfo.Status status) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doCancelExternalVibrateLocked");
try {
if (mCurrentExternalVibration == null) {
return;
}
endVibrationLocked(mCurrentExternalVibration, status);
mCurrentExternalVibration.externalVibration.unlinkToDeath(
mCurrentExternalDeathRecipient);
mCurrentExternalDeathRecipient = null;
mCurrentExternalVibration = null;
setVibratorUnderExternalControl(false);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
private class ExternalVibrationDeathRecipient implements IBinder.DeathRecipient {
public void binderDied() {
synchronized (mLock) {
onExternalVibrationStop(mCurrentExternalVibration);
if (mCurrentExternalVibration != null) {
if (DEBUG) {
Slog.d(TAG, "External vibration finished because binder died");
}
doCancelExternalVibrateLocked(VibrationInfo.Status.CANCELLED);
}
}
}
}
@@ -2229,5 +2449,4 @@ public class VibratorService extends IVibratorService.Stub
}
}
}
}