1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.vibrator; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.media.AudioAttributes; 22 import android.os.CombinedVibration; 23 import android.os.IBinder; 24 import android.os.VibrationAttributes; 25 import android.os.VibrationEffect; 26 import android.os.vibrator.PrebakedSegment; 27 import android.os.vibrator.PrimitiveSegment; 28 import android.os.vibrator.RampSegment; 29 import android.os.vibrator.StepSegment; 30 import android.os.vibrator.VibrationEffectSegment; 31 import android.util.IndentingPrintWriter; 32 import android.util.proto.ProtoOutputStream; 33 34 import java.io.PrintWriter; 35 import java.time.Instant; 36 import java.time.ZoneId; 37 import java.time.format.DateTimeFormatter; 38 import java.util.Locale; 39 import java.util.Objects; 40 import java.util.concurrent.atomic.AtomicInteger; 41 42 /** 43 * The base class for all vibrations. 44 */ 45 abstract class Vibration { 46 private static final DateTimeFormatter DEBUG_TIME_FORMATTER = DateTimeFormatter.ofPattern( 47 "HH:mm:ss.SSS").withZone(ZoneId.systemDefault()); 48 private static final DateTimeFormatter DEBUG_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern( 49 "MM-dd HH:mm:ss.SSS").withZone(ZoneId.systemDefault()); 50 51 // Used to generate globally unique vibration ids. 52 private static final AtomicInteger sNextVibrationId = new AtomicInteger(1); // 0 = no callback 53 54 public final long id; 55 public final CallerInfo callerInfo; 56 public final VibrationStats stats = new VibrationStats(); 57 public final IBinder callerToken; 58 59 /** Vibration status with reference to values from vibratormanagerservice.proto for logging. */ 60 enum Status { 61 UNKNOWN(VibrationProto.UNKNOWN), 62 RUNNING(VibrationProto.RUNNING), 63 FINISHED(VibrationProto.FINISHED), 64 FINISHED_UNEXPECTED(VibrationProto.FINISHED_UNEXPECTED), 65 FORWARDED_TO_INPUT_DEVICES(VibrationProto.FORWARDED_TO_INPUT_DEVICES), 66 CANCELLED_BINDER_DIED(VibrationProto.CANCELLED_BINDER_DIED), 67 CANCELLED_BY_SCREEN_OFF(VibrationProto.CANCELLED_BY_SCREEN_OFF), 68 CANCELLED_BY_SETTINGS_UPDATE(VibrationProto.CANCELLED_BY_SETTINGS_UPDATE), 69 CANCELLED_BY_USER(VibrationProto.CANCELLED_BY_USER), 70 CANCELLED_BY_UNKNOWN_REASON(VibrationProto.CANCELLED_BY_UNKNOWN_REASON), 71 CANCELLED_SUPERSEDED(VibrationProto.CANCELLED_SUPERSEDED), 72 IGNORED_ERROR_APP_OPS(VibrationProto.IGNORED_ERROR_APP_OPS), 73 IGNORED_ERROR_CANCELLING(VibrationProto.IGNORED_ERROR_CANCELLING), 74 IGNORED_ERROR_SCHEDULING(VibrationProto.IGNORED_ERROR_SCHEDULING), 75 IGNORED_ERROR_TOKEN(VibrationProto.IGNORED_ERROR_TOKEN), 76 IGNORED_APP_OPS(VibrationProto.IGNORED_APP_OPS), 77 IGNORED_BACKGROUND(VibrationProto.IGNORED_BACKGROUND), 78 IGNORED_MISSING_PERMISSION(VibrationProto.IGNORED_MISSING_PERMISSION), 79 IGNORED_UNSUPPORTED(VibrationProto.IGNORED_UNSUPPORTED), 80 IGNORED_FOR_EXTERNAL(VibrationProto.IGNORED_FOR_EXTERNAL), 81 IGNORED_FOR_HIGHER_IMPORTANCE(VibrationProto.IGNORED_FOR_HIGHER_IMPORTANCE), 82 IGNORED_FOR_ONGOING(VibrationProto.IGNORED_FOR_ONGOING), 83 IGNORED_FOR_POWER(VibrationProto.IGNORED_FOR_POWER), 84 IGNORED_FOR_RINGER_MODE(VibrationProto.IGNORED_FOR_RINGER_MODE), 85 IGNORED_FOR_SETTINGS(VibrationProto.IGNORED_FOR_SETTINGS), 86 IGNORED_SUPERSEDED(VibrationProto.IGNORED_SUPERSEDED), 87 IGNORED_FROM_VIRTUAL_DEVICE(VibrationProto.IGNORED_FROM_VIRTUAL_DEVICE), 88 IGNORED_ON_WIRELESS_CHARGER(VibrationProto.IGNORED_ON_WIRELESS_CHARGER); 89 90 private final int mProtoEnumValue; 91 Status(int value)92 Status(int value) { 93 mProtoEnumValue = value; 94 } 95 getProtoEnumValue()96 public int getProtoEnumValue() { 97 return mProtoEnumValue; 98 } 99 } 100 Vibration(@onNull IBinder token, @NonNull CallerInfo callerInfo)101 Vibration(@NonNull IBinder token, @NonNull CallerInfo callerInfo) { 102 Objects.requireNonNull(token); 103 Objects.requireNonNull(callerInfo); 104 this.id = sNextVibrationId.getAndIncrement(); 105 this.callerToken = token; 106 this.callerInfo = callerInfo; 107 } 108 109 /** Return true if vibration is a repeating vibration. */ isRepeating()110 abstract boolean isRepeating(); 111 112 /** 113 * Holds lightweight immutable info on the process that triggered the vibration. This data 114 * could potentially be kept in memory for a long time for bugreport dumpsys operations. 115 * 116 * Since CallerInfo can be kept in memory for a long time, it shouldn't hold any references to 117 * potentially expensive or resource-linked objects, such as {@link IBinder}. 118 */ 119 static final class CallerInfo { 120 public final VibrationAttributes attrs; 121 public final int uid; 122 public final int deviceId; 123 public final String opPkg; 124 public final String reason; 125 CallerInfo(@onNull VibrationAttributes attrs, int uid, int deviceId, String opPkg, String reason)126 CallerInfo(@NonNull VibrationAttributes attrs, int uid, int deviceId, String opPkg, 127 String reason) { 128 Objects.requireNonNull(attrs); 129 this.attrs = attrs; 130 this.uid = uid; 131 this.deviceId = deviceId; 132 this.opPkg = opPkg; 133 this.reason = reason; 134 } 135 136 @Override equals(Object o)137 public boolean equals(Object o) { 138 if (this == o) return true; 139 if (!(o instanceof CallerInfo)) return false; 140 CallerInfo that = (CallerInfo) o; 141 return Objects.equals(attrs, that.attrs) 142 && uid == that.uid 143 && deviceId == that.deviceId 144 && Objects.equals(opPkg, that.opPkg) 145 && Objects.equals(reason, that.reason); 146 } 147 148 @Override hashCode()149 public int hashCode() { 150 return Objects.hash(attrs, uid, deviceId, opPkg, reason); 151 } 152 153 @Override toString()154 public String toString() { 155 return "CallerInfo{" 156 + " uid=" + uid 157 + ", opPkg=" + opPkg 158 + ", deviceId=" + deviceId 159 + ", attrs=" + attrs 160 + ", reason=" + reason 161 + '}'; 162 } 163 } 164 165 /** Immutable info passed as a signal to end a vibration. */ 166 static final class EndInfo { 167 /** The {@link Status} to be set to the vibration when it ends with this info. */ 168 @NonNull 169 public final Status status; 170 /** Info about the process that ended the vibration. */ 171 public final CallerInfo endedBy; 172 EndInfo(@onNull Vibration.Status status)173 EndInfo(@NonNull Vibration.Status status) { 174 this(status, null); 175 } 176 EndInfo(@onNull Vibration.Status status, @Nullable CallerInfo endedBy)177 EndInfo(@NonNull Vibration.Status status, @Nullable CallerInfo endedBy) { 178 this.status = status; 179 this.endedBy = endedBy; 180 } 181 182 @Override equals(Object o)183 public boolean equals(Object o) { 184 if (this == o) return true; 185 if (!(o instanceof EndInfo)) return false; 186 EndInfo that = (EndInfo) o; 187 return Objects.equals(endedBy, that.endedBy) 188 && status == that.status; 189 } 190 191 @Override hashCode()192 public int hashCode() { 193 return Objects.hash(status, endedBy); 194 } 195 196 @Override toString()197 public String toString() { 198 return "EndInfo{" 199 + "status=" + status 200 + ", endedBy=" + endedBy 201 + '}'; 202 } 203 } 204 205 /** 206 * Holds lightweight debug information about the vibration that could potentially be kept in 207 * memory for a long time for bugreport dumpsys operations. 208 * 209 * Since DebugInfo can be kept in memory for a long time, it shouldn't hold any references to 210 * potentially expensive or resource-linked objects, such as {@link IBinder}. 211 */ 212 static final class DebugInfo { 213 final Status mStatus; 214 final long mCreateTime; 215 final CallerInfo mCallerInfo; 216 @Nullable 217 final CombinedVibration mPlayedEffect; 218 219 private final long mStartTime; 220 private final long mEndTime; 221 private final long mDurationMs; 222 @Nullable 223 private final CombinedVibration mOriginalEffect; 224 private final int mScaleLevel; 225 private final float mAdaptiveScale; 226 DebugInfo(Status status, VibrationStats stats, @Nullable CombinedVibration playedEffect, @Nullable CombinedVibration originalEffect, int scaleLevel, float adaptiveScale, @NonNull CallerInfo callerInfo)227 DebugInfo(Status status, VibrationStats stats, @Nullable CombinedVibration playedEffect, 228 @Nullable CombinedVibration originalEffect, int scaleLevel, 229 float adaptiveScale, @NonNull CallerInfo callerInfo) { 230 Objects.requireNonNull(callerInfo); 231 mCreateTime = stats.getCreateTimeDebug(); 232 mStartTime = stats.getStartTimeDebug(); 233 mEndTime = stats.getEndTimeDebug(); 234 mDurationMs = stats.getDurationDebug(); 235 mPlayedEffect = playedEffect; 236 mOriginalEffect = originalEffect; 237 mScaleLevel = scaleLevel; 238 mAdaptiveScale = adaptiveScale; 239 mCallerInfo = callerInfo; 240 mStatus = status; 241 } 242 243 @Override toString()244 public String toString() { 245 return "createTime: " + DEBUG_DATE_TIME_FORMATTER.format( 246 Instant.ofEpochMilli(mCreateTime)) 247 + ", startTime: " + DEBUG_DATE_TIME_FORMATTER.format( 248 Instant.ofEpochMilli(mStartTime)) 249 + ", endTime: " + (mEndTime == 0 ? null : DEBUG_DATE_TIME_FORMATTER.format( 250 Instant.ofEpochMilli(mEndTime))) 251 + ", durationMs: " + mDurationMs 252 + ", status: " + mStatus.name().toLowerCase(Locale.ROOT) 253 + ", playedEffect: " + mPlayedEffect 254 + ", originalEffect: " + mOriginalEffect 255 + ", scaleLevel: " + VibrationScaler.scaleLevelToString(mScaleLevel) 256 + ", adaptiveScale: " + String.format(Locale.ROOT, "%.2f", mAdaptiveScale) 257 + ", callerInfo: " + mCallerInfo; 258 } 259 logMetrics(VibratorFrameworkStatsLogger statsLogger)260 void logMetrics(VibratorFrameworkStatsLogger statsLogger) { 261 statsLogger.logVibrationAdaptiveHapticScale(mCallerInfo.uid, mAdaptiveScale); 262 } 263 264 /** 265 * Write this info in a compact way into given {@link PrintWriter}. 266 * 267 * <p>This is used by dumpsys to log multiple vibration records in single lines that are 268 * easy to skim through by the sorted created time. 269 */ dumpCompact(IndentingPrintWriter pw)270 void dumpCompact(IndentingPrintWriter pw) { 271 boolean isExternalVibration = mPlayedEffect == null; 272 String timingsStr = String.format(Locale.ROOT, 273 "%s | %8s | %20s | duration: %5dms | start: %12s | end: %12s", 274 DEBUG_DATE_TIME_FORMATTER.format(Instant.ofEpochMilli(mCreateTime)), 275 isExternalVibration ? "external" : "effect", 276 mStatus.name().toLowerCase(Locale.ROOT), 277 mDurationMs, 278 mStartTime == 0 ? "" 279 : DEBUG_TIME_FORMATTER.format(Instant.ofEpochMilli(mStartTime)), 280 mEndTime == 0 ? "" 281 : DEBUG_TIME_FORMATTER.format(Instant.ofEpochMilli(mEndTime))); 282 String paramStr = String.format(Locale.ROOT, 283 " | scale: %8s (adaptive=%.2f) | flags: %4s | usage: %s", 284 VibrationScaler.scaleLevelToString(mScaleLevel), mAdaptiveScale, 285 Long.toBinaryString(mCallerInfo.attrs.getFlags()), 286 mCallerInfo.attrs.usageToString()); 287 // Optional, most vibrations have category unknown so skip them to simplify the logs 288 String categoryStr = 289 mCallerInfo.attrs.getCategory() != VibrationAttributes.CATEGORY_UNKNOWN 290 ? " | category=" + VibrationAttributes.categoryToString( 291 mCallerInfo.attrs.getCategory()) 292 : ""; 293 // Optional, most vibrations should not be defined via AudioAttributes 294 // so skip them to simplify the logs 295 String audioUsageStr = 296 mCallerInfo.attrs.getOriginalAudioUsage() != AudioAttributes.USAGE_UNKNOWN 297 ? " | audioUsage=" + AudioAttributes.usageToString( 298 mCallerInfo.attrs.getOriginalAudioUsage()) 299 : ""; 300 String callerStr = String.format(Locale.ROOT, 301 " | %s (uid=%d, deviceId=%d) | reason: %s", 302 mCallerInfo.opPkg, mCallerInfo.uid, mCallerInfo.deviceId, mCallerInfo.reason); 303 String effectStr = String.format(Locale.ROOT, 304 " | played: %s | original: %s", 305 mPlayedEffect == null ? null : mPlayedEffect.toDebugString(), 306 mOriginalEffect == null ? null : mOriginalEffect.toDebugString()); 307 pw.println(timingsStr + paramStr + categoryStr + audioUsageStr + callerStr + effectStr); 308 } 309 310 /** Write this info into given {@link PrintWriter}. */ dump(IndentingPrintWriter pw)311 void dump(IndentingPrintWriter pw) { 312 pw.println("Vibration:"); 313 pw.increaseIndent(); 314 pw.println("status = " + mStatus.name().toLowerCase(Locale.ROOT)); 315 pw.println("durationMs = " + mDurationMs); 316 pw.println("createTime = " + DEBUG_DATE_TIME_FORMATTER.format( 317 Instant.ofEpochMilli(mCreateTime))); 318 pw.println("startTime = " + DEBUG_DATE_TIME_FORMATTER.format( 319 Instant.ofEpochMilli(mStartTime))); 320 pw.println("endTime = " + (mEndTime == 0 ? null 321 : DEBUG_DATE_TIME_FORMATTER.format(Instant.ofEpochMilli(mEndTime)))); 322 pw.println("playedEffect = " + mPlayedEffect); 323 pw.println("originalEffect = " + mOriginalEffect); 324 pw.println("scale = " + VibrationScaler.scaleLevelToString(mScaleLevel)); 325 pw.println("adaptiveScale = " + String.format(Locale.ROOT, "%.2f", mAdaptiveScale)); 326 pw.println("callerInfo = " + mCallerInfo); 327 pw.decreaseIndent(); 328 } 329 330 /** Write this info into given {@code fieldId} on {@link ProtoOutputStream}. */ dump(ProtoOutputStream proto, long fieldId)331 void dump(ProtoOutputStream proto, long fieldId) { 332 final long token = proto.start(fieldId); 333 proto.write(VibrationProto.START_TIME, mStartTime); 334 proto.write(VibrationProto.END_TIME, mEndTime); 335 proto.write(VibrationProto.DURATION_MS, mDurationMs); 336 proto.write(VibrationProto.STATUS, mStatus.ordinal()); 337 338 final long attrsToken = proto.start(VibrationProto.ATTRIBUTES); 339 final VibrationAttributes attrs = mCallerInfo.attrs; 340 proto.write(VibrationAttributesProto.USAGE, attrs.getUsage()); 341 proto.write(VibrationAttributesProto.AUDIO_USAGE, attrs.getAudioUsage()); 342 proto.write(VibrationAttributesProto.CATEGORY, attrs.getCategory()); 343 proto.write(VibrationAttributesProto.FLAGS, attrs.getFlags()); 344 proto.end(attrsToken); 345 346 if (mPlayedEffect != null) { 347 dumpEffect(proto, VibrationProto.PLAYED_EFFECT, mPlayedEffect); 348 } 349 if (mOriginalEffect != null) { 350 dumpEffect(proto, VibrationProto.ORIGINAL_EFFECT, mOriginalEffect); 351 } 352 353 proto.end(token); 354 } 355 dumpEffect( ProtoOutputStream proto, long fieldId, CombinedVibration effect)356 private void dumpEffect( 357 ProtoOutputStream proto, long fieldId, CombinedVibration effect) { 358 dumpEffect(proto, fieldId, 359 (CombinedVibration.Sequential) CombinedVibration.startSequential() 360 .addNext(effect) 361 .combine()); 362 } 363 dumpEffect( ProtoOutputStream proto, long fieldId, CombinedVibration.Sequential effect)364 private void dumpEffect( 365 ProtoOutputStream proto, long fieldId, CombinedVibration.Sequential effect) { 366 final long token = proto.start(fieldId); 367 for (int i = 0; i < effect.getEffects().size(); i++) { 368 CombinedVibration nestedEffect = effect.getEffects().get(i); 369 if (nestedEffect instanceof CombinedVibration.Mono) { 370 dumpEffect(proto, CombinedVibrationEffectProto.EFFECTS, 371 (CombinedVibration.Mono) nestedEffect); 372 } else if (nestedEffect instanceof CombinedVibration.Stereo) { 373 dumpEffect(proto, CombinedVibrationEffectProto.EFFECTS, 374 (CombinedVibration.Stereo) nestedEffect); 375 } 376 proto.write(CombinedVibrationEffectProto.DELAYS, effect.getDelays().get(i)); 377 } 378 proto.end(token); 379 } 380 dumpEffect( ProtoOutputStream proto, long fieldId, CombinedVibration.Mono effect)381 private void dumpEffect( 382 ProtoOutputStream proto, long fieldId, CombinedVibration.Mono effect) { 383 final long token = proto.start(fieldId); 384 dumpEffect(proto, SyncVibrationEffectProto.EFFECTS, effect.getEffect()); 385 proto.end(token); 386 } 387 dumpEffect( ProtoOutputStream proto, long fieldId, CombinedVibration.Stereo effect)388 private void dumpEffect( 389 ProtoOutputStream proto, long fieldId, CombinedVibration.Stereo effect) { 390 final long token = proto.start(fieldId); 391 for (int i = 0; i < effect.getEffects().size(); i++) { 392 proto.write(SyncVibrationEffectProto.VIBRATOR_IDS, effect.getEffects().keyAt(i)); 393 dumpEffect(proto, SyncVibrationEffectProto.EFFECTS, effect.getEffects().valueAt(i)); 394 } 395 proto.end(token); 396 } 397 dumpEffect( ProtoOutputStream proto, long fieldId, VibrationEffect effect)398 private void dumpEffect( 399 ProtoOutputStream proto, long fieldId, VibrationEffect effect) { 400 final long token = proto.start(fieldId); 401 VibrationEffect.Composed composed = (VibrationEffect.Composed) effect; 402 for (VibrationEffectSegment segment : composed.getSegments()) { 403 dumpEffect(proto, VibrationEffectProto.SEGMENTS, segment); 404 } 405 proto.write(VibrationEffectProto.REPEAT, composed.getRepeatIndex()); 406 proto.end(token); 407 } 408 dumpEffect(ProtoOutputStream proto, long fieldId, VibrationEffectSegment segment)409 private void dumpEffect(ProtoOutputStream proto, long fieldId, 410 VibrationEffectSegment segment) { 411 final long token = proto.start(fieldId); 412 if (segment instanceof StepSegment) { 413 dumpEffect(proto, SegmentProto.STEP, (StepSegment) segment); 414 } else if (segment instanceof RampSegment) { 415 dumpEffect(proto, SegmentProto.RAMP, (RampSegment) segment); 416 } else if (segment instanceof PrebakedSegment) { 417 dumpEffect(proto, SegmentProto.PREBAKED, (PrebakedSegment) segment); 418 } else if (segment instanceof PrimitiveSegment) { 419 dumpEffect(proto, SegmentProto.PRIMITIVE, (PrimitiveSegment) segment); 420 } 421 proto.end(token); 422 } 423 dumpEffect(ProtoOutputStream proto, long fieldId, StepSegment segment)424 private void dumpEffect(ProtoOutputStream proto, long fieldId, StepSegment segment) { 425 final long token = proto.start(fieldId); 426 proto.write(StepSegmentProto.DURATION, segment.getDuration()); 427 proto.write(StepSegmentProto.AMPLITUDE, segment.getAmplitude()); 428 proto.write(StepSegmentProto.FREQUENCY, segment.getFrequencyHz()); 429 proto.end(token); 430 } 431 dumpEffect(ProtoOutputStream proto, long fieldId, RampSegment segment)432 private void dumpEffect(ProtoOutputStream proto, long fieldId, RampSegment segment) { 433 final long token = proto.start(fieldId); 434 proto.write(RampSegmentProto.DURATION, segment.getDuration()); 435 proto.write(RampSegmentProto.START_AMPLITUDE, segment.getStartAmplitude()); 436 proto.write(RampSegmentProto.END_AMPLITUDE, segment.getEndAmplitude()); 437 proto.write(RampSegmentProto.START_FREQUENCY, segment.getStartFrequencyHz()); 438 proto.write(RampSegmentProto.END_FREQUENCY, segment.getEndFrequencyHz()); 439 proto.end(token); 440 } 441 dumpEffect(ProtoOutputStream proto, long fieldId, PrebakedSegment segment)442 private void dumpEffect(ProtoOutputStream proto, long fieldId, 443 PrebakedSegment segment) { 444 final long token = proto.start(fieldId); 445 proto.write(PrebakedSegmentProto.EFFECT_ID, segment.getEffectId()); 446 proto.write(PrebakedSegmentProto.EFFECT_STRENGTH, segment.getEffectStrength()); 447 proto.write(PrebakedSegmentProto.FALLBACK, segment.shouldFallback()); 448 proto.end(token); 449 } 450 dumpEffect(ProtoOutputStream proto, long fieldId, PrimitiveSegment segment)451 private void dumpEffect(ProtoOutputStream proto, long fieldId, 452 PrimitiveSegment segment) { 453 final long token = proto.start(fieldId); 454 proto.write(PrimitiveSegmentProto.PRIMITIVE_ID, segment.getPrimitiveId()); 455 proto.write(PrimitiveSegmentProto.SCALE, segment.getScale()); 456 proto.write(PrimitiveSegmentProto.DELAY, segment.getDelay()); 457 proto.end(token); 458 } 459 } 460 } 461