1 /* 2 * Copyright (C) 2023 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.metrics; 18 19 import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_UID; 20 import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_UID_TAG; 21 import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_XT; 22 23 import static com.android.server.ConnectivityStatsLog.NETWORK_STATS_RECORDER_FILE_OPERATED; 24 import static com.android.server.ConnectivityStatsLog.NETWORK_STATS_RECORDER_FILE_OPERATED__FAST_DATA_INPUT_STATE__FDIS_DISABLED; 25 import static com.android.server.ConnectivityStatsLog.NETWORK_STATS_RECORDER_FILE_OPERATED__FAST_DATA_INPUT_STATE__FDIS_ENABLED; 26 import static com.android.server.ConnectivityStatsLog.NETWORK_STATS_RECORDER_FILE_OPERATED__OPERATION_TYPE__ROT_READ; 27 import static com.android.server.ConnectivityStatsLog.NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_UID; 28 import static com.android.server.ConnectivityStatsLog.NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_UIDTAG; 29 import static com.android.server.ConnectivityStatsLog.NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_UNKNOWN; 30 import static com.android.server.ConnectivityStatsLog.NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_XT; 31 32 import android.annotation.NonNull; 33 import android.annotation.Nullable; 34 import android.net.NetworkStatsCollection; 35 import android.net.NetworkStatsHistory; 36 import android.util.Pair; 37 38 import com.android.internal.annotations.VisibleForTesting; 39 import com.android.server.ConnectivityStatsLog; 40 41 import java.io.File; 42 import java.util.HashSet; 43 import java.util.Map; 44 import java.util.Set; 45 import java.util.regex.Pattern; 46 47 /** 48 * Helper class to log NetworkStats related metrics. 49 * 50 * This class does not provide thread-safe. 51 */ 52 public class NetworkStatsMetricsLogger { 53 final Dependencies mDeps; 54 int mReadIndex = 1; 55 56 /** Dependency class */ 57 @VisibleForTesting 58 public static class Dependencies { 59 /** 60 * Writes a NETWORK_STATS_RECORDER_FILE_OPERATION_REPORTED event to ConnectivityStatsLog. 61 */ writeRecorderFileReadingStats(int recorderType, int readIndex, int readLatencyMillis, int fileCount, int totalFileSize, int keys, int uids, int totalHistorySize, boolean useFastDataInput)62 public void writeRecorderFileReadingStats(int recorderType, int readIndex, 63 int readLatencyMillis, 64 int fileCount, int totalFileSize, 65 int keys, int uids, int totalHistorySize, 66 boolean useFastDataInput) { 67 ConnectivityStatsLog.write(NETWORK_STATS_RECORDER_FILE_OPERATED, 68 NETWORK_STATS_RECORDER_FILE_OPERATED__OPERATION_TYPE__ROT_READ, 69 recorderType, 70 readIndex, 71 readLatencyMillis, 72 fileCount, 73 totalFileSize, 74 keys, 75 uids, 76 totalHistorySize, 77 useFastDataInput 78 ? NETWORK_STATS_RECORDER_FILE_OPERATED__FAST_DATA_INPUT_STATE__FDIS_ENABLED 79 : NETWORK_STATS_RECORDER_FILE_OPERATED__FAST_DATA_INPUT_STATE__FDIS_DISABLED); 80 } 81 } 82 NetworkStatsMetricsLogger()83 public NetworkStatsMetricsLogger() { 84 mDeps = new Dependencies(); 85 } 86 87 @VisibleForTesting NetworkStatsMetricsLogger(Dependencies deps)88 public NetworkStatsMetricsLogger(Dependencies deps) { 89 mDeps = deps; 90 } 91 prefixToRecorderType(@onNull String prefix)92 private static int prefixToRecorderType(@NonNull String prefix) { 93 switch (prefix) { 94 case PREFIX_XT: 95 return NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_XT; 96 case PREFIX_UID: 97 return NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_UID; 98 case PREFIX_UID_TAG: 99 return NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_UIDTAG; 100 default: 101 return NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_UNKNOWN; 102 } 103 } 104 105 /** 106 * Get file count and total byte count for the given directory and prefix. 107 * 108 * @return File count and total byte count as a pair, or 0s if met errors. 109 */ getStatsFilesAttributes( @ullable File statsDir, @NonNull String prefix)110 private static Pair<Integer, Integer> getStatsFilesAttributes( 111 @Nullable File statsDir, @NonNull String prefix) { 112 if (statsDir == null || !statsDir.isDirectory()) return new Pair<>(0, 0); 113 114 // Only counts the matching files. 115 // The files are named in the following format: 116 // <prefix>.<startTimestamp>-[<endTimestamp>] 117 // e.g. uid_tag.12345- 118 // See FileRotator#FileInfo for more detail. 119 final Pattern pattern = Pattern.compile("^" + prefix + "\\.[0-9]+-[0-9]*$"); 120 121 int totalFiles = 0; 122 int totalBytes = 0; 123 for (String name : emptyIfNull(statsDir.list())) { 124 if (!pattern.matcher(name).matches()) continue; 125 126 totalFiles++; 127 // Cast to int is safe since stats persistent files are several MBs in total. 128 totalBytes += (int) (new File(statsDir, name).length()); 129 130 } 131 return new Pair<>(totalFiles, totalBytes); 132 } 133 emptyIfNull(@ullable String [] array)134 private static String [] emptyIfNull(@Nullable String [] array) { 135 return (array == null) ? new String[0] : array; 136 } 137 138 /** 139 * Log statistics from the NetworkStatsRecorder file reading process into statsd. 140 */ logRecorderFileReading(@onNull String prefix, int readLatencyMillis, @Nullable File statsDir, @NonNull NetworkStatsCollection collection, boolean useFastDataInput)141 public void logRecorderFileReading(@NonNull String prefix, int readLatencyMillis, 142 @Nullable File statsDir, @NonNull NetworkStatsCollection collection, 143 boolean useFastDataInput) { 144 final Set<Integer> uids = new HashSet<>(); 145 final Map<NetworkStatsCollection.Key, NetworkStatsHistory> entries = 146 collection.getEntries(); 147 148 for (final NetworkStatsCollection.Key key : entries.keySet()) { 149 uids.add(key.uid); 150 } 151 152 int totalHistorySize = 0; 153 for (final NetworkStatsHistory history : entries.values()) { 154 totalHistorySize += history.size(); 155 } 156 157 final Pair<Integer, Integer> fileAttributes = getStatsFilesAttributes(statsDir, prefix); 158 mDeps.writeRecorderFileReadingStats(prefixToRecorderType(prefix), 159 mReadIndex++, 160 readLatencyMillis, 161 fileAttributes.first /* fileCount */, 162 fileAttributes.second /* totalFileSize */, 163 entries.size(), 164 uids.size(), 165 totalHistorySize, 166 useFastDataInput); 167 } 168 } 169