1 /*
2  * Copyright (C) 2019 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.providers.media.util;
18 
19 import static com.android.providers.media.MediaProviderStatsLog.MEDIA_CONTENT_DELETED;
20 import static com.android.providers.media.MediaProviderStatsLog.MEDIA_PROVIDER_IDLE_MAINTENANCE_FINISHED;
21 import static com.android.providers.media.MediaProviderStatsLog.MEDIA_PROVIDER_PERMISSION_REQUESTED;
22 import static com.android.providers.media.MediaProviderStatsLog.MEDIA_PROVIDER_PERMISSION_REQUESTED__RESULT__USER_DENIED;
23 import static com.android.providers.media.MediaProviderStatsLog.MEDIA_PROVIDER_PERMISSION_REQUESTED__RESULT__USER_GRANTED;
24 import static com.android.providers.media.MediaProviderStatsLog.MEDIA_PROVIDER_SCAN_OCCURRED;
25 import static com.android.providers.media.MediaProviderStatsLog.MEDIA_PROVIDER_SCAN_OCCURRED__VOLUME_TYPE__EXTERNAL_OTHER;
26 import static com.android.providers.media.MediaProviderStatsLog.MEDIA_PROVIDER_SCAN_OCCURRED__VOLUME_TYPE__EXTERNAL_PRIMARY;
27 import static com.android.providers.media.MediaProviderStatsLog.MEDIA_PROVIDER_SCAN_OCCURRED__VOLUME_TYPE__INTERNAL;
28 import static com.android.providers.media.MediaProviderStatsLog.MEDIA_PROVIDER_SCAN_OCCURRED__VOLUME_TYPE__UNKNOWN;
29 import static com.android.providers.media.MediaProviderStatsLog.MEDIA_PROVIDER_SCHEMA_CHANGED;
30 import static com.android.providers.media.scan.MediaScanner.REASON_DEMAND;
31 import static com.android.providers.media.scan.MediaScanner.REASON_IDLE;
32 import static com.android.providers.media.scan.MediaScanner.REASON_MOUNTED;
33 import static com.android.providers.media.scan.MediaScanner.REASON_UNKNOWN;
34 
35 import android.provider.MediaStore;
36 
37 import androidx.annotation.NonNull;
38 
39 import com.android.providers.media.MediaProviderStatsLog;
40 
41 /**
42  * Class that emits common metrics to both remote and local endpoints to aid in
43  * regression investigations and bug triage.
44  */
45 public class Metrics {
logScan(@onNull String volumeName, int reason, long itemCount, long durationMillis, int insertCount, int updateCount, int deleteCount)46     public static void logScan(@NonNull String volumeName, int reason, long itemCount,
47             long durationMillis, int insertCount, int updateCount, int deleteCount) {
48         Logging.logPersistent(String.format(
49                 "Scanned %s due to %s, found %d items in %dms, %d inserts %d updates %d deletes",
50                 volumeName, translateReason(reason), itemCount, durationMillis, insertCount,
51                 updateCount, deleteCount));
52 
53         final float normalizedDurationMillis = ((float) durationMillis) / itemCount;
54         final float normalizedInsertCount = ((float) insertCount) / itemCount;
55         final float normalizedUpdateCount = ((float) updateCount) / itemCount;
56         final float normalizedDeleteCount = ((float) deleteCount) / itemCount;
57 
58         MediaProviderStatsLog.write(MEDIA_PROVIDER_SCAN_OCCURRED,
59                 translateVolumeName(volumeName), reason, itemCount, normalizedDurationMillis,
60                 normalizedInsertCount, normalizedUpdateCount, normalizedDeleteCount);
61     }
62 
logDeletion(@onNull String volumeName, int uid, String packageName, int itemCount)63     public static void logDeletion(@NonNull String volumeName, int uid, String packageName,
64             int itemCount) {
65         Logging.logPersistent(String.format(
66                 "Deleted %3$d items on %1$s due to %2$s",
67                 volumeName, packageName, itemCount));
68 
69         MediaProviderStatsLog.write(MEDIA_CONTENT_DELETED,
70                 translateVolumeName(volumeName), uid, itemCount);
71     }
72 
logPermissionGranted(@onNull String volumeName, int uid, String packageName, int itemCount)73     public static void logPermissionGranted(@NonNull String volumeName, int uid, String packageName,
74             int itemCount) {
75         Logging.logPersistent(String.format(
76                 "Granted permission to %3$d items on %1$s to %2$s",
77                 volumeName, packageName, itemCount));
78 
79         MediaProviderStatsLog.write(MEDIA_PROVIDER_PERMISSION_REQUESTED,
80                 translateVolumeName(volumeName), uid, itemCount,
81                 MEDIA_PROVIDER_PERMISSION_REQUESTED__RESULT__USER_GRANTED);
82     }
83 
logPermissionDenied(@onNull String volumeName, int uid, String packageName, int itemCount)84     public static void logPermissionDenied(@NonNull String volumeName, int uid, String packageName,
85             int itemCount) {
86         Logging.logPersistent(String.format(
87                 "Denied permission to %3$d items on %1$s to %2$s",
88                 volumeName, packageName, itemCount));
89 
90         MediaProviderStatsLog.write(MEDIA_PROVIDER_PERMISSION_REQUESTED,
91                 translateVolumeName(volumeName), uid, itemCount,
92                 MEDIA_PROVIDER_PERMISSION_REQUESTED__RESULT__USER_DENIED);
93     }
94 
logSchemaChange(@onNull String volumeName, int versionFrom, int versionTo, long itemCount, long durationMillis)95     public static void logSchemaChange(@NonNull String volumeName, int versionFrom, int versionTo,
96             long itemCount, long durationMillis) {
97         Logging.logPersistent(String.format(
98                 "Changed schema version on %s from %d to %d, %d items taking %dms",
99                 volumeName, versionFrom, versionTo, itemCount, durationMillis));
100 
101         final float normalizedDurationMillis = ((float) durationMillis) / itemCount;
102 
103         MediaProviderStatsLog.write(MEDIA_PROVIDER_SCHEMA_CHANGED,
104                 translateVolumeName(volumeName), versionFrom, versionTo, itemCount,
105                 normalizedDurationMillis);
106     }
107 
logIdleMaintenance(@onNull String volumeName, long itemCount, long durationMillis, int staleThumbnails, int expiredMedia)108     public static void logIdleMaintenance(@NonNull String volumeName, long itemCount,
109             long durationMillis, int staleThumbnails, int expiredMedia) {
110         Logging.logPersistent(String.format(
111                 "Idle maintenance on %s, %d items taking %dms, %d stale, %d expired",
112                 volumeName, itemCount, durationMillis, staleThumbnails, expiredMedia));
113 
114         final float normalizedDurationMillis = ((float) durationMillis) / itemCount;
115         final float normalizedStaleThumbnails = ((float) staleThumbnails) / itemCount;
116         final float normalizedExpiredMedia = ((float) expiredMedia) / itemCount;
117 
118         MediaProviderStatsLog.write(MEDIA_PROVIDER_IDLE_MAINTENANCE_FINISHED,
119                 translateVolumeName(volumeName), itemCount, normalizedDurationMillis,
120                 normalizedStaleThumbnails, normalizedExpiredMedia);
121     }
122 
translateReason(int reason)123     public static String translateReason(int reason) {
124         switch (reason) {
125             case REASON_UNKNOWN: return "REASON_UNKNOWN";
126             case REASON_MOUNTED: return "REASON_MOUNTED";
127             case REASON_DEMAND: return "REASON_DEMAND";
128             case REASON_IDLE: return "REASON_IDLE";
129             default: return String.valueOf(reason);
130         }
131     }
132 
translateVolumeName(@onNull String volumeName)133     private static int translateVolumeName(@NonNull String volumeName) {
134         switch (volumeName) {
135             case MediaStore.VOLUME_INTERNAL:
136                 return MEDIA_PROVIDER_SCAN_OCCURRED__VOLUME_TYPE__INTERNAL;
137             case MediaStore.VOLUME_EXTERNAL:
138                 // Callers using generic "external" volume name end up applying
139                 // to all external volumes, so we can't tell which volumes were
140                 // actually changed
141                 return MEDIA_PROVIDER_SCAN_OCCURRED__VOLUME_TYPE__UNKNOWN;
142             case MediaStore.VOLUME_EXTERNAL_PRIMARY:
143                 return MEDIA_PROVIDER_SCAN_OCCURRED__VOLUME_TYPE__EXTERNAL_PRIMARY;
144             default:
145                 return MEDIA_PROVIDER_SCAN_OCCURRED__VOLUME_TYPE__EXTERNAL_OTHER;
146         }
147     }
148 }
149