1 /* 2 * Copyright (C) 2011 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.internal.net; 18 19 import static android.net.NetworkStats.SET_ALL; 20 import static android.net.NetworkStats.TAG_NONE; 21 import static android.net.NetworkStats.UID_ALL; 22 import static com.android.server.NetworkManagementSocketTagger.kernelToTag; 23 24 import android.net.NetworkStats; 25 import android.os.StrictMode; 26 import android.os.SystemClock; 27 28 import com.android.internal.annotations.VisibleForTesting; 29 import com.android.internal.util.ProcFileReader; 30 31 import java.io.File; 32 import java.io.FileInputStream; 33 import java.io.IOException; 34 import java.net.ProtocolException; 35 36 import libcore.io.IoUtils; 37 38 /** 39 * Creates {@link NetworkStats} instances by parsing various {@code /proc/} 40 * files as needed. 41 */ 42 public class NetworkStatsFactory { 43 private static final String TAG = "NetworkStatsFactory"; 44 45 private static final boolean USE_NATIVE_PARSING = true; 46 private static final boolean SANITY_CHECK_NATIVE = false; 47 48 /** Path to {@code /proc/net/xt_qtaguid/iface_stat_all}. */ 49 private final File mStatsXtIfaceAll; 50 /** Path to {@code /proc/net/xt_qtaguid/iface_stat_fmt}. */ 51 private final File mStatsXtIfaceFmt; 52 /** Path to {@code /proc/net/xt_qtaguid/stats}. */ 53 private final File mStatsXtUid; 54 55 public NetworkStatsFactory() { 56 this(new File("/proc/")); 57 } 58 59 @VisibleForTesting 60 public NetworkStatsFactory(File procRoot) { 61 mStatsXtIfaceAll = new File(procRoot, "net/xt_qtaguid/iface_stat_all"); 62 mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt"); 63 mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats"); 64 } 65 66 /** 67 * Parse and return interface-level summary {@link NetworkStats} measured 68 * using {@code /proc/net/dev} style hooks, which may include non IP layer 69 * traffic. Values monotonically increase since device boot, and may include 70 * details about inactive interfaces. 71 * 72 * @throws IllegalStateException when problem parsing stats. 73 */ 74 public NetworkStats readNetworkStatsSummaryDev() throws IOException { 75 final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads(); 76 77 final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6); 78 final NetworkStats.Entry entry = new NetworkStats.Entry(); 79 80 ProcFileReader reader = null; 81 try { 82 reader = new ProcFileReader(new FileInputStream(mStatsXtIfaceAll)); 83 84 while (reader.hasMoreData()) { 85 entry.iface = reader.nextString(); 86 entry.uid = UID_ALL; 87 entry.set = SET_ALL; 88 entry.tag = TAG_NONE; 89 90 final boolean active = reader.nextInt() != 0; 91 92 // always include snapshot values 93 entry.rxBytes = reader.nextLong(); 94 entry.rxPackets = reader.nextLong(); 95 entry.txBytes = reader.nextLong(); 96 entry.txPackets = reader.nextLong(); 97 98 // fold in active numbers, but only when active 99 if (active) { 100 entry.rxBytes += reader.nextLong(); 101 entry.rxPackets += reader.nextLong(); 102 entry.txBytes += reader.nextLong(); 103 entry.txPackets += reader.nextLong(); 104 } 105 106 stats.addValues(entry); 107 reader.finishLine(); 108 } 109 } catch (NullPointerException e) { 110 throw new ProtocolException("problem parsing stats", e); 111 } catch (NumberFormatException e) { 112 throw new ProtocolException("problem parsing stats", e); 113 } finally { 114 IoUtils.closeQuietly(reader); 115 StrictMode.setThreadPolicy(savedPolicy); 116 } 117 return stats; 118 } 119 120 /** 121 * Parse and return interface-level summary {@link NetworkStats}. Designed 122 * to return only IP layer traffic. Values monotonically increase since 123 * device boot, and may include details about inactive interfaces. 124 * 125 * @throws IllegalStateException when problem parsing stats. 126 */ 127 public NetworkStats readNetworkStatsSummaryXt() throws IOException { 128 final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads(); 129 130 // return null when kernel doesn't support 131 if (!mStatsXtIfaceFmt.exists()) return null; 132 133 final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6); 134 final NetworkStats.Entry entry = new NetworkStats.Entry(); 135 136 ProcFileReader reader = null; 137 try { 138 // open and consume header line 139 reader = new ProcFileReader(new FileInputStream(mStatsXtIfaceFmt)); 140 reader.finishLine(); 141 142 while (reader.hasMoreData()) { 143 entry.iface = reader.nextString(); 144 entry.uid = UID_ALL; 145 entry.set = SET_ALL; 146 entry.tag = TAG_NONE; 147 148 entry.rxBytes = reader.nextLong(); 149 entry.rxPackets = reader.nextLong(); 150 entry.txBytes = reader.nextLong(); 151 entry.txPackets = reader.nextLong(); 152 153 stats.addValues(entry); 154 reader.finishLine(); 155 } 156 } catch (NullPointerException e) { 157 throw new ProtocolException("problem parsing stats", e); 158 } catch (NumberFormatException e) { 159 throw new ProtocolException("problem parsing stats", e); 160 } finally { 161 IoUtils.closeQuietly(reader); 162 StrictMode.setThreadPolicy(savedPolicy); 163 } 164 return stats; 165 } 166 167 public NetworkStats readNetworkStatsDetail() throws IOException { 168 return readNetworkStatsDetail(UID_ALL); 169 } 170 171 public NetworkStats readNetworkStatsDetail(int limitUid) throws IOException { 172 if (USE_NATIVE_PARSING) { 173 final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0); 174 if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), limitUid) != 0) { 175 throw new IOException("Failed to parse network stats"); 176 } 177 if (SANITY_CHECK_NATIVE) { 178 final NetworkStats javaStats = javaReadNetworkStatsDetail(mStatsXtUid, limitUid); 179 assertEquals(javaStats, stats); 180 } 181 return stats; 182 } else { 183 return javaReadNetworkStatsDetail(mStatsXtUid, limitUid); 184 } 185 } 186 187 /** 188 * Parse and return {@link NetworkStats} with UID-level details. Values are 189 * expected to monotonically increase since device boot. 190 */ 191 @VisibleForTesting 192 public static NetworkStats javaReadNetworkStatsDetail(File detailPath, int limitUid) 193 throws IOException { 194 final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads(); 195 196 final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24); 197 final NetworkStats.Entry entry = new NetworkStats.Entry(); 198 199 int idx = 1; 200 int lastIdx = 1; 201 202 ProcFileReader reader = null; 203 try { 204 // open and consume header line 205 reader = new ProcFileReader(new FileInputStream(detailPath)); 206 reader.finishLine(); 207 208 while (reader.hasMoreData()) { 209 idx = reader.nextInt(); 210 if (idx != lastIdx + 1) { 211 throw new ProtocolException( 212 "inconsistent idx=" + idx + " after lastIdx=" + lastIdx); 213 } 214 lastIdx = idx; 215 216 entry.iface = reader.nextString(); 217 entry.tag = kernelToTag(reader.nextString()); 218 entry.uid = reader.nextInt(); 219 entry.set = reader.nextInt(); 220 entry.rxBytes = reader.nextLong(); 221 entry.rxPackets = reader.nextLong(); 222 entry.txBytes = reader.nextLong(); 223 entry.txPackets = reader.nextLong(); 224 225 if (limitUid == UID_ALL || limitUid == entry.uid) { 226 stats.addValues(entry); 227 } 228 229 reader.finishLine(); 230 } 231 } catch (NullPointerException e) { 232 throw new ProtocolException("problem parsing idx " + idx, e); 233 } catch (NumberFormatException e) { 234 throw new ProtocolException("problem parsing idx " + idx, e); 235 } finally { 236 IoUtils.closeQuietly(reader); 237 StrictMode.setThreadPolicy(savedPolicy); 238 } 239 240 return stats; 241 } 242 243 public void assertEquals(NetworkStats expected, NetworkStats actual) { 244 if (expected.size() != actual.size()) { 245 throw new AssertionError( 246 "Expected size " + expected.size() + ", actual size " + actual.size()); 247 } 248 249 NetworkStats.Entry expectedRow = null; 250 NetworkStats.Entry actualRow = null; 251 for (int i = 0; i < expected.size(); i++) { 252 expectedRow = expected.getValues(i, expectedRow); 253 actualRow = actual.getValues(i, actualRow); 254 if (!expectedRow.equals(actualRow)) { 255 throw new AssertionError( 256 "Expected row " + i + ": " + expectedRow + ", actual row " + actualRow); 257 } 258 } 259 } 260 261 /** 262 * Parse statistics from file into given {@link NetworkStats} object. Values 263 * are expected to monotonically increase since device boot. 264 */ 265 @VisibleForTesting 266 public static native int nativeReadNetworkStatsDetail( 267 NetworkStats stats, String path, int limitUid); 268 } 269