• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
63     /**
64      * Logs persistent deletion logs on-device.
65      */
logDeletionPersistent(@onNull String volumeName, String reason, int[] countPerMediaType)66     public static void logDeletionPersistent(@NonNull String volumeName, String reason,
67             int[] countPerMediaType) {
68         final StringBuilder builder = new StringBuilder("Deleted ");
69         for (int count: countPerMediaType) {
70             builder.append(count).append(' ');
71         }
72         builder.append("items on ")
73                 .append(volumeName)
74                 .append(" due to ")
75                 .append(reason);
76 
77         Logging.logPersistent(builder.toString());
78     }
79 
80     /**
81      * Logs persistent deletion logs on-device and stats metrics. Count of items per-media-type
82      * are not uploaded to MediaProviderStats logs.
83      */
logDeletion(@onNull String volumeName, int uid, String packageName, int itemCount, int[] countPerMediaType)84     public static void logDeletion(@NonNull String volumeName, int uid, String packageName,
85             int itemCount, int[] countPerMediaType) {
86         logDeletionPersistent(volumeName, packageName, countPerMediaType);
87         MediaProviderStatsLog.write(MEDIA_CONTENT_DELETED,
88                 translateVolumeName(volumeName), uid, itemCount);
89     }
90 
logPermissionGranted(@onNull String volumeName, int uid, String packageName, int itemCount)91     public static void logPermissionGranted(@NonNull String volumeName, int uid, String packageName,
92             int itemCount) {
93         Logging.logPersistent(String.format(
94                 "Granted permission to %3$d items on %1$s to %2$s",
95                 volumeName, packageName, itemCount));
96 
97         MediaProviderStatsLog.write(MEDIA_PROVIDER_PERMISSION_REQUESTED,
98                 translateVolumeName(volumeName), uid, itemCount,
99                 MEDIA_PROVIDER_PERMISSION_REQUESTED__RESULT__USER_GRANTED);
100     }
101 
logPermissionDenied(@onNull String volumeName, int uid, String packageName, int itemCount)102     public static void logPermissionDenied(@NonNull String volumeName, int uid, String packageName,
103             int itemCount) {
104         Logging.logPersistent(String.format(
105                 "Denied permission to %3$d items on %1$s to %2$s",
106                 volumeName, packageName, itemCount));
107 
108         MediaProviderStatsLog.write(MEDIA_PROVIDER_PERMISSION_REQUESTED,
109                 translateVolumeName(volumeName), uid, itemCount,
110                 MEDIA_PROVIDER_PERMISSION_REQUESTED__RESULT__USER_DENIED);
111     }
112 
logSchemaChange(@onNull String volumeName, int versionFrom, int versionTo, long itemCount, long durationMillis, @NonNull String databaseUuid)113     public static void logSchemaChange(@NonNull String volumeName, int versionFrom, int versionTo,
114             long itemCount, long durationMillis, @NonNull String databaseUuid) {
115         Logging.logPersistent(String.format(
116                 "Changed schema version on %s from %d to %d, %d items taking %dms UUID %s",
117                 volumeName, versionFrom, versionTo, itemCount, durationMillis, databaseUuid));
118 
119         final float normalizedDurationMillis = ((float) durationMillis) / itemCount;
120 
121         MediaProviderStatsLog.write(MEDIA_PROVIDER_SCHEMA_CHANGED,
122                 translateVolumeName(volumeName), versionFrom, versionTo, itemCount,
123                 normalizedDurationMillis);
124     }
125 
logIdleMaintenance(@onNull String volumeName, long itemCount, long durationMillis, int staleThumbnails, int expiredMedia)126     public static void logIdleMaintenance(@NonNull String volumeName, long itemCount,
127             long durationMillis, int staleThumbnails, int expiredMedia) {
128         Logging.logPersistent(String.format(
129                 "Idle maintenance on %s, %d items taking %dms, %d stale, %d expired",
130                 volumeName, itemCount, durationMillis, staleThumbnails, expiredMedia));
131 
132         final float normalizedDurationMillis = ((float) durationMillis) / itemCount;
133         final float normalizedStaleThumbnails = ((float) staleThumbnails) / itemCount;
134         final float normalizedExpiredMedia = ((float) expiredMedia) / itemCount;
135 
136         MediaProviderStatsLog.write(MEDIA_PROVIDER_IDLE_MAINTENANCE_FINISHED,
137                 translateVolumeName(volumeName), itemCount, normalizedDurationMillis,
138                 normalizedStaleThumbnails, normalizedExpiredMedia);
139     }
140 
translateReason(int reason)141     public static String translateReason(int reason) {
142         switch (reason) {
143             case REASON_UNKNOWN: return "REASON_UNKNOWN";
144             case REASON_MOUNTED: return "REASON_MOUNTED";
145             case REASON_DEMAND: return "REASON_DEMAND";
146             case REASON_IDLE: return "REASON_IDLE";
147             default: return String.valueOf(reason);
148         }
149     }
150 
translateVolumeName(@onNull String volumeName)151     private static int translateVolumeName(@NonNull String volumeName) {
152         switch (volumeName) {
153             case MediaStore.VOLUME_INTERNAL:
154                 return MEDIA_PROVIDER_SCAN_OCCURRED__VOLUME_TYPE__INTERNAL;
155             case MediaStore.VOLUME_EXTERNAL:
156                 // Callers using generic "external" volume name end up applying
157                 // to all external volumes, so we can't tell which volumes were
158                 // actually changed
159                 return MEDIA_PROVIDER_SCAN_OCCURRED__VOLUME_TYPE__UNKNOWN;
160             case MediaStore.VOLUME_EXTERNAL_PRIMARY:
161                 return MEDIA_PROVIDER_SCAN_OCCURRED__VOLUME_TYPE__EXTERNAL_PRIMARY;
162             default:
163                 return MEDIA_PROVIDER_SCAN_OCCURRED__VOLUME_TYPE__EXTERNAL_OTHER;
164         }
165     }
166 }
167