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