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