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