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