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