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.os.CombinedVibration; 22 import android.os.IBinder; 23 import android.os.VibrationAttributes; 24 import android.os.VibrationEffect; 25 import android.os.vibrator.PrebakedSegment; 26 import android.os.vibrator.PrimitiveSegment; 27 import android.os.vibrator.RampSegment; 28 import android.os.vibrator.StepSegment; 29 import android.os.vibrator.VibrationEffectSegment; 30 import android.util.proto.ProtoOutputStream; 31 32 import java.text.SimpleDateFormat; 33 import java.util.Date; 34 import java.util.Objects; 35 import java.util.concurrent.atomic.AtomicInteger; 36 37 /** 38 * The base class for all vibrations. 39 */ 40 abstract class Vibration { 41 private static final SimpleDateFormat DEBUG_DATE_FORMAT = 42 new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); 43 // Used to generate globally unique vibration ids. 44 private static final AtomicInteger sNextVibrationId = new AtomicInteger(1); // 0 = no callback 45 46 public final long id; 47 public final CallerInfo callerInfo; 48 public final VibrationStats stats = new VibrationStats(); 49 public final IBinder callerToken; 50 51 /** Vibration status with reference to values from vibratormanagerservice.proto for logging. */ 52 enum Status { 53 UNKNOWN(VibrationProto.UNKNOWN), 54 RUNNING(VibrationProto.RUNNING), 55 FINISHED(VibrationProto.FINISHED), 56 FINISHED_UNEXPECTED(VibrationProto.FINISHED_UNEXPECTED), 57 FORWARDED_TO_INPUT_DEVICES(VibrationProto.FORWARDED_TO_INPUT_DEVICES), 58 CANCELLED_BINDER_DIED(VibrationProto.CANCELLED_BINDER_DIED), 59 CANCELLED_BY_SCREEN_OFF(VibrationProto.CANCELLED_BY_SCREEN_OFF), 60 CANCELLED_BY_SETTINGS_UPDATE(VibrationProto.CANCELLED_BY_SETTINGS_UPDATE), 61 CANCELLED_BY_USER(VibrationProto.CANCELLED_BY_USER), 62 CANCELLED_BY_UNKNOWN_REASON(VibrationProto.CANCELLED_BY_UNKNOWN_REASON), 63 CANCELLED_SUPERSEDED(VibrationProto.CANCELLED_SUPERSEDED), 64 IGNORED_ERROR_APP_OPS(VibrationProto.IGNORED_ERROR_APP_OPS), 65 IGNORED_ERROR_CANCELLING(VibrationProto.IGNORED_ERROR_CANCELLING), 66 IGNORED_ERROR_SCHEDULING(VibrationProto.IGNORED_ERROR_SCHEDULING), 67 IGNORED_ERROR_TOKEN(VibrationProto.IGNORED_ERROR_TOKEN), 68 IGNORED_APP_OPS(VibrationProto.IGNORED_APP_OPS), 69 IGNORED_BACKGROUND(VibrationProto.IGNORED_BACKGROUND), 70 IGNORED_UNKNOWN_VIBRATION(VibrationProto.IGNORED_UNKNOWN_VIBRATION), 71 IGNORED_UNSUPPORTED(VibrationProto.IGNORED_UNSUPPORTED), 72 IGNORED_FOR_EXTERNAL(VibrationProto.IGNORED_FOR_EXTERNAL), 73 IGNORED_FOR_HIGHER_IMPORTANCE(VibrationProto.IGNORED_FOR_HIGHER_IMPORTANCE), 74 IGNORED_FOR_ONGOING(VibrationProto.IGNORED_FOR_ONGOING), 75 IGNORED_FOR_POWER(VibrationProto.IGNORED_FOR_POWER), 76 IGNORED_FOR_RINGER_MODE(VibrationProto.IGNORED_FOR_RINGER_MODE), 77 IGNORED_FOR_SETTINGS(VibrationProto.IGNORED_FOR_SETTINGS), 78 IGNORED_SUPERSEDED(VibrationProto.IGNORED_SUPERSEDED), 79 IGNORED_FROM_VIRTUAL_DEVICE(VibrationProto.IGNORED_FROM_VIRTUAL_DEVICE); 80 81 private final int mProtoEnumValue; 82 Status(int value)83 Status(int value) { 84 mProtoEnumValue = value; 85 } 86 getProtoEnumValue()87 public int getProtoEnumValue() { 88 return mProtoEnumValue; 89 } 90 } 91 Vibration(@onNull IBinder token, @NonNull CallerInfo callerInfo)92 Vibration(@NonNull IBinder token, @NonNull CallerInfo callerInfo) { 93 Objects.requireNonNull(token); 94 Objects.requireNonNull(callerInfo); 95 this.id = sNextVibrationId.getAndIncrement(); 96 this.callerToken = token; 97 this.callerInfo = callerInfo; 98 } 99 100 /** Return true if vibration is a repeating vibration. */ isRepeating()101 abstract boolean isRepeating(); 102 103 /** 104 * Holds lightweight immutable info on the process that triggered the vibration. This data 105 * could potentially be kept in memory for a long time for bugreport dumpsys operations. 106 * 107 * Since CallerInfo can be kept in memory for a long time, it shouldn't hold any references to 108 * potentially expensive or resource-linked objects, such as {@link IBinder}. 109 */ 110 static final class CallerInfo { 111 public final VibrationAttributes attrs; 112 public final int uid; 113 public final int displayId; 114 public final String opPkg; 115 public final String reason; 116 CallerInfo(@onNull VibrationAttributes attrs, int uid, int displayId, String opPkg, String reason)117 CallerInfo(@NonNull VibrationAttributes attrs, int uid, int displayId, 118 String opPkg, String reason) { 119 Objects.requireNonNull(attrs); 120 this.attrs = attrs; 121 this.uid = uid; 122 this.displayId = displayId; 123 this.opPkg = opPkg; 124 this.reason = reason; 125 } 126 127 @Override equals(Object o)128 public boolean equals(Object o) { 129 if (this == o) return true; 130 if (!(o instanceof CallerInfo)) return false; 131 CallerInfo that = (CallerInfo) o; 132 return Objects.equals(attrs, that.attrs) 133 && uid == that.uid 134 && displayId == that.displayId 135 && Objects.equals(opPkg, that.opPkg) 136 && Objects.equals(reason, that.reason); 137 } 138 139 @Override hashCode()140 public int hashCode() { 141 return Objects.hash(attrs, uid, displayId, opPkg, reason); 142 } 143 144 @Override toString()145 public String toString() { 146 return "CallerInfo{" 147 + " attrs=" + attrs 148 + ", uid=" + uid 149 + ", displayId=" + displayId 150 + ", opPkg=" + opPkg 151 + ", reason=" + reason 152 + '}'; 153 } 154 } 155 156 /** Immutable info passed as a signal to end a vibration. */ 157 static final class EndInfo { 158 /** The {@link Status} to be set to the vibration when it ends with this info. */ 159 @NonNull 160 public final Status status; 161 /** Info about the process that ended the vibration. */ 162 public final CallerInfo endedBy; 163 EndInfo(@onNull Vibration.Status status)164 EndInfo(@NonNull Vibration.Status status) { 165 this(status, null); 166 } 167 EndInfo(@onNull Vibration.Status status, @Nullable CallerInfo endedBy)168 EndInfo(@NonNull Vibration.Status status, @Nullable CallerInfo endedBy) { 169 this.status = status; 170 this.endedBy = endedBy; 171 } 172 173 @Override equals(Object o)174 public boolean equals(Object o) { 175 if (this == o) return true; 176 if (!(o instanceof EndInfo)) return false; 177 EndInfo that = (EndInfo) o; 178 return Objects.equals(endedBy, that.endedBy) 179 && status == that.status; 180 } 181 182 @Override hashCode()183 public int hashCode() { 184 return Objects.hash(status, endedBy); 185 } 186 187 @Override toString()188 public String toString() { 189 return "EndInfo{" 190 + "status=" + status 191 + ", endedBy=" + endedBy 192 + '}'; 193 } 194 } 195 196 /** 197 * Holds lightweight debug information about the vibration that could potentially be kept in 198 * memory for a long time for bugreport dumpsys operations. 199 * 200 * Since DebugInfo can be kept in memory for a long time, it shouldn't hold any references to 201 * potentially expensive or resource-linked objects, such as {@link IBinder}. 202 */ 203 static final class DebugInfo { 204 private final long mCreateTime; 205 private final long mStartTime; 206 private final long mEndTime; 207 private final long mDurationMs; 208 private final CombinedVibration mEffect; 209 private final CombinedVibration mOriginalEffect; 210 private final float mScale; 211 private final CallerInfo mCallerInfo; 212 private final Status mStatus; 213 DebugInfo(Status status, VibrationStats stats, @Nullable CombinedVibration effect, @Nullable CombinedVibration originalEffect, float scale, @NonNull CallerInfo callerInfo)214 DebugInfo(Status status, VibrationStats stats, @Nullable CombinedVibration effect, 215 @Nullable CombinedVibration originalEffect, float scale, 216 @NonNull CallerInfo callerInfo) { 217 Objects.requireNonNull(callerInfo); 218 mCreateTime = stats.getCreateTimeDebug(); 219 mStartTime = stats.getStartTimeDebug(); 220 mEndTime = stats.getEndTimeDebug(); 221 mDurationMs = stats.getDurationDebug(); 222 mEffect = effect; 223 mOriginalEffect = originalEffect; 224 mScale = scale; 225 mCallerInfo = callerInfo; 226 mStatus = status; 227 } 228 229 @Override toString()230 public String toString() { 231 return new StringBuilder() 232 .append("createTime: ") 233 .append(DEBUG_DATE_FORMAT.format(new Date(mCreateTime))) 234 .append(", startTime: ") 235 .append(DEBUG_DATE_FORMAT.format(new Date(mStartTime))) 236 .append(", endTime: ") 237 .append(mEndTime == 0 ? null 238 : DEBUG_DATE_FORMAT.format(new Date(mEndTime))) 239 .append(", durationMs: ") 240 .append(mDurationMs) 241 .append(", status: ") 242 .append(mStatus.name().toLowerCase()) 243 .append(", effect: ") 244 .append(mEffect) 245 .append(", originalEffect: ") 246 .append(mOriginalEffect) 247 .append(", scale: ") 248 .append(String.format("%.2f", mScale)) 249 .append(", callerInfo: ") 250 .append(mCallerInfo) 251 .toString(); 252 } 253 254 /** Write this info into given {@code fieldId} on {@link ProtoOutputStream}. */ dumpProto(ProtoOutputStream proto, long fieldId)255 public void dumpProto(ProtoOutputStream proto, long fieldId) { 256 final long token = proto.start(fieldId); 257 proto.write(VibrationProto.START_TIME, mStartTime); 258 proto.write(VibrationProto.END_TIME, mEndTime); 259 proto.write(VibrationProto.DURATION_MS, mDurationMs); 260 proto.write(VibrationProto.STATUS, mStatus.ordinal()); 261 262 final long attrsToken = proto.start(VibrationProto.ATTRIBUTES); 263 final VibrationAttributes attrs = mCallerInfo.attrs; 264 proto.write(VibrationAttributesProto.USAGE, attrs.getUsage()); 265 proto.write(VibrationAttributesProto.AUDIO_USAGE, attrs.getAudioUsage()); 266 proto.write(VibrationAttributesProto.FLAGS, attrs.getFlags()); 267 proto.end(attrsToken); 268 269 if (mEffect != null) { 270 dumpEffect(proto, VibrationProto.EFFECT, mEffect); 271 } 272 if (mOriginalEffect != null) { 273 dumpEffect(proto, VibrationProto.ORIGINAL_EFFECT, mOriginalEffect); 274 } 275 276 proto.end(token); 277 } 278 dumpEffect( ProtoOutputStream proto, long fieldId, CombinedVibration effect)279 private void dumpEffect( 280 ProtoOutputStream proto, long fieldId, CombinedVibration effect) { 281 dumpEffect(proto, fieldId, 282 (CombinedVibration.Sequential) CombinedVibration.startSequential() 283 .addNext(effect) 284 .combine()); 285 } 286 dumpEffect( ProtoOutputStream proto, long fieldId, CombinedVibration.Sequential effect)287 private void dumpEffect( 288 ProtoOutputStream proto, long fieldId, CombinedVibration.Sequential effect) { 289 final long token = proto.start(fieldId); 290 for (int i = 0; i < effect.getEffects().size(); i++) { 291 CombinedVibration nestedEffect = effect.getEffects().get(i); 292 if (nestedEffect instanceof CombinedVibration.Mono) { 293 dumpEffect(proto, CombinedVibrationEffectProto.EFFECTS, 294 (CombinedVibration.Mono) nestedEffect); 295 } else if (nestedEffect instanceof CombinedVibration.Stereo) { 296 dumpEffect(proto, CombinedVibrationEffectProto.EFFECTS, 297 (CombinedVibration.Stereo) nestedEffect); 298 } 299 proto.write(CombinedVibrationEffectProto.DELAYS, effect.getDelays().get(i)); 300 } 301 proto.end(token); 302 } 303 dumpEffect( ProtoOutputStream proto, long fieldId, CombinedVibration.Mono effect)304 private void dumpEffect( 305 ProtoOutputStream proto, long fieldId, CombinedVibration.Mono effect) { 306 final long token = proto.start(fieldId); 307 dumpEffect(proto, SyncVibrationEffectProto.EFFECTS, effect.getEffect()); 308 proto.end(token); 309 } 310 dumpEffect( ProtoOutputStream proto, long fieldId, CombinedVibration.Stereo effect)311 private void dumpEffect( 312 ProtoOutputStream proto, long fieldId, CombinedVibration.Stereo effect) { 313 final long token = proto.start(fieldId); 314 for (int i = 0; i < effect.getEffects().size(); i++) { 315 proto.write(SyncVibrationEffectProto.VIBRATOR_IDS, effect.getEffects().keyAt(i)); 316 dumpEffect(proto, SyncVibrationEffectProto.EFFECTS, effect.getEffects().valueAt(i)); 317 } 318 proto.end(token); 319 } 320 dumpEffect( ProtoOutputStream proto, long fieldId, VibrationEffect effect)321 private void dumpEffect( 322 ProtoOutputStream proto, long fieldId, VibrationEffect effect) { 323 final long token = proto.start(fieldId); 324 VibrationEffect.Composed composed = (VibrationEffect.Composed) effect; 325 for (VibrationEffectSegment segment : composed.getSegments()) { 326 dumpEffect(proto, VibrationEffectProto.SEGMENTS, segment); 327 } 328 proto.write(VibrationEffectProto.REPEAT, composed.getRepeatIndex()); 329 proto.end(token); 330 } 331 dumpEffect(ProtoOutputStream proto, long fieldId, VibrationEffectSegment segment)332 private void dumpEffect(ProtoOutputStream proto, long fieldId, 333 VibrationEffectSegment segment) { 334 final long token = proto.start(fieldId); 335 if (segment instanceof StepSegment) { 336 dumpEffect(proto, SegmentProto.STEP, (StepSegment) segment); 337 } else if (segment instanceof RampSegment) { 338 dumpEffect(proto, SegmentProto.RAMP, (RampSegment) segment); 339 } else if (segment instanceof PrebakedSegment) { 340 dumpEffect(proto, SegmentProto.PREBAKED, (PrebakedSegment) segment); 341 } else if (segment instanceof PrimitiveSegment) { 342 dumpEffect(proto, SegmentProto.PRIMITIVE, (PrimitiveSegment) segment); 343 } 344 proto.end(token); 345 } 346 dumpEffect(ProtoOutputStream proto, long fieldId, StepSegment segment)347 private void dumpEffect(ProtoOutputStream proto, long fieldId, StepSegment segment) { 348 final long token = proto.start(fieldId); 349 proto.write(StepSegmentProto.DURATION, segment.getDuration()); 350 proto.write(StepSegmentProto.AMPLITUDE, segment.getAmplitude()); 351 proto.write(StepSegmentProto.FREQUENCY, segment.getFrequencyHz()); 352 proto.end(token); 353 } 354 dumpEffect(ProtoOutputStream proto, long fieldId, RampSegment segment)355 private void dumpEffect(ProtoOutputStream proto, long fieldId, RampSegment segment) { 356 final long token = proto.start(fieldId); 357 proto.write(RampSegmentProto.DURATION, segment.getDuration()); 358 proto.write(RampSegmentProto.START_AMPLITUDE, segment.getStartAmplitude()); 359 proto.write(RampSegmentProto.END_AMPLITUDE, segment.getEndAmplitude()); 360 proto.write(RampSegmentProto.START_FREQUENCY, segment.getStartFrequencyHz()); 361 proto.write(RampSegmentProto.END_FREQUENCY, segment.getEndFrequencyHz()); 362 proto.end(token); 363 } 364 dumpEffect(ProtoOutputStream proto, long fieldId, PrebakedSegment segment)365 private void dumpEffect(ProtoOutputStream proto, long fieldId, 366 PrebakedSegment segment) { 367 final long token = proto.start(fieldId); 368 proto.write(PrebakedSegmentProto.EFFECT_ID, segment.getEffectId()); 369 proto.write(PrebakedSegmentProto.EFFECT_STRENGTH, segment.getEffectStrength()); 370 proto.write(PrebakedSegmentProto.FALLBACK, segment.shouldFallback()); 371 proto.end(token); 372 } 373 dumpEffect(ProtoOutputStream proto, long fieldId, PrimitiveSegment segment)374 private void dumpEffect(ProtoOutputStream proto, long fieldId, 375 PrimitiveSegment segment) { 376 final long token = proto.start(fieldId); 377 proto.write(PrimitiveSegmentProto.PRIMITIVE_ID, segment.getPrimitiveId()); 378 proto.write(PrimitiveSegmentProto.SCALE, segment.getScale()); 379 proto.write(PrimitiveSegmentProto.DELAY, segment.getDelay()); 380 proto.end(token); 381 } 382 } 383 } 384