• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.os.SystemClock;
20 import android.os.vibrator.PrebakedSegment;
21 import android.os.vibrator.PrimitiveSegment;
22 import android.os.vibrator.RampSegment;
23 import android.util.Slog;
24 import android.util.SparseBooleanArray;
25 
26 import com.android.internal.annotations.VisibleForTesting;
27 import com.android.internal.util.FrameworkStatsLog;
28 
29 /** Holds basic stats about the vibration playback and interaction with the vibrator HAL. */
30 final class VibrationStats {
31     static final String TAG = "VibrationStats";
32 
33     // Milestone timestamps, using SystemClock.uptimeMillis(), for calculations.
34     // - Create: time a vibration object was created, which is closer to when the service receives a
35     //           vibrate request.
36     // - Start: time a vibration started to play, which is closer to the time that the
37     //          VibrationEffect started playing the very first segment.
38     // - End: time a vibration ended, even if it never started to play. This can be as soon as the
39     //        vibrator HAL reports it has finished the last command, or before it has even started
40     //        when the vibration is ignored or cancelled.
41     // Create and end times set by VibratorManagerService only, guarded by its lock.
42     // Start times set by VibrationThread only (single-threaded).
43     private long mCreateUptimeMillis;
44     private long mStartUptimeMillis;
45     private long mEndUptimeMillis;
46 
47     // Milestone timestamps, using unix epoch time, only to be used for debugging purposes and
48     // to correlate with other system events. Any duration calculations should be done with the
49     // {create/start/end}UptimeMillis counterparts so as not to be affected by discontinuities
50     // created by RTC adjustments.
51     // Set together with the *UptimeMillis counterparts.
52     private long mCreateTimeDebug;
53     private long mStartTimeDebug;
54     private long mEndTimeDebug;
55 
56     // Vibration interruption tracking.
57     // Set by VibratorManagerService only, guarded by its lock.
58     private int mEndedByUid;
59     private int mEndedByUsage;
60     private int mInterruptedUsage;
61 
62     // All following counters are set by VibrationThread only (single-threaded):
63     // Counts how many times the VibrationEffect was repeated.
64     private int mRepeatCount;
65     // Total duration, in milliseconds, the vibrator was active with non-zero amplitude.
66     private int mVibratorOnTotalDurationMillis;
67     // Total number of primitives used in compositions.
68     private int mVibrationCompositionTotalSize;
69     private int mVibrationPwleTotalSize;
70     // Counts how many times each IVibrator method was triggered by this vibration.
71     private int mVibratorOnCount;
72     private int mVibratorOffCount;
73     private int mVibratorSetAmplitudeCount;
74     private int mVibratorSetExternalControlCount;
75     private int mVibratorPerformCount;
76     private int mVibratorComposeCount;
77     private int mVibratorComposePwleCount;
78 
79     // Ids of vibration effects and primitives used by this vibration, with support flag.
80     // Set by VibrationThread only (single-threaded).
81     private SparseBooleanArray mVibratorEffectsUsed = new SparseBooleanArray();
82     private SparseBooleanArray mVibratorPrimitivesUsed = new SparseBooleanArray();
83 
VibrationStats()84     VibrationStats() {
85         mCreateUptimeMillis = SystemClock.uptimeMillis();
86         mCreateTimeDebug = System.currentTimeMillis();
87         // Set invalid UID and VibrationAttributes.USAGE values to indicate fields are unset.
88         mEndedByUid = -1;
89         mEndedByUsage = -1;
90         mInterruptedUsage = -1;
91     }
92 
getCreateUptimeMillis()93     long getCreateUptimeMillis() {
94         return mCreateUptimeMillis;
95     }
96 
getStartUptimeMillis()97     long getStartUptimeMillis() {
98         return mStartUptimeMillis;
99     }
100 
getEndUptimeMillis()101     long getEndUptimeMillis() {
102         return mEndUptimeMillis;
103     }
104 
getCreateTimeDebug()105     long getCreateTimeDebug() {
106         return mCreateTimeDebug;
107     }
108 
getStartTimeDebug()109     long getStartTimeDebug() {
110         return mStartTimeDebug;
111     }
112 
getEndTimeDebug()113     long getEndTimeDebug() {
114         return mEndTimeDebug;
115     }
116 
117     /**
118      * Duration calculated for debugging purposes, between the creation of a vibration and the
119      * end time being reported, or -1 if the vibration has not ended.
120      */
getDurationDebug()121     long getDurationDebug() {
122         return hasEnded() ? (mEndUptimeMillis - mCreateUptimeMillis) : -1;
123     }
124 
125     /** Return true if vibration reported it has ended. */
hasEnded()126     boolean hasEnded() {
127         return mEndUptimeMillis > 0;
128     }
129 
130     /** Return true if vibration reported it has started triggering the vibrator. */
hasStarted()131     boolean hasStarted() {
132         return mStartUptimeMillis > 0;
133     }
134 
135     /**
136      * Set the current system time as this vibration start time, for debugging purposes.
137      *
138      * <p>This indicates the vibration has started to interact with the vibrator HAL and the
139      * device may start vibrating after this point.
140      *
141      * <p>This method will only accept given value if the start timestamp was never set.
142      */
reportStarted()143     void reportStarted() {
144         if (hasEnded() || (mStartUptimeMillis != 0)) {
145             // Vibration already started or ended, keep first time set and ignore this one.
146             return;
147         }
148         mStartUptimeMillis = SystemClock.uptimeMillis();
149         mStartTimeDebug = System.currentTimeMillis();
150     }
151 
152     /**
153      * Set status and end cause for this vibration to end, and the current system time as this
154      * vibration end time, for debugging purposes.
155      *
156      * <p>This might be triggered before {@link #reportStarted()}, which indicates this
157      * vibration was cancelled or ignored before it started triggering the vibrator.
158      *
159      * @return true if the status was accepted. This method will only accept given values if
160      * the end timestamp was never set.
161      */
reportEnded(int endedByUid, int endedByUsage)162     boolean reportEnded(int endedByUid, int endedByUsage) {
163         if (hasEnded()) {
164             // Vibration already ended, keep first ending stats set and ignore this one.
165             return false;
166         }
167         mEndedByUid = endedByUid;
168         mEndedByUsage = endedByUsage;
169         mEndUptimeMillis = SystemClock.uptimeMillis();
170         mEndTimeDebug = System.currentTimeMillis();
171         return true;
172     }
173 
174     /**
175      * Report this vibration has interrupted another vibration.
176      *
177      * <p>This method will only accept the first value as the one that was interrupted by this
178      * vibration, and will ignore all successive calls.
179      */
reportInterruptedAnotherVibration(int interruptedUsage)180     void reportInterruptedAnotherVibration(int interruptedUsage) {
181         if (mInterruptedUsage < 0) {
182             mInterruptedUsage = interruptedUsage;
183         }
184     }
185 
186     /** Report the vibration has looped a few more times. */
reportRepetition(int loops)187     void reportRepetition(int loops) {
188         mRepeatCount += loops;
189     }
190 
191     /** Report a call to vibrator method to turn on for given duration. */
reportVibratorOn(long halResult)192     void reportVibratorOn(long halResult) {
193         mVibratorOnCount++;
194 
195         if (halResult > 0) {
196             // If HAL result is positive then it represents the actual duration it will be ON.
197             mVibratorOnTotalDurationMillis += (int) halResult;
198         }
199     }
200 
201     /** Report a call to vibrator method to turn off. */
reportVibratorOff()202     void reportVibratorOff() {
203         mVibratorOffCount++;
204     }
205 
206     /** Report a call to vibrator method to change the vibration amplitude. */
reportSetAmplitude()207     void reportSetAmplitude() {
208         mVibratorSetAmplitudeCount++;
209     }
210 
211     /** Report a call to vibrator method to trigger a vibration effect. */
reportPerformEffect(long halResult, PrebakedSegment prebaked)212     void reportPerformEffect(long halResult, PrebakedSegment prebaked) {
213         mVibratorPerformCount++;
214 
215         if (halResult > 0) {
216             // If HAL result is positive then it represents the actual duration of the vibration.
217             mVibratorEffectsUsed.put(prebaked.getEffectId(), true);
218             mVibratorOnTotalDurationMillis += (int) halResult;
219         } else {
220             // Effect unsupported or request failed.
221             mVibratorEffectsUsed.put(prebaked.getEffectId(), false);
222         }
223     }
224 
225     /** Report a call to vibrator method to trigger a vibration as a composition of primitives. */
reportComposePrimitives(long halResult, PrimitiveSegment[] primitives)226     void reportComposePrimitives(long halResult, PrimitiveSegment[] primitives) {
227         mVibratorComposeCount++;
228         mVibrationCompositionTotalSize += primitives.length;
229 
230         if (halResult > 0) {
231             // If HAL result is positive then it represents the actual duration of the vibration.
232             // Remove the requested delays to update the total time the vibrator was ON.
233             for (PrimitiveSegment primitive : primitives) {
234                 halResult -= primitive.getDelay();
235                 mVibratorPrimitivesUsed.put(primitive.getPrimitiveId(), true);
236             }
237             if (halResult > 0) {
238                 mVibratorOnTotalDurationMillis += (int) halResult;
239             }
240         } else {
241             // One or more primitives were unsupported, or request failed.
242             for (PrimitiveSegment primitive : primitives) {
243                 mVibratorPrimitivesUsed.put(primitive.getPrimitiveId(), false);
244             }
245         }
246     }
247 
248     /** Report a call to vibrator method to trigger a vibration as a PWLE. */
reportComposePwle(long halResult, RampSegment[] segments)249     void reportComposePwle(long halResult, RampSegment[] segments) {
250         mVibratorComposePwleCount++;
251         mVibrationPwleTotalSize += segments.length;
252 
253         if (halResult > 0) {
254             // If HAL result is positive then it represents the actual duration of the vibration.
255             // Remove the zero-amplitude segments to update the total time the vibrator was ON.
256             for (RampSegment ramp : segments) {
257                 if ((ramp.getStartAmplitude() == 0) && (ramp.getEndAmplitude() == 0)) {
258                     halResult -= ramp.getDuration();
259                 }
260             }
261             if (halResult > 0) {
262                 mVibratorOnTotalDurationMillis += (int) halResult;
263             }
264         }
265     }
266 
267     /**
268      * Increment the stats for total number of times the {@code setExternalControl} method was
269      * triggered in the vibrator HAL.
270      */
reportSetExternalControl()271     void reportSetExternalControl() {
272         mVibratorSetExternalControlCount++;
273     }
274 
275     /**
276      * Immutable metrics about this vibration, to be kept in memory until it can be pushed through
277      * {@link com.android.internal.util.FrameworkStatsLog} as a
278      * {@link com.android.internal.util.FrameworkStatsLog#VIBRATION_REPORTED}.
279      */
280     static final class StatsInfo {
281         public final int uid;
282         public final int vibrationType;
283         public final int usage;
284         public final int status;
285         public final boolean endedBySameUid;
286         public final int endedByUsage;
287         public final int interruptedUsage;
288         public final int repeatCount;
289         public final int totalDurationMillis;
290         public final int vibratorOnMillis;
291         public final int startLatencyMillis;
292         public final int endLatencyMillis;
293         public final int halComposeCount;
294         public final int halComposePwleCount;
295         public final int halOnCount;
296         public final int halOffCount;
297         public final int halPerformCount;
298         public final int halSetAmplitudeCount;
299         public final int halSetExternalControlCount;
300         public final int halCompositionSize;
301         public final int halPwleSize;
302         public final int[] halSupportedCompositionPrimitivesUsed;
303         public final int[] halSupportedEffectsUsed;
304         public final int[] halUnsupportedCompositionPrimitivesUsed;
305         public final int[] halUnsupportedEffectsUsed;
306         private boolean mIsWritten;
307 
StatsInfo(int uid, int vibrationType, int usage, Vibration.Status status, VibrationStats stats, long completionUptimeMillis)308         StatsInfo(int uid, int vibrationType, int usage, Vibration.Status status,
309                 VibrationStats stats, long completionUptimeMillis) {
310             this.uid = uid;
311             this.vibrationType = vibrationType;
312             this.usage = usage;
313             this.status = status.getProtoEnumValue();
314             endedBySameUid = (uid == stats.mEndedByUid);
315             endedByUsage = stats.mEndedByUsage;
316             interruptedUsage = stats.mInterruptedUsage;
317             repeatCount = stats.mRepeatCount;
318 
319             // This duration goes from the time this object was created until the time it was
320             // completed. We can use latencies to detect the times between first and last
321             // interaction with vibrator.
322             totalDurationMillis =
323                     (int) Math.max(0,  completionUptimeMillis - stats.mCreateUptimeMillis);
324             vibratorOnMillis = stats.mVibratorOnTotalDurationMillis;
325 
326             if (stats.hasStarted()) {
327                 // We only measure latencies for vibrations that actually triggered the vibrator.
328                 startLatencyMillis =
329                         (int) Math.max(0, stats.mStartUptimeMillis - stats.mCreateUptimeMillis);
330                 endLatencyMillis =
331                         (int) Math.max(0, completionUptimeMillis - stats.mEndUptimeMillis);
332             } else {
333                 startLatencyMillis = endLatencyMillis = 0;
334             }
335 
336             halComposeCount = stats.mVibratorComposeCount;
337             halComposePwleCount = stats.mVibratorComposePwleCount;
338             halOnCount = stats.mVibratorOnCount;
339             halOffCount = stats.mVibratorOffCount;
340             halPerformCount = stats.mVibratorPerformCount;
341             halSetAmplitudeCount = stats.mVibratorSetAmplitudeCount;
342             halSetExternalControlCount = stats.mVibratorSetExternalControlCount;
343             halCompositionSize = stats.mVibrationCompositionTotalSize;
344             halPwleSize = stats.mVibrationPwleTotalSize;
345             halSupportedCompositionPrimitivesUsed =
346                     filteredKeys(stats.mVibratorPrimitivesUsed, /* supported= */ true);
347             halSupportedEffectsUsed =
348                     filteredKeys(stats.mVibratorEffectsUsed, /* supported= */ true);
349             halUnsupportedCompositionPrimitivesUsed =
350                     filteredKeys(stats.mVibratorPrimitivesUsed, /* supported= */ false);
351             halUnsupportedEffectsUsed =
352                     filteredKeys(stats.mVibratorEffectsUsed, /* supported= */ false);
353         }
354 
355         @VisibleForTesting
isWritten()356         boolean isWritten() {
357             return mIsWritten;
358         }
359 
writeVibrationReported()360         void writeVibrationReported() {
361             if (mIsWritten) {
362                 Slog.wtf(TAG, "Writing same vibration stats multiple times for uid=" + uid);
363             }
364             mIsWritten = true;
365             // Mapping from this MetricInfo representation and the atom proto VibrationReported.
366             FrameworkStatsLog.write_non_chained(
367                     FrameworkStatsLog.VIBRATION_REPORTED,
368                     uid, null, vibrationType, usage, status, endedBySameUid, endedByUsage,
369                     interruptedUsage, repeatCount, totalDurationMillis, vibratorOnMillis,
370                     startLatencyMillis, endLatencyMillis, halComposeCount, halComposePwleCount,
371                     halOnCount, halOffCount, halPerformCount, halSetAmplitudeCount,
372                     halSetExternalControlCount, halSupportedCompositionPrimitivesUsed,
373                     halSupportedEffectsUsed, halUnsupportedCompositionPrimitivesUsed,
374                     halUnsupportedEffectsUsed, halCompositionSize, halPwleSize);
375         }
376 
filteredKeys(SparseBooleanArray supportArray, boolean supported)377         private static int[] filteredKeys(SparseBooleanArray supportArray, boolean supported) {
378             int count = 0;
379             for (int i = 0; i < supportArray.size(); i++) {
380                 if (supportArray.valueAt(i) == supported) count++;
381             }
382             if (count == 0) {
383                 return null;
384             }
385             int pos = 0;
386             int[] res = new int[count];
387             for (int i = 0; i < supportArray.size(); i++) {
388                 if (supportArray.valueAt(i) == supported) {
389                     res[pos++] = supportArray.keyAt(i);
390                 }
391             }
392             return res;
393         }
394     }
395 }
396